Fossil SCM
Merge changes from trunk
Commit
9ff7d9e5e3b75658f6d7d19a00b0f27da3a94dd3d1d4d9ab68300253733149cd
Parent
7b81aac17a10ff4…
42 files changed
+1
+1
-3
+3
+13
-3
+1
-1
+1
-1
+87
-66
+22
-1
+3
-3
+13
-1
+2
-1
+4
+8
-1
-12
-12
+38
-11
+2
-2
+103
-91
-1038
+159
-98
+3
-3
+47
-3
+21
-6
+50
-4
+1
-1
-921
+14
-8
+14
-8
+12
-2
+12
-10
+1
-1
+4
-10
-12
+38
-33
+20
-13
+41
-1
+7
-4
+32
-2
+39
-27
+17
+14
~
.dockerignore
~
.fossil-settings/clean-glob
~
.fossil-settings/ignore-glob
~
auto.def
~
src/backoffice.c
~
src/blob.c
~
src/chat.c
~
src/chat.js
~
src/file.c
~
src/forum.c
~
src/fossil.fetch.js
~
src/info.c
~
src/json_detail.h
~
src/main.c
~
src/main.mk
~
src/main.mk
~
src/makemake.tcl
~
src/printf.c
~
src/shell.c
~
src/smtp.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/th.c
~
src/th.h
~
src/th_main.c
~
src/user.c
-
src/webmail.c
~
src/wiki.c
~
src/wiki.c
~
test/tester.tcl
~
test/th1.test
~
test/unversioned.test
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
win/buildmsvc.bat
~
www/build.wiki
~
www/event.wiki
~
www/gsoc-ideas.md
~
www/server/windows/service.md
~
www/server/windows/stunnel.md
+1
| --- .dockerignore | ||
| +++ .dockerignore | ||
| @@ -6,10 +6,11 @@ | ||
| 6 | 6 | bld |
| 7 | 7 | compat |
| 8 | 8 | debian |
| 9 | 9 | fossil |
| 10 | 10 | fossil.exe |
| 11 | +msvcbld | |
| 11 | 12 | setup |
| 12 | 13 | src |
| 13 | 14 | test |
| 14 | 15 | tools |
| 15 | 16 | win |
| 16 | 17 |
| --- .dockerignore | |
| +++ .dockerignore | |
| @@ -6,10 +6,11 @@ | |
| 6 | bld |
| 7 | compat |
| 8 | debian |
| 9 | fossil |
| 10 | fossil.exe |
| 11 | setup |
| 12 | src |
| 13 | test |
| 14 | tools |
| 15 | win |
| 16 |
| --- .dockerignore | |
| +++ .dockerignore | |
| @@ -6,10 +6,11 @@ | |
| 6 | bld |
| 7 | compat |
| 8 | debian |
| 9 | fossil |
| 10 | fossil.exe |
| 11 | msvcbld |
| 12 | setup |
| 13 | src |
| 14 | test |
| 15 | tools |
| 16 | win |
| 17 |
| --- .fossil-settings/clean-glob | ||
| +++ .fossil-settings/clean-glob | ||
| @@ -8,10 +8,11 @@ | ||
| 8 | 8 | Makefile |
| 9 | 9 | autosetup/jimsh0 |
| 10 | 10 | autosetup/jimsh0.exe |
| 11 | 11 | bld/* |
| 12 | 12 | msvcbld/* |
| 13 | +wbld/* | |
| 13 | 14 | win/*.c |
| 14 | 15 | win/*.h |
| 15 | 16 | win/*.exe |
| 16 | 17 | win/headers |
| 17 | 18 | win/linkopts |
| 18 | 19 |
| --- .fossil-settings/clean-glob | |
| +++ .fossil-settings/clean-glob | |
| @@ -8,10 +8,11 @@ | |
| 8 | Makefile |
| 9 | autosetup/jimsh0 |
| 10 | autosetup/jimsh0.exe |
| 11 | bld/* |
| 12 | msvcbld/* |
| 13 | win/*.c |
| 14 | win/*.h |
| 15 | win/*.exe |
| 16 | win/headers |
| 17 | win/linkopts |
| 18 |
| --- .fossil-settings/clean-glob | |
| +++ .fossil-settings/clean-glob | |
| @@ -8,10 +8,11 @@ | |
| 8 | Makefile |
| 9 | autosetup/jimsh0 |
| 10 | autosetup/jimsh0.exe |
| 11 | bld/* |
| 12 | msvcbld/* |
| 13 | wbld/* |
| 14 | win/*.c |
| 15 | win/*.h |
| 16 | win/*.exe |
| 17 | win/headers |
| 18 | win/linkopts |
| 19 |
| --- .fossil-settings/ignore-glob | ||
| +++ .fossil-settings/ignore-glob | ||
| @@ -1,10 +1,7 @@ | ||
| 1 | 1 | compat/openssl* |
| 2 | 2 | compat/tcl* |
| 3 | -compat/zlib* | |
| 4 | 3 | fossil |
| 5 | 4 | fossil.exe |
| 6 | 5 | win/fossil.exe |
| 7 | 6 | *shell-see.* |
| 8 | 7 | *sqlite3-see.* |
| 9 | -bld/* | |
| 10 | -msvcbld/* | |
| 11 | 8 |
| --- .fossil-settings/ignore-glob | |
| +++ .fossil-settings/ignore-glob | |
| @@ -1,10 +1,7 @@ | |
| 1 | compat/openssl* |
| 2 | compat/tcl* |
| 3 | compat/zlib* |
| 4 | fossil |
| 5 | fossil.exe |
| 6 | win/fossil.exe |
| 7 | *shell-see.* |
| 8 | *sqlite3-see.* |
| 9 | bld/* |
| 10 | msvcbld/* |
| 11 |
| --- .fossil-settings/ignore-glob | |
| +++ .fossil-settings/ignore-glob | |
| @@ -1,10 +1,7 @@ | |
| 1 | compat/openssl* |
| 2 | compat/tcl* |
| 3 | fossil |
| 4 | fossil.exe |
| 5 | win/fossil.exe |
| 6 | *shell-see.* |
| 7 | *sqlite3-see.* |
| 8 |
M
auto.def
+3
| --- auto.def | ||
| +++ auto.def | ||
| @@ -235,10 +235,13 @@ | ||
| 235 | 235 | } |
| 236 | 236 | |
| 237 | 237 | if {[opt-bool no-opt]} { |
| 238 | 238 | define CFLAGS {-g -O0 -Wall} |
| 239 | 239 | msg-result "Builting without compiler optimization" |
| 240 | + if {[opt-bool fossil-debug]} { | |
| 241 | + define-append CFLAGS -DFOSSIL_DEBUG | |
| 242 | + } | |
| 240 | 243 | } |
| 241 | 244 | |
| 242 | 245 | if {[opt-bool with-mman]} { |
| 243 | 246 | define-append EXTRA_CFLAGS -DUSE_MMAN_H |
| 244 | 247 | define USE_MMAN_H 1 |
| 245 | 248 |
| --- auto.def | |
| +++ auto.def | |
| @@ -235,10 +235,13 @@ | |
| 235 | } |
| 236 | |
| 237 | if {[opt-bool no-opt]} { |
| 238 | define CFLAGS {-g -O0 -Wall} |
| 239 | msg-result "Builting without compiler optimization" |
| 240 | } |
| 241 | |
| 242 | if {[opt-bool with-mman]} { |
| 243 | define-append EXTRA_CFLAGS -DUSE_MMAN_H |
| 244 | define USE_MMAN_H 1 |
| 245 |
| --- auto.def | |
| +++ auto.def | |
| @@ -235,10 +235,13 @@ | |
| 235 | } |
| 236 | |
| 237 | if {[opt-bool no-opt]} { |
| 238 | define CFLAGS {-g -O0 -Wall} |
| 239 | msg-result "Builting without compiler optimization" |
| 240 | if {[opt-bool fossil-debug]} { |
| 241 | define-append CFLAGS -DFOSSIL_DEBUG |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | if {[opt-bool with-mman]} { |
| 246 | define-append EXTRA_CFLAGS -DUSE_MMAN_H |
| 247 | define USE_MMAN_H 1 |
| 248 |
+13
-3
| --- src/backoffice.c | ||
| +++ src/backoffice.c | ||
| @@ -364,20 +364,32 @@ | ||
| 364 | 364 | } |
| 365 | 365 | |
| 366 | 366 | /* |
| 367 | 367 | ** COMMAND: test-backoffice-lease |
| 368 | 368 | ** |
| 369 | -** Usage: %fossil test-backoffice-lease | |
| 369 | +** Usage: %fossil test-backoffice-lease ?--reset? | |
| 370 | 370 | ** |
| 371 | 371 | ** Print out information about the backoffice "lease" entry in the |
| 372 | 372 | ** config table that controls whether or not backoffice should run. |
| 373 | +** | |
| 374 | +** If the --reset option is given, the backoffice lease is reset. | |
| 375 | +** The use of the --reset option can be disruptive. It can cause two | |
| 376 | +** or more backoffice processes to be run simultaneously. Use it with | |
| 377 | +** caution. | |
| 373 | 378 | */ |
| 374 | 379 | void test_backoffice_lease(void){ |
| 375 | 380 | sqlite3_int64 tmNow = time(0); |
| 376 | 381 | Lease x; |
| 377 | 382 | const char *zLease; |
| 378 | 383 | db_find_and_open_repository(0,0); |
| 384 | + if( find_option("reset",0,0)!=0 ){ | |
| 385 | + db_unprotect(PROTECT_CONFIG); | |
| 386 | + db_multi_exec( | |
| 387 | + "DELETE FROM repository.config WHERE name='backoffice'" | |
| 388 | + ); | |
| 389 | + db_protect_pop(); | |
| 390 | + } | |
| 379 | 391 | verify_all_options(); |
| 380 | 392 | zLease = db_get("backoffice",""); |
| 381 | 393 | fossil_print("now: %lld\n", tmNow); |
| 382 | 394 | fossil_print("lease: \"%s\"\n", zLease); |
| 383 | 395 | backofficeReadLease(&x); |
| @@ -630,12 +642,10 @@ | ||
| 630 | 642 | } |
| 631 | 643 | |
| 632 | 644 | /* Here is where the actual work of the backoffice happens */ |
| 633 | 645 | nThis = alert_backoffice(0); |
| 634 | 646 | if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; } |
| 635 | - nThis = smtp_cleanup(); | |
| 636 | - if( nThis ){ backoffice_log("%d SMTPs", nThis); nTotal += nThis; } | |
| 637 | 647 | nThis = hook_backoffice(); |
| 638 | 648 | if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; } |
| 639 | 649 | |
| 640 | 650 | /* Close the log */ |
| 641 | 651 | if( backofficeFILE ){ |
| 642 | 652 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -364,20 +364,32 @@ | |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | ** COMMAND: test-backoffice-lease |
| 368 | ** |
| 369 | ** Usage: %fossil test-backoffice-lease |
| 370 | ** |
| 371 | ** Print out information about the backoffice "lease" entry in the |
| 372 | ** config table that controls whether or not backoffice should run. |
| 373 | */ |
| 374 | void test_backoffice_lease(void){ |
| 375 | sqlite3_int64 tmNow = time(0); |
| 376 | Lease x; |
| 377 | const char *zLease; |
| 378 | db_find_and_open_repository(0,0); |
| 379 | verify_all_options(); |
| 380 | zLease = db_get("backoffice",""); |
| 381 | fossil_print("now: %lld\n", tmNow); |
| 382 | fossil_print("lease: \"%s\"\n", zLease); |
| 383 | backofficeReadLease(&x); |
| @@ -630,12 +642,10 @@ | |
| 630 | } |
| 631 | |
| 632 | /* Here is where the actual work of the backoffice happens */ |
| 633 | nThis = alert_backoffice(0); |
| 634 | if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; } |
| 635 | nThis = smtp_cleanup(); |
| 636 | if( nThis ){ backoffice_log("%d SMTPs", nThis); nTotal += nThis; } |
| 637 | nThis = hook_backoffice(); |
| 638 | if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; } |
| 639 | |
| 640 | /* Close the log */ |
| 641 | if( backofficeFILE ){ |
| 642 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -364,20 +364,32 @@ | |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | ** COMMAND: test-backoffice-lease |
| 368 | ** |
| 369 | ** Usage: %fossil test-backoffice-lease ?--reset? |
| 370 | ** |
| 371 | ** Print out information about the backoffice "lease" entry in the |
| 372 | ** config table that controls whether or not backoffice should run. |
| 373 | ** |
| 374 | ** If the --reset option is given, the backoffice lease is reset. |
| 375 | ** The use of the --reset option can be disruptive. It can cause two |
| 376 | ** or more backoffice processes to be run simultaneously. Use it with |
| 377 | ** caution. |
| 378 | */ |
| 379 | void test_backoffice_lease(void){ |
| 380 | sqlite3_int64 tmNow = time(0); |
| 381 | Lease x; |
| 382 | const char *zLease; |
| 383 | db_find_and_open_repository(0,0); |
| 384 | if( find_option("reset",0,0)!=0 ){ |
| 385 | db_unprotect(PROTECT_CONFIG); |
| 386 | db_multi_exec( |
| 387 | "DELETE FROM repository.config WHERE name='backoffice'" |
| 388 | ); |
| 389 | db_protect_pop(); |
| 390 | } |
| 391 | verify_all_options(); |
| 392 | zLease = db_get("backoffice",""); |
| 393 | fossil_print("now: %lld\n", tmNow); |
| 394 | fossil_print("lease: \"%s\"\n", zLease); |
| 395 | backofficeReadLease(&x); |
| @@ -630,12 +642,10 @@ | |
| 642 | } |
| 643 | |
| 644 | /* Here is where the actual work of the backoffice happens */ |
| 645 | nThis = alert_backoffice(0); |
| 646 | if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; } |
| 647 | nThis = hook_backoffice(); |
| 648 | if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; } |
| 649 | |
| 650 | /* Close the log */ |
| 651 | if( backofficeFILE ){ |
| 652 |
+1
-1
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -998,11 +998,11 @@ | ||
| 998 | 998 | #endif |
| 999 | 999 | }else{ |
| 1000 | 1000 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 1001 | 1001 | out = fossil_fopen(zFilename, "wb"); |
| 1002 | 1002 | if( out==0 ){ |
| 1003 | -#if _WIN32 | |
| 1003 | +#if defined(_WIN32) | |
| 1004 | 1004 | const char *zReserved = file_is_win_reserved(zFilename); |
| 1005 | 1005 | if( zReserved ){ |
| 1006 | 1006 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 1007 | 1007 | "a reserved name on Windows", zFilename, zReserved); |
| 1008 | 1008 | } |
| 1009 | 1009 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -998,11 +998,11 @@ | |
| 998 | #endif |
| 999 | }else{ |
| 1000 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 1001 | out = fossil_fopen(zFilename, "wb"); |
| 1002 | if( out==0 ){ |
| 1003 | #if _WIN32 |
| 1004 | const char *zReserved = file_is_win_reserved(zFilename); |
| 1005 | if( zReserved ){ |
| 1006 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 1007 | "a reserved name on Windows", zFilename, zReserved); |
| 1008 | } |
| 1009 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -998,11 +998,11 @@ | |
| 998 | #endif |
| 999 | }else{ |
| 1000 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 1001 | out = fossil_fopen(zFilename, "wb"); |
| 1002 | if( out==0 ){ |
| 1003 | #if defined(_WIN32) |
| 1004 | const char *zReserved = file_is_win_reserved(zFilename); |
| 1005 | if( zReserved ){ |
| 1006 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 1007 | "a reserved name on Windows", zFilename, zReserved); |
| 1008 | } |
| 1009 |
+1
-1
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -186,11 +186,11 @@ | ||
| 186 | 186 | @ <div id='chat-messages-wrapper'> |
| 187 | 187 | /* New chat messages get inserted immediately after this element */ |
| 188 | 188 | @ <span id='message-inject-point'></span> |
| 189 | 189 | @ </div> |
| 190 | 190 | |
| 191 | - builtin_fossil_js_bundle_or("popupwidget", "storage", NULL); | |
| 191 | + builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch", NULL); | |
| 192 | 192 | /* Always in-line the javascript for the chat page */ |
| 193 | 193 | @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */ |
| 194 | 194 | /* We need an onload handler to ensure that window.fossil is |
| 195 | 195 | initialized before the chat init code runs. */ |
| 196 | 196 | @ window.addEventListener('load', function(){ |
| 197 | 197 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -186,11 +186,11 @@ | |
| 186 | @ <div id='chat-messages-wrapper'> |
| 187 | /* New chat messages get inserted immediately after this element */ |
| 188 | @ <span id='message-inject-point'></span> |
| 189 | @ </div> |
| 190 | |
| 191 | builtin_fossil_js_bundle_or("popupwidget", "storage", NULL); |
| 192 | /* Always in-line the javascript for the chat page */ |
| 193 | @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */ |
| 194 | /* We need an onload handler to ensure that window.fossil is |
| 195 | initialized before the chat init code runs. */ |
| 196 | @ window.addEventListener('load', function(){ |
| 197 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -186,11 +186,11 @@ | |
| 186 | @ <div id='chat-messages-wrapper'> |
| 187 | /* New chat messages get inserted immediately after this element */ |
| 188 | @ <span id='message-inject-point'></span> |
| 189 | @ </div> |
| 190 | |
| 191 | builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch", NULL); |
| 192 | /* Always in-line the javascript for the chat page */ |
| 193 | @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */ |
| 194 | /* We need an onload handler to ensure that window.fossil is |
| 195 | initialized before the chat init code runs. */ |
| 196 | @ window.addEventListener('load', function(){ |
| 197 |
+87
-66
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -397,10 +397,12 @@ | ||
| 397 | 397 | this.playNewMessageSound.uri = uri; |
| 398 | 398 | this.settings.set('audible-alert', !!uri); |
| 399 | 399 | return this; |
| 400 | 400 | } |
| 401 | 401 | }; |
| 402 | + F.fetch.beforesend = ()=>cs.ajaxStart(); | |
| 403 | + F.fetch.aftersend = ()=>cs.ajaxEnd(); | |
| 402 | 404 | cs.e.inputCurrent = cs.e.inputSingle; |
| 403 | 405 | /* Install default settings... */ |
| 404 | 406 | Object.keys(cs.settings.defaults).forEach(function(k){ |
| 405 | 407 | const v = cs.settings.get(k,cs); |
| 406 | 408 | if(cs===v) cs.settings.set(k,cs.settings.defaults[k]); |
| @@ -538,18 +540,15 @@ | ||
| 538 | 540 | e = this.getMessageElemById(id); |
| 539 | 541 | } |
| 540 | 542 | if(!(e instanceof HTMLElement)) return; |
| 541 | 543 | if(this.userMayDelete(e)){ |
| 542 | 544 | this.ajaxStart(); |
| 543 | - fetch("chat-delete/" + id) | |
| 544 | - .then(function(response){ | |
| 545 | - if(!response.ok) throw cs._newResponseError(response); | |
| 546 | - return response; | |
| 547 | - }) | |
| 548 | - .then(()=>this.deleteMessageElem(e)) | |
| 549 | - .catch(err=>this.reportErrorAsMessage(err)) | |
| 550 | - .finally(()=>this.ajaxEnd()); | |
| 545 | + F.fetch("chat-delete/" + id, { | |
| 546 | + responseType: 'json', | |
| 547 | + onload:(r)=>this.deleteMessageElem(r), | |
| 548 | + onerror:(err)=>this.reportErrorAsMessage(err) | |
| 549 | + }); | |
| 551 | 550 | }else{ |
| 552 | 551 | this.deleteMessageElem(id); |
| 553 | 552 | } |
| 554 | 553 | }; |
| 555 | 554 | document.addEventListener('visibilitychange', function(ev){ |
| @@ -878,27 +877,25 @@ | ||
| 878 | 877 | const file = BlobXferState.blob || this.e.inputFile.files[0]; |
| 879 | 878 | if(file) fd.set("file", file); |
| 880 | 879 | if( !msg && !file ) return; |
| 881 | 880 | const self = this; |
| 882 | 881 | fd.set("lmtime", localTime8601(new Date())); |
| 883 | - fetch("chat-send",{ | |
| 884 | - method: 'POST', | |
| 885 | - body: fd | |
| 886 | - }).then((x)=>{ | |
| 887 | - if(x.ok) return x.text(); | |
| 888 | - else throw Chat._newResponseError(x); | |
| 889 | - }).then(function(txt){ | |
| 882 | + F.fetch("chat-send",{ | |
| 883 | + payload: fd, | |
| 884 | + responseType: 'text', | |
| 885 | + onerror:(err)=>this.reportErrorAsMessage(err), | |
| 886 | + onload:function(txt){ | |
| 890 | 887 | if(!txt) return/*success response*/; |
| 891 | 888 | try{ |
| 892 | 889 | const json = JSON.parse(txt); |
| 893 | 890 | self.newContent({msgs:[json]}); |
| 894 | 891 | }catch(e){ |
| 895 | 892 | self.reportError(e); |
| 896 | 893 | return; |
| 897 | 894 | } |
| 898 | - }) | |
| 899 | - .catch((e)=>this.reportErrorAsMessage(e)); | |
| 895 | + } | |
| 896 | + }); | |
| 900 | 897 | BlobXferState.clear(); |
| 901 | 898 | Chat.inputValue("").inputFocus(); |
| 902 | 899 | }; |
| 903 | 900 | |
| 904 | 901 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| @@ -1155,35 +1152,36 @@ | ||
| 1155 | 1152 | D.fieldset(loadLegend), "id", "load-msg-toolbar" |
| 1156 | 1153 | ); |
| 1157 | 1154 | Chat.disableDuringAjax.push(toolbar); |
| 1158 | 1155 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 1159 | 1156 | const loadOldMessages = function(n){ |
| 1160 | - Chat.ajaxStart(); | |
| 1161 | 1157 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1162 | 1158 | Chat._isBatchLoading = true; |
| 1163 | - var gotMessages = false; | |
| 1164 | 1159 | const scrollHt = Chat.e.messagesWrapper.scrollHeight, |
| 1165 | 1160 | scrollTop = Chat.e.messagesWrapper.scrollTop; |
| 1166 | - fetch("chat-poll?before="+Chat.mnMsg+"&n="+n) | |
| 1167 | - .then(Chat._fetchJsonOrError) | |
| 1168 | - .then(function(x){ | |
| 1169 | - gotMessages = x.msgs.length; | |
| 1161 | + F.fetch("chat-poll",{ | |
| 1162 | + urlParams:{ | |
| 1163 | + before: Chat.mnMsg, | |
| 1164 | + n: n | |
| 1165 | + }, | |
| 1166 | + responseType: 'json', | |
| 1167 | + onerror:function(err){ | |
| 1168 | + Chat.reportErrorAsMessage(err); | |
| 1169 | + Chat._isBatchLoading = false; | |
| 1170 | + }, | |
| 1171 | + onload:function(x){ | |
| 1172 | + let gotMessages = x.msgs.length; | |
| 1170 | 1173 | newcontent(x,true); |
| 1171 | - }) | |
| 1172 | - .catch(e=>Chat.reportErrorAsMessage(e)) | |
| 1173 | - .finally(function(){ | |
| 1174 | 1174 | Chat._isBatchLoading = false; |
| 1175 | - Chat.e.messagesWrapper.classList.remove('loading'); | |
| 1176 | - Chat.ajaxEnd(); | |
| 1177 | 1175 | if(Chat._gotServerError){ |
| 1178 | - F.toast.error("Got an error response from the server. ", | |
| 1179 | - "See message for details."); | |
| 1176 | + Chat._gotServerError = false; | |
| 1180 | 1177 | return; |
| 1181 | - }else if(n<0/*we asked for all history*/ | |
| 1178 | + } | |
| 1179 | + if(n<0/*we asked for all history*/ | |
| 1182 | 1180 | || 0===gotMessages/*we found no history*/ |
| 1183 | 1181 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 1184 | - || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount | |
| 1182 | + || (n===0 && gotMessages<Chat.loadMessageCount | |
| 1185 | 1183 | /*we asked for default amount and got fewer than that.*/)){ |
| 1186 | 1184 | /* We've loaded all history. Permanently disable the |
| 1187 | 1185 | history-load toolbar and keep it from being re-enabled |
| 1188 | 1186 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 1189 | 1187 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -1199,11 +1197,16 @@ | ||
| 1199 | 1197 | was requested, per user request */ |
| 1200 | 1198 | Chat.e.messagesWrapper.scrollTo( |
| 1201 | 1199 | 0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop |
| 1202 | 1200 | ); |
| 1203 | 1201 | } |
| 1204 | - }); | |
| 1202 | + }, | |
| 1203 | + aftersend:function(){ | |
| 1204 | + Chat.e.messagesWrapper.classList.remove('loading'); | |
| 1205 | + Chat.ajaxEnd(); | |
| 1206 | + } | |
| 1207 | + }); | |
| 1205 | 1208 | }; |
| 1206 | 1209 | const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */; |
| 1207 | 1210 | D.append(toolbar, wrapper); |
| 1208 | 1211 | var btn = D.button("Previous "+Chat.loadMessageCount+" messages"); |
| 1209 | 1212 | D.append(wrapper, btn); |
| @@ -1213,47 +1216,65 @@ | ||
| 1213 | 1216 | btn.addEventListener('click',()=>loadOldMessages(-1)); |
| 1214 | 1217 | D.append(Chat.e.messagesWrapper, toolbar); |
| 1215 | 1218 | toolbar.disabled = true /*will be enabled when msg load finishes */; |
| 1216 | 1219 | })()/*end history loading widget setup*/; |
| 1217 | 1220 | |
| 1218 | - async function poll(isFirstCall){ | |
| 1219 | - if(poll.running) return; | |
| 1220 | - poll.running = true; | |
| 1221 | - if(isFirstCall){ | |
| 1221 | + const afterFetch = function f(){ | |
| 1222 | + if(true===f.isFirstCall){ | |
| 1223 | + f.isFirstCall = false; | |
| 1224 | + Chat.ajaxEnd(); | |
| 1225 | + Chat.e.messagesWrapper.classList.remove('loading'); | |
| 1226 | + setTimeout(function(){ | |
| 1227 | + Chat.scrollMessagesTo(1); | |
| 1228 | + }, 250); | |
| 1229 | + } | |
| 1230 | + if(Chat._gotServerError && Chat.intervalTimer){ | |
| 1231 | + clearInterval(Chat.intervalTimer); | |
| 1232 | + Chat.reportErrorAsMessage( | |
| 1233 | + "Shutting down chat poller due to server-side error. ", | |
| 1234 | + "Reload this page to reactivate it."); | |
| 1235 | + delete Chat.intervalTimer; | |
| 1236 | + } | |
| 1237 | + poll.running = false; | |
| 1238 | + }; | |
| 1239 | + afterFetch.isFirstCall = true; | |
| 1240 | + const poll = async function f(){ | |
| 1241 | + if(f.running) return; | |
| 1242 | + f.running = true; | |
| 1243 | + Chat._isBatchLoading = f.isFirstCall; | |
| 1244 | + if(true===f.isFirstCall){ | |
| 1245 | + f.isFirstCall = false; | |
| 1222 | 1246 | Chat.ajaxStart(); |
| 1223 | 1247 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1224 | 1248 | } |
| 1225 | - Chat._isBatchLoading = isFirstCall; | |
| 1226 | - var p = fetch("chat-poll?name=" + Chat.mxMsg); | |
| 1227 | - p.then(Chat._fetchJsonOrError) | |
| 1228 | - .then(y=>newcontent(y)) | |
| 1229 | - .catch(e=>console.error(e)) | |
| 1230 | - /* ^^^ we don't use Chat.reportError(e) here b/c the polling | |
| 1231 | - fails exepectedly when it times out, but is then immediately | |
| 1232 | - resumed, and reportError() produces a loud error message. */ | |
| 1233 | - .finally(function(){ | |
| 1234 | - if(isFirstCall){ | |
| 1235 | - Chat._isBatchLoading = false; | |
| 1236 | - Chat.ajaxEnd(); | |
| 1237 | - Chat.e.messagesWrapper.classList.remove('loading'); | |
| 1238 | - setTimeout(function(){ | |
| 1239 | - Chat.scrollMessagesTo(1); | |
| 1240 | - }, 250); | |
| 1241 | - } | |
| 1242 | - if(Chat._gotServerError && Chat.intervalTimer){ | |
| 1243 | - clearInterval(Chat.intervalTimer); | |
| 1244 | - delete Chat.intervalTimer; | |
| 1245 | - } | |
| 1246 | - poll.running=false; | |
| 1247 | - }); | |
| 1248 | - } | |
| 1249 | + F.fetch("chat-poll",{ | |
| 1250 | + timeout: 420 * 1000/*FIXME: get the value from the server*/, | |
| 1251 | + urlParams:{ | |
| 1252 | + name: Chat.mxMsg | |
| 1253 | + }, | |
| 1254 | + responseType: "json", | |
| 1255 | + // Disable the ajax start/end handling for this long-polling op: | |
| 1256 | + beforesend: function(){}, | |
| 1257 | + aftersend: function(){}, | |
| 1258 | + onerror:function(err){ | |
| 1259 | + Chat._isBatchLoading = false; | |
| 1260 | + console.error(err); | |
| 1261 | + /* ^^^ we don't use Chat.reportError() here b/c the polling | |
| 1262 | + fails exepectedly when it times out, but is then immediately | |
| 1263 | + resumed, and reportError() produces a loud error message. */ | |
| 1264 | + afterFetch(); | |
| 1265 | + }, | |
| 1266 | + onload:function(y){ | |
| 1267 | + newcontent(y); | |
| 1268 | + Chat._isBatchLoading = false; | |
| 1269 | + afterFetch(); | |
| 1270 | + } | |
| 1271 | + }); | |
| 1272 | + }; | |
| 1273 | + poll.isFirstCall = true; | |
| 1249 | 1274 | Chat._gotServerError = poll.running = false; |
| 1250 | - poll(true); | |
| 1251 | - if(!Chat._gotServerError){ | |
| 1252 | - Chat.intervalTimer = setInterval(poll, 1000); | |
| 1253 | - } | |
| 1254 | 1275 | if( window.fossil.config.chat.fromcli ){ |
| 1255 | 1276 | Chat.chatOnlyMode(true); |
| 1256 | 1277 | } |
| 1257 | - | |
| 1278 | + Chat.intervalTimer = setInterval(poll, 1000); | |
| 1258 | 1279 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1259 | 1280 | })(); |
| 1260 | 1281 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -397,10 +397,12 @@ | |
| 397 | this.playNewMessageSound.uri = uri; |
| 398 | this.settings.set('audible-alert', !!uri); |
| 399 | return this; |
| 400 | } |
| 401 | }; |
| 402 | cs.e.inputCurrent = cs.e.inputSingle; |
| 403 | /* Install default settings... */ |
| 404 | Object.keys(cs.settings.defaults).forEach(function(k){ |
| 405 | const v = cs.settings.get(k,cs); |
| 406 | if(cs===v) cs.settings.set(k,cs.settings.defaults[k]); |
| @@ -538,18 +540,15 @@ | |
| 538 | e = this.getMessageElemById(id); |
| 539 | } |
| 540 | if(!(e instanceof HTMLElement)) return; |
| 541 | if(this.userMayDelete(e)){ |
| 542 | this.ajaxStart(); |
| 543 | fetch("chat-delete/" + id) |
| 544 | .then(function(response){ |
| 545 | if(!response.ok) throw cs._newResponseError(response); |
| 546 | return response; |
| 547 | }) |
| 548 | .then(()=>this.deleteMessageElem(e)) |
| 549 | .catch(err=>this.reportErrorAsMessage(err)) |
| 550 | .finally(()=>this.ajaxEnd()); |
| 551 | }else{ |
| 552 | this.deleteMessageElem(id); |
| 553 | } |
| 554 | }; |
| 555 | document.addEventListener('visibilitychange', function(ev){ |
| @@ -878,27 +877,25 @@ | |
| 878 | const file = BlobXferState.blob || this.e.inputFile.files[0]; |
| 879 | if(file) fd.set("file", file); |
| 880 | if( !msg && !file ) return; |
| 881 | const self = this; |
| 882 | fd.set("lmtime", localTime8601(new Date())); |
| 883 | fetch("chat-send",{ |
| 884 | method: 'POST', |
| 885 | body: fd |
| 886 | }).then((x)=>{ |
| 887 | if(x.ok) return x.text(); |
| 888 | else throw Chat._newResponseError(x); |
| 889 | }).then(function(txt){ |
| 890 | if(!txt) return/*success response*/; |
| 891 | try{ |
| 892 | const json = JSON.parse(txt); |
| 893 | self.newContent({msgs:[json]}); |
| 894 | }catch(e){ |
| 895 | self.reportError(e); |
| 896 | return; |
| 897 | } |
| 898 | }) |
| 899 | .catch((e)=>this.reportErrorAsMessage(e)); |
| 900 | BlobXferState.clear(); |
| 901 | Chat.inputValue("").inputFocus(); |
| 902 | }; |
| 903 | |
| 904 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| @@ -1155,35 +1152,36 @@ | |
| 1155 | D.fieldset(loadLegend), "id", "load-msg-toolbar" |
| 1156 | ); |
| 1157 | Chat.disableDuringAjax.push(toolbar); |
| 1158 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 1159 | const loadOldMessages = function(n){ |
| 1160 | Chat.ajaxStart(); |
| 1161 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1162 | Chat._isBatchLoading = true; |
| 1163 | var gotMessages = false; |
| 1164 | const scrollHt = Chat.e.messagesWrapper.scrollHeight, |
| 1165 | scrollTop = Chat.e.messagesWrapper.scrollTop; |
| 1166 | fetch("chat-poll?before="+Chat.mnMsg+"&n="+n) |
| 1167 | .then(Chat._fetchJsonOrError) |
| 1168 | .then(function(x){ |
| 1169 | gotMessages = x.msgs.length; |
| 1170 | newcontent(x,true); |
| 1171 | }) |
| 1172 | .catch(e=>Chat.reportErrorAsMessage(e)) |
| 1173 | .finally(function(){ |
| 1174 | Chat._isBatchLoading = false; |
| 1175 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 1176 | Chat.ajaxEnd(); |
| 1177 | if(Chat._gotServerError){ |
| 1178 | F.toast.error("Got an error response from the server. ", |
| 1179 | "See message for details."); |
| 1180 | return; |
| 1181 | }else if(n<0/*we asked for all history*/ |
| 1182 | || 0===gotMessages/*we found no history*/ |
| 1183 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 1184 | || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount |
| 1185 | /*we asked for default amount and got fewer than that.*/)){ |
| 1186 | /* We've loaded all history. Permanently disable the |
| 1187 | history-load toolbar and keep it from being re-enabled |
| 1188 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 1189 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -1199,11 +1197,16 @@ | |
| 1199 | was requested, per user request */ |
| 1200 | Chat.e.messagesWrapper.scrollTo( |
| 1201 | 0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop |
| 1202 | ); |
| 1203 | } |
| 1204 | }); |
| 1205 | }; |
| 1206 | const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */; |
| 1207 | D.append(toolbar, wrapper); |
| 1208 | var btn = D.button("Previous "+Chat.loadMessageCount+" messages"); |
| 1209 | D.append(wrapper, btn); |
| @@ -1213,47 +1216,65 @@ | |
| 1213 | btn.addEventListener('click',()=>loadOldMessages(-1)); |
| 1214 | D.append(Chat.e.messagesWrapper, toolbar); |
| 1215 | toolbar.disabled = true /*will be enabled when msg load finishes */; |
| 1216 | })()/*end history loading widget setup*/; |
| 1217 | |
| 1218 | async function poll(isFirstCall){ |
| 1219 | if(poll.running) return; |
| 1220 | poll.running = true; |
| 1221 | if(isFirstCall){ |
| 1222 | Chat.ajaxStart(); |
| 1223 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1224 | } |
| 1225 | Chat._isBatchLoading = isFirstCall; |
| 1226 | var p = fetch("chat-poll?name=" + Chat.mxMsg); |
| 1227 | p.then(Chat._fetchJsonOrError) |
| 1228 | .then(y=>newcontent(y)) |
| 1229 | .catch(e=>console.error(e)) |
| 1230 | /* ^^^ we don't use Chat.reportError(e) here b/c the polling |
| 1231 | fails exepectedly when it times out, but is then immediately |
| 1232 | resumed, and reportError() produces a loud error message. */ |
| 1233 | .finally(function(){ |
| 1234 | if(isFirstCall){ |
| 1235 | Chat._isBatchLoading = false; |
| 1236 | Chat.ajaxEnd(); |
| 1237 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 1238 | setTimeout(function(){ |
| 1239 | Chat.scrollMessagesTo(1); |
| 1240 | }, 250); |
| 1241 | } |
| 1242 | if(Chat._gotServerError && Chat.intervalTimer){ |
| 1243 | clearInterval(Chat.intervalTimer); |
| 1244 | delete Chat.intervalTimer; |
| 1245 | } |
| 1246 | poll.running=false; |
| 1247 | }); |
| 1248 | } |
| 1249 | Chat._gotServerError = poll.running = false; |
| 1250 | poll(true); |
| 1251 | if(!Chat._gotServerError){ |
| 1252 | Chat.intervalTimer = setInterval(poll, 1000); |
| 1253 | } |
| 1254 | if( window.fossil.config.chat.fromcli ){ |
| 1255 | Chat.chatOnlyMode(true); |
| 1256 | } |
| 1257 | |
| 1258 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1259 | })(); |
| 1260 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -397,10 +397,12 @@ | |
| 397 | this.playNewMessageSound.uri = uri; |
| 398 | this.settings.set('audible-alert', !!uri); |
| 399 | return this; |
| 400 | } |
| 401 | }; |
| 402 | F.fetch.beforesend = ()=>cs.ajaxStart(); |
| 403 | F.fetch.aftersend = ()=>cs.ajaxEnd(); |
| 404 | cs.e.inputCurrent = cs.e.inputSingle; |
| 405 | /* Install default settings... */ |
| 406 | Object.keys(cs.settings.defaults).forEach(function(k){ |
| 407 | const v = cs.settings.get(k,cs); |
| 408 | if(cs===v) cs.settings.set(k,cs.settings.defaults[k]); |
| @@ -538,18 +540,15 @@ | |
| 540 | e = this.getMessageElemById(id); |
| 541 | } |
| 542 | if(!(e instanceof HTMLElement)) return; |
| 543 | if(this.userMayDelete(e)){ |
| 544 | this.ajaxStart(); |
| 545 | F.fetch("chat-delete/" + id, { |
| 546 | responseType: 'json', |
| 547 | onload:(r)=>this.deleteMessageElem(r), |
| 548 | onerror:(err)=>this.reportErrorAsMessage(err) |
| 549 | }); |
| 550 | }else{ |
| 551 | this.deleteMessageElem(id); |
| 552 | } |
| 553 | }; |
| 554 | document.addEventListener('visibilitychange', function(ev){ |
| @@ -878,27 +877,25 @@ | |
| 877 | const file = BlobXferState.blob || this.e.inputFile.files[0]; |
| 878 | if(file) fd.set("file", file); |
| 879 | if( !msg && !file ) return; |
| 880 | const self = this; |
| 881 | fd.set("lmtime", localTime8601(new Date())); |
| 882 | F.fetch("chat-send",{ |
| 883 | payload: fd, |
| 884 | responseType: 'text', |
| 885 | onerror:(err)=>this.reportErrorAsMessage(err), |
| 886 | onload:function(txt){ |
| 887 | if(!txt) return/*success response*/; |
| 888 | try{ |
| 889 | const json = JSON.parse(txt); |
| 890 | self.newContent({msgs:[json]}); |
| 891 | }catch(e){ |
| 892 | self.reportError(e); |
| 893 | return; |
| 894 | } |
| 895 | } |
| 896 | }); |
| 897 | BlobXferState.clear(); |
| 898 | Chat.inputValue("").inputFocus(); |
| 899 | }; |
| 900 | |
| 901 | Chat.e.inputSingle.addEventListener('keydown',function(ev){ |
| @@ -1155,35 +1152,36 @@ | |
| 1152 | D.fieldset(loadLegend), "id", "load-msg-toolbar" |
| 1153 | ); |
| 1154 | Chat.disableDuringAjax.push(toolbar); |
| 1155 | /* Loads the next n oldest messages, or all previous history if n is negative. */ |
| 1156 | const loadOldMessages = function(n){ |
| 1157 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1158 | Chat._isBatchLoading = true; |
| 1159 | const scrollHt = Chat.e.messagesWrapper.scrollHeight, |
| 1160 | scrollTop = Chat.e.messagesWrapper.scrollTop; |
| 1161 | F.fetch("chat-poll",{ |
| 1162 | urlParams:{ |
| 1163 | before: Chat.mnMsg, |
| 1164 | n: n |
| 1165 | }, |
| 1166 | responseType: 'json', |
| 1167 | onerror:function(err){ |
| 1168 | Chat.reportErrorAsMessage(err); |
| 1169 | Chat._isBatchLoading = false; |
| 1170 | }, |
| 1171 | onload:function(x){ |
| 1172 | let gotMessages = x.msgs.length; |
| 1173 | newcontent(x,true); |
| 1174 | Chat._isBatchLoading = false; |
| 1175 | if(Chat._gotServerError){ |
| 1176 | Chat._gotServerError = false; |
| 1177 | return; |
| 1178 | } |
| 1179 | if(n<0/*we asked for all history*/ |
| 1180 | || 0===gotMessages/*we found no history*/ |
| 1181 | || (n>0 && gotMessages<n /*we got fewer history entries than requested*/) |
| 1182 | || (n===0 && gotMessages<Chat.loadMessageCount |
| 1183 | /*we asked for default amount and got fewer than that.*/)){ |
| 1184 | /* We've loaded all history. Permanently disable the |
| 1185 | history-load toolbar and keep it from being re-enabled |
| 1186 | via the ajaxStart()/ajaxEnd() mechanism... */ |
| 1187 | const div = Chat.e.loadOlderToolbar.querySelector('div'); |
| @@ -1199,11 +1197,16 @@ | |
| 1197 | was requested, per user request */ |
| 1198 | Chat.e.messagesWrapper.scrollTo( |
| 1199 | 0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop |
| 1200 | ); |
| 1201 | } |
| 1202 | }, |
| 1203 | aftersend:function(){ |
| 1204 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 1205 | Chat.ajaxEnd(); |
| 1206 | } |
| 1207 | }); |
| 1208 | }; |
| 1209 | const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */; |
| 1210 | D.append(toolbar, wrapper); |
| 1211 | var btn = D.button("Previous "+Chat.loadMessageCount+" messages"); |
| 1212 | D.append(wrapper, btn); |
| @@ -1213,47 +1216,65 @@ | |
| 1216 | btn.addEventListener('click',()=>loadOldMessages(-1)); |
| 1217 | D.append(Chat.e.messagesWrapper, toolbar); |
| 1218 | toolbar.disabled = true /*will be enabled when msg load finishes */; |
| 1219 | })()/*end history loading widget setup*/; |
| 1220 | |
| 1221 | const afterFetch = function f(){ |
| 1222 | if(true===f.isFirstCall){ |
| 1223 | f.isFirstCall = false; |
| 1224 | Chat.ajaxEnd(); |
| 1225 | Chat.e.messagesWrapper.classList.remove('loading'); |
| 1226 | setTimeout(function(){ |
| 1227 | Chat.scrollMessagesTo(1); |
| 1228 | }, 250); |
| 1229 | } |
| 1230 | if(Chat._gotServerError && Chat.intervalTimer){ |
| 1231 | clearInterval(Chat.intervalTimer); |
| 1232 | Chat.reportErrorAsMessage( |
| 1233 | "Shutting down chat poller due to server-side error. ", |
| 1234 | "Reload this page to reactivate it."); |
| 1235 | delete Chat.intervalTimer; |
| 1236 | } |
| 1237 | poll.running = false; |
| 1238 | }; |
| 1239 | afterFetch.isFirstCall = true; |
| 1240 | const poll = async function f(){ |
| 1241 | if(f.running) return; |
| 1242 | f.running = true; |
| 1243 | Chat._isBatchLoading = f.isFirstCall; |
| 1244 | if(true===f.isFirstCall){ |
| 1245 | f.isFirstCall = false; |
| 1246 | Chat.ajaxStart(); |
| 1247 | Chat.e.messagesWrapper.classList.add('loading'); |
| 1248 | } |
| 1249 | F.fetch("chat-poll",{ |
| 1250 | timeout: 420 * 1000/*FIXME: get the value from the server*/, |
| 1251 | urlParams:{ |
| 1252 | name: Chat.mxMsg |
| 1253 | }, |
| 1254 | responseType: "json", |
| 1255 | // Disable the ajax start/end handling for this long-polling op: |
| 1256 | beforesend: function(){}, |
| 1257 | aftersend: function(){}, |
| 1258 | onerror:function(err){ |
| 1259 | Chat._isBatchLoading = false; |
| 1260 | console.error(err); |
| 1261 | /* ^^^ we don't use Chat.reportError() here b/c the polling |
| 1262 | fails exepectedly when it times out, but is then immediately |
| 1263 | resumed, and reportError() produces a loud error message. */ |
| 1264 | afterFetch(); |
| 1265 | }, |
| 1266 | onload:function(y){ |
| 1267 | newcontent(y); |
| 1268 | Chat._isBatchLoading = false; |
| 1269 | afterFetch(); |
| 1270 | } |
| 1271 | }); |
| 1272 | }; |
| 1273 | poll.isFirstCall = true; |
| 1274 | Chat._gotServerError = poll.running = false; |
| 1275 | if( window.fossil.config.chat.fromcli ){ |
| 1276 | Chat.chatOnlyMode(true); |
| 1277 | } |
| 1278 | Chat.intervalTimer = setInterval(poll, 1000); |
| 1279 | F.page.chat = Chat/* enables testing the APIs via the dev tools */; |
| 1280 | })(); |
| 1281 |
+22
-1
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -2059,11 +2059,11 @@ | ||
| 2059 | 2059 | }else{ |
| 2060 | 2060 | FILE * p; |
| 2061 | 2061 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 2062 | 2062 | p = fossil_fopen(zFilename, "wb"); |
| 2063 | 2063 | if( p==0 ){ |
| 2064 | -#if _WIN32 | |
| 2064 | +#if defined(_WIN32) | |
| 2065 | 2065 | const char *zReserved = file_is_win_reserved(zFilename); |
| 2066 | 2066 | if( zReserved ){ |
| 2067 | 2067 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 2068 | 2068 | "a reserved name on Windows", zFilename, |
| 2069 | 2069 | zReserved); |
| @@ -2565,5 +2565,26 @@ | ||
| 2565 | 2565 | default:{ |
| 2566 | 2566 | return 0; |
| 2567 | 2567 | } |
| 2568 | 2568 | } |
| 2569 | 2569 | } |
| 2570 | + | |
| 2571 | +/* | |
| 2572 | +** COMMAND: test-is-reserved-name | |
| 2573 | +** | |
| 2574 | +** Usage: %fossil test-is-reserved-name FILENAMES... | |
| 2575 | +** | |
| 2576 | +** Passes each given name to file_is_reserved_name() and outputs one | |
| 2577 | +** line per file: the result value of that function followed by the | |
| 2578 | +** name. | |
| 2579 | +*/ | |
| 2580 | +void test_is_reserved_name_cmd(void){ | |
| 2581 | + int i; | |
| 2582 | + | |
| 2583 | + if(g.argc<3){ | |
| 2584 | + usage("FILENAME_1 [...FILENAME_N]"); | |
| 2585 | + } | |
| 2586 | + for( i = 2; i < g.argc; ++i ){ | |
| 2587 | + const int check = file_is_reserved_name(g.argv[i], -1); | |
| 2588 | + fossil_print("%d %s\n", check, g.argv[i]); | |
| 2589 | + } | |
| 2590 | +} | |
| 2570 | 2591 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -2059,11 +2059,11 @@ | |
| 2059 | }else{ |
| 2060 | FILE * p; |
| 2061 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 2062 | p = fossil_fopen(zFilename, "wb"); |
| 2063 | if( p==0 ){ |
| 2064 | #if _WIN32 |
| 2065 | const char *zReserved = file_is_win_reserved(zFilename); |
| 2066 | if( zReserved ){ |
| 2067 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 2068 | "a reserved name on Windows", zFilename, |
| 2069 | zReserved); |
| @@ -2565,5 +2565,26 @@ | |
| 2565 | default:{ |
| 2566 | return 0; |
| 2567 | } |
| 2568 | } |
| 2569 | } |
| 2570 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -2059,11 +2059,11 @@ | |
| 2059 | }else{ |
| 2060 | FILE * p; |
| 2061 | file_mkfolder(zFilename, ExtFILE, 1, 0); |
| 2062 | p = fossil_fopen(zFilename, "wb"); |
| 2063 | if( p==0 ){ |
| 2064 | #if defined(_WIN32) |
| 2065 | const char *zReserved = file_is_win_reserved(zFilename); |
| 2066 | if( zReserved ){ |
| 2067 | fossil_fatal("cannot open \"%s\" because \"%s\" is " |
| 2068 | "a reserved name on Windows", zFilename, |
| 2069 | zReserved); |
| @@ -2565,5 +2565,26 @@ | |
| 2565 | default:{ |
| 2566 | return 0; |
| 2567 | } |
| 2568 | } |
| 2569 | } |
| 2570 | |
| 2571 | /* |
| 2572 | ** COMMAND: test-is-reserved-name |
| 2573 | ** |
| 2574 | ** Usage: %fossil test-is-reserved-name FILENAMES... |
| 2575 | ** |
| 2576 | ** Passes each given name to file_is_reserved_name() and outputs one |
| 2577 | ** line per file: the result value of that function followed by the |
| 2578 | ** name. |
| 2579 | */ |
| 2580 | void test_is_reserved_name_cmd(void){ |
| 2581 | int i; |
| 2582 | |
| 2583 | if(g.argc<3){ |
| 2584 | usage("FILENAME_1 [...FILENAME_N]"); |
| 2585 | } |
| 2586 | for( i = 2; i < g.argc; ++i ){ |
| 2587 | const int check = file_is_reserved_name(g.argv[i], -1); |
| 2588 | fossil_print("%d %s\n", check, g.argv[i]); |
| 2589 | } |
| 2590 | } |
| 2591 |
+3
-3
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -481,16 +481,16 @@ | ||
| 481 | 481 | ** * The post is unedited |
| 482 | 482 | ** * The post was last edited by the original author |
| 483 | 483 | ** * The post was last edited by a different person |
| 484 | 484 | */ |
| 485 | 485 | if( p->pEditHead ){ |
| 486 | - zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate); | |
| 486 | + zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->pEditHead->rDate); | |
| 487 | 487 | }else{ |
| 488 | 488 | zPosterName = forum_post_display_name(p, pManifest); |
| 489 | 489 | zEditorName = zPosterName; |
| 490 | 490 | } |
| 491 | - zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); | |
| 491 | + zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate); | |
| 492 | 492 | if( p->pEditPrev ){ |
| 493 | 493 | zPosterName = forum_post_display_name(p->pEditHead, 0); |
| 494 | 494 | zEditorName = forum_post_display_name(p, pManifest); |
| 495 | 495 | zHist = bHist ? "" : "&hist"; |
| 496 | 496 | @ <h3 class='forumPostHdr'>(%d(p->sid)\ |
| @@ -1287,11 +1287,11 @@ | ||
| 1287 | 1287 | style_header("Reply"); |
| 1288 | 1288 | if( pRootPost->zThreadTitle ){ |
| 1289 | 1289 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1290 | 1290 | } |
| 1291 | 1291 | @ <h2>Replying To:</h2> |
| 1292 | - zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); | |
| 1292 | + zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); | |
| 1293 | 1293 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1294 | 1294 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1295 | 1295 | fossil_free(zDisplayName); |
| 1296 | 1296 | fossil_free(zDate); |
| 1297 | 1297 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1298 | 1298 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -481,16 +481,16 @@ | |
| 481 | ** * The post is unedited |
| 482 | ** * The post was last edited by the original author |
| 483 | ** * The post was last edited by a different person |
| 484 | */ |
| 485 | if( p->pEditHead ){ |
| 486 | zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate); |
| 487 | }else{ |
| 488 | zPosterName = forum_post_display_name(p, pManifest); |
| 489 | zEditorName = zPosterName; |
| 490 | } |
| 491 | zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate); |
| 492 | if( p->pEditPrev ){ |
| 493 | zPosterName = forum_post_display_name(p->pEditHead, 0); |
| 494 | zEditorName = forum_post_display_name(p, pManifest); |
| 495 | zHist = bHist ? "" : "&hist"; |
| 496 | @ <h3 class='forumPostHdr'>(%d(p->sid)\ |
| @@ -1287,11 +1287,11 @@ | |
| 1287 | style_header("Reply"); |
| 1288 | if( pRootPost->zThreadTitle ){ |
| 1289 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1290 | } |
| 1291 | @ <h2>Replying To:</h2> |
| 1292 | zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); |
| 1293 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1294 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1295 | fossil_free(zDisplayName); |
| 1296 | fossil_free(zDate); |
| 1297 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1298 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -481,16 +481,16 @@ | |
| 481 | ** * The post is unedited |
| 482 | ** * The post was last edited by the original author |
| 483 | ** * The post was last edited by a different person |
| 484 | */ |
| 485 | if( p->pEditHead ){ |
| 486 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->pEditHead->rDate); |
| 487 | }else{ |
| 488 | zPosterName = forum_post_display_name(p, pManifest); |
| 489 | zEditorName = zPosterName; |
| 490 | } |
| 491 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate); |
| 492 | if( p->pEditPrev ){ |
| 493 | zPosterName = forum_post_display_name(p->pEditHead, 0); |
| 494 | zEditorName = forum_post_display_name(p, pManifest); |
| 495 | zHist = bHist ? "" : "&hist"; |
| 496 | @ <h3 class='forumPostHdr'>(%d(p->sid)\ |
| @@ -1287,11 +1287,11 @@ | |
| 1287 | style_header("Reply"); |
| 1288 | if( pRootPost->zThreadTitle ){ |
| 1289 | @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1> |
| 1290 | } |
| 1291 | @ <h2>Replying To:</h2> |
| 1292 | zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate); |
| 1293 | zDisplayName = display_name_from_login(pPost->zUser); |
| 1294 | @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3> |
| 1295 | fossil_free(zDisplayName); |
| 1296 | fossil_free(zDate); |
| 1297 | forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1); |
| 1298 |
+13
-1
| --- src/fossil.fetch.js | ||
| +++ src/fossil.fetch.js | ||
| @@ -86,11 +86,11 @@ | ||
| 86 | 86 | - timeout: integer in milliseconds specifying the XHR timeout |
| 87 | 87 | duration. Default = fossil.fetch.timeout. |
| 88 | 88 | |
| 89 | 89 | When an options object does not provide |
| 90 | 90 | onload/onerror/beforesend/aftersend handlers of its own, this |
| 91 | - function falls to defaults which are member properties of this | |
| 91 | + function falls back to defaults which are member properties of this | |
| 92 | 92 | function with the same name, e.g. fossil.fetch.onload(). The |
| 93 | 93 | default onload/onerror implementations route the data through the |
| 94 | 94 | dev console and (for onerror()) through fossil.error(). The default |
| 95 | 95 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 96 | 96 | those members to provide default implementations suitable for the |
| @@ -166,10 +166,22 @@ | ||
| 166 | 166 | opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired.")); |
| 167 | 167 | }; |
| 168 | 168 | x.onreadystatechange = function(){ |
| 169 | 169 | if(XMLHttpRequest.DONE !== x.readyState) return; |
| 170 | 170 | try{opt.aftersend()}catch(e){/*ignore*/} |
| 171 | + if(false && 0===x.status){ | |
| 172 | + /* For reasons unknown, we _sometimes_ trigger x.status==0 in FF | |
| 173 | + when the /chat page starts up, but not in Chrome nor in other | |
| 174 | + apps. Insofar as has been determined, this happens before a | |
| 175 | + request is actually sent and it appears to have no | |
| 176 | + side-effects on the app other than to generate an error | |
| 177 | + (i.e. no requests/responses are missing). This is a silly | |
| 178 | + workaround which may or may not bite us later. If so, it can | |
| 179 | + be removed at the cost of an unsightly console error message | |
| 180 | + in FF. */ | |
| 181 | + return; | |
| 182 | + } | |
| 171 | 183 | if(200!==x.status){ |
| 172 | 184 | let err; |
| 173 | 185 | try{ |
| 174 | 186 | const j = JSON.parse(x.response); |
| 175 | 187 | if(j.error) err = new Error(j.error); |
| 176 | 188 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -86,11 +86,11 @@ | |
| 86 | - timeout: integer in milliseconds specifying the XHR timeout |
| 87 | duration. Default = fossil.fetch.timeout. |
| 88 | |
| 89 | When an options object does not provide |
| 90 | onload/onerror/beforesend/aftersend handlers of its own, this |
| 91 | function falls to defaults which are member properties of this |
| 92 | function with the same name, e.g. fossil.fetch.onload(). The |
| 93 | default onload/onerror implementations route the data through the |
| 94 | dev console and (for onerror()) through fossil.error(). The default |
| 95 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 96 | those members to provide default implementations suitable for the |
| @@ -166,10 +166,22 @@ | |
| 166 | opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired.")); |
| 167 | }; |
| 168 | x.onreadystatechange = function(){ |
| 169 | if(XMLHttpRequest.DONE !== x.readyState) return; |
| 170 | try{opt.aftersend()}catch(e){/*ignore*/} |
| 171 | if(200!==x.status){ |
| 172 | let err; |
| 173 | try{ |
| 174 | const j = JSON.parse(x.response); |
| 175 | if(j.error) err = new Error(j.error); |
| 176 |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -86,11 +86,11 @@ | |
| 86 | - timeout: integer in milliseconds specifying the XHR timeout |
| 87 | duration. Default = fossil.fetch.timeout. |
| 88 | |
| 89 | When an options object does not provide |
| 90 | onload/onerror/beforesend/aftersend handlers of its own, this |
| 91 | function falls back to defaults which are member properties of this |
| 92 | function with the same name, e.g. fossil.fetch.onload(). The |
| 93 | default onload/onerror implementations route the data through the |
| 94 | dev console and (for onerror()) through fossil.error(). The default |
| 95 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 96 | those members to provide default implementations suitable for the |
| @@ -166,10 +166,22 @@ | |
| 166 | opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired.")); |
| 167 | }; |
| 168 | x.onreadystatechange = function(){ |
| 169 | if(XMLHttpRequest.DONE !== x.readyState) return; |
| 170 | try{opt.aftersend()}catch(e){/*ignore*/} |
| 171 | if(false && 0===x.status){ |
| 172 | /* For reasons unknown, we _sometimes_ trigger x.status==0 in FF |
| 173 | when the /chat page starts up, but not in Chrome nor in other |
| 174 | apps. Insofar as has been determined, this happens before a |
| 175 | request is actually sent and it appears to have no |
| 176 | side-effects on the app other than to generate an error |
| 177 | (i.e. no requests/responses are missing). This is a silly |
| 178 | workaround which may or may not bite us later. If so, it can |
| 179 | be removed at the cost of an unsightly console error message |
| 180 | in FF. */ |
| 181 | return; |
| 182 | } |
| 183 | if(200!==x.status){ |
| 184 | let err; |
| 185 | try{ |
| 186 | const j = JSON.parse(x.response); |
| 187 | if(j.error) err = new Error(j.error); |
| 188 |
+2
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1662,11 +1662,12 @@ | ||
| 1662 | 1662 | ** * The "preferred-diff-type" setting |
| 1663 | 1663 | ** * 1 for mobile and 2 for desktop, based on the UserAgent |
| 1664 | 1664 | */ |
| 1665 | 1665 | int preferred_diff_type(void){ |
| 1666 | 1666 | int dflt; |
| 1667 | - char zDflt[2]; | |
| 1667 | + static char zDflt[2] | |
| 1668 | + /*static b/c cookie_link_parameter() does not copy it!*/; | |
| 1668 | 1669 | dflt = db_get_int("preferred-diff-type",-99); |
| 1669 | 1670 | if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2; |
| 1670 | 1671 | zDflt[0] = dflt + '0'; |
| 1671 | 1672 | zDflt[1] = 0; |
| 1672 | 1673 | cookie_link_parameter("diff","diff", zDflt); |
| 1673 | 1674 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1662,11 +1662,12 @@ | |
| 1662 | ** * The "preferred-diff-type" setting |
| 1663 | ** * 1 for mobile and 2 for desktop, based on the UserAgent |
| 1664 | */ |
| 1665 | int preferred_diff_type(void){ |
| 1666 | int dflt; |
| 1667 | char zDflt[2]; |
| 1668 | dflt = db_get_int("preferred-diff-type",-99); |
| 1669 | if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2; |
| 1670 | zDflt[0] = dflt + '0'; |
| 1671 | zDflt[1] = 0; |
| 1672 | cookie_link_parameter("diff","diff", zDflt); |
| 1673 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1662,11 +1662,12 @@ | |
| 1662 | ** * The "preferred-diff-type" setting |
| 1663 | ** * 1 for mobile and 2 for desktop, based on the UserAgent |
| 1664 | */ |
| 1665 | int preferred_diff_type(void){ |
| 1666 | int dflt; |
| 1667 | static char zDflt[2] |
| 1668 | /*static b/c cookie_link_parameter() does not copy it!*/; |
| 1669 | dflt = db_get_int("preferred-diff-type",-99); |
| 1670 | if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2; |
| 1671 | zDflt[0] = dflt + '0'; |
| 1672 | zDflt[1] = 0; |
| 1673 | cookie_link_parameter("diff","diff", zDflt); |
| 1674 |
+4
| --- src/json_detail.h | ||
| +++ src/json_detail.h | ||
| @@ -16,11 +16,13 @@ | ||
| 16 | 16 | ** [email protected] |
| 17 | 17 | ** http://www.hwaci.com/drh/ |
| 18 | 18 | ** |
| 19 | 19 | */ |
| 20 | 20 | |
| 21 | +#if !defined(_RC_COMPILE_) | |
| 21 | 22 | #include "cson_amalgamation.h" |
| 23 | +#endif /* !defined(_RC_COMPILE_) */ | |
| 22 | 24 | |
| 23 | 25 | /** |
| 24 | 26 | FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest |
| 25 | 27 | "significant" change to the JSON API (a change in an interface or |
| 26 | 28 | new functionality). It is sent as part of the /json/version |
| @@ -53,10 +55,11 @@ | ||
| 53 | 55 | ** Maintenance reminder: when entries are added to this list, update |
| 54 | 56 | ** the code in json_page_resultCodes() and json_err_cstr() (both in |
| 55 | 57 | ** json.c)! |
| 56 | 58 | ** |
| 57 | 59 | */ |
| 60 | +#if !defined(_RC_COMPILE_) | |
| 58 | 61 | enum FossilJsonCodes { |
| 59 | 62 | FSL_JSON_W_START = 0, |
| 60 | 63 | FSL_JSON_W_UNKNOWN /*+1*/, |
| 61 | 64 | FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, |
| 62 | 65 | FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, |
| @@ -263,7 +266,8 @@ | ||
| 263 | 266 | |
| 264 | 267 | enum json_get_changed_files_flags { |
| 265 | 268 | json_get_changed_files_ELIDE_PARENT = 1 << 0 |
| 266 | 269 | }; |
| 267 | 270 | |
| 271 | +#endif /* !defined(_RC_COMPILE_) */ | |
| 268 | 272 | #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/ |
| 269 | 273 | #endif /* FOSSIL_ENABLE_JSON */ |
| 270 | 274 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -16,11 +16,13 @@ | |
| 16 | ** [email protected] |
| 17 | ** http://www.hwaci.com/drh/ |
| 18 | ** |
| 19 | */ |
| 20 | |
| 21 | #include "cson_amalgamation.h" |
| 22 | |
| 23 | /** |
| 24 | FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest |
| 25 | "significant" change to the JSON API (a change in an interface or |
| 26 | new functionality). It is sent as part of the /json/version |
| @@ -53,10 +55,11 @@ | |
| 53 | ** Maintenance reminder: when entries are added to this list, update |
| 54 | ** the code in json_page_resultCodes() and json_err_cstr() (both in |
| 55 | ** json.c)! |
| 56 | ** |
| 57 | */ |
| 58 | enum FossilJsonCodes { |
| 59 | FSL_JSON_W_START = 0, |
| 60 | FSL_JSON_W_UNKNOWN /*+1*/, |
| 61 | FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, |
| 62 | FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, |
| @@ -263,7 +266,8 @@ | |
| 263 | |
| 264 | enum json_get_changed_files_flags { |
| 265 | json_get_changed_files_ELIDE_PARENT = 1 << 0 |
| 266 | }; |
| 267 | |
| 268 | #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/ |
| 269 | #endif /* FOSSIL_ENABLE_JSON */ |
| 270 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -16,11 +16,13 @@ | |
| 16 | ** [email protected] |
| 17 | ** http://www.hwaci.com/drh/ |
| 18 | ** |
| 19 | */ |
| 20 | |
| 21 | #if !defined(_RC_COMPILE_) |
| 22 | #include "cson_amalgamation.h" |
| 23 | #endif /* !defined(_RC_COMPILE_) */ |
| 24 | |
| 25 | /** |
| 26 | FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest |
| 27 | "significant" change to the JSON API (a change in an interface or |
| 28 | new functionality). It is sent as part of the /json/version |
| @@ -53,10 +55,11 @@ | |
| 55 | ** Maintenance reminder: when entries are added to this list, update |
| 56 | ** the code in json_page_resultCodes() and json_err_cstr() (both in |
| 57 | ** json.c)! |
| 58 | ** |
| 59 | */ |
| 60 | #if !defined(_RC_COMPILE_) |
| 61 | enum FossilJsonCodes { |
| 62 | FSL_JSON_W_START = 0, |
| 63 | FSL_JSON_W_UNKNOWN /*+1*/, |
| 64 | FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, |
| 65 | FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, |
| @@ -263,7 +266,8 @@ | |
| 266 | |
| 267 | enum json_get_changed_files_flags { |
| 268 | json_get_changed_files_ELIDE_PARENT = 1 << 0 |
| 269 | }; |
| 270 | |
| 271 | #endif /* !defined(_RC_COMPILE_) */ |
| 272 | #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/ |
| 273 | #endif /* FOSSIL_ENABLE_JSON */ |
| 274 |
+8
-1
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -349,11 +349,11 @@ | ||
| 349 | 349 | /* |
| 350 | 350 | ** Zero, unlock, and free the saved database encryption key now. |
| 351 | 351 | */ |
| 352 | 352 | db_unsave_encryption_key(); |
| 353 | 353 | #endif |
| 354 | -#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) | |
| 354 | +#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) | |
| 355 | 355 | /* |
| 356 | 356 | ** Free the secure getpass() buffer now. |
| 357 | 357 | */ |
| 358 | 358 | freepass(); |
| 359 | 359 | #endif |
| @@ -388,10 +388,17 @@ | ||
| 388 | 388 | */ |
| 389 | 389 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 390 | 390 | if( g.interp ){ |
| 391 | 391 | Th_DeleteInterp(g.interp); g.interp = 0; |
| 392 | 392 | } |
| 393 | +#if defined(TH_MEMDEBUG) | |
| 394 | + if( Th_GetOutstandingMalloc()!=0 ){ | |
| 395 | + fossil_print("Th_GetOutstandingMalloc() => %d\n", | |
| 396 | + Th_GetOutstandingMalloc()); | |
| 397 | + } | |
| 398 | + assert( Th_GetOutstandingMalloc()==0 ); | |
| 399 | +#endif | |
| 393 | 400 | } |
| 394 | 401 | } |
| 395 | 402 | |
| 396 | 403 | /* |
| 397 | 404 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 398 | 405 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -349,11 +349,11 @@ | |
| 349 | /* |
| 350 | ** Zero, unlock, and free the saved database encryption key now. |
| 351 | */ |
| 352 | db_unsave_encryption_key(); |
| 353 | #endif |
| 354 | #if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) |
| 355 | /* |
| 356 | ** Free the secure getpass() buffer now. |
| 357 | */ |
| 358 | freepass(); |
| 359 | #endif |
| @@ -388,10 +388,17 @@ | |
| 388 | */ |
| 389 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 390 | if( g.interp ){ |
| 391 | Th_DeleteInterp(g.interp); g.interp = 0; |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | /* |
| 397 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 398 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -349,11 +349,11 @@ | |
| 349 | /* |
| 350 | ** Zero, unlock, and free the saved database encryption key now. |
| 351 | */ |
| 352 | db_unsave_encryption_key(); |
| 353 | #endif |
| 354 | #if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) |
| 355 | /* |
| 356 | ** Free the secure getpass() buffer now. |
| 357 | */ |
| 358 | freepass(); |
| 359 | #endif |
| @@ -388,10 +388,17 @@ | |
| 388 | */ |
| 389 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 390 | if( g.interp ){ |
| 391 | Th_DeleteInterp(g.interp); g.interp = 0; |
| 392 | } |
| 393 | #if defined(TH_MEMDEBUG) |
| 394 | if( Th_GetOutstandingMalloc()!=0 ){ |
| 395 | fossil_print("Th_GetOutstandingMalloc() => %d\n", |
| 396 | Th_GetOutstandingMalloc()); |
| 397 | } |
| 398 | assert( Th_GetOutstandingMalloc()==0 ); |
| 399 | #endif |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | /* |
| 404 | ** Convert all arguments from mbcs (or unicode) to UTF-8. Then |
| 405 |
-12
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -154,11 +154,10 @@ | ||
| 154 | 154 | $(SRCDIR)/user.c \ |
| 155 | 155 | $(SRCDIR)/utf8.c \ |
| 156 | 156 | $(SRCDIR)/util.c \ |
| 157 | 157 | $(SRCDIR)/verify.c \ |
| 158 | 158 | $(SRCDIR)/vfile.c \ |
| 159 | - $(SRCDIR)/webmail.c \ | |
| 160 | 159 | $(SRCDIR)/wiki.c \ |
| 161 | 160 | $(SRCDIR)/wikiformat.c \ |
| 162 | 161 | $(SRCDIR)/winfile.c \ |
| 163 | 162 | $(SRCDIR)/winhttp.c \ |
| 164 | 163 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | ||
| 411 | 410 | $(OBJDIR)/user_.c \ |
| 412 | 411 | $(OBJDIR)/utf8_.c \ |
| 413 | 412 | $(OBJDIR)/util_.c \ |
| 414 | 413 | $(OBJDIR)/verify_.c \ |
| 415 | 414 | $(OBJDIR)/vfile_.c \ |
| 416 | - $(OBJDIR)/webmail_.c \ | |
| 417 | 415 | $(OBJDIR)/wiki_.c \ |
| 418 | 416 | $(OBJDIR)/wikiformat_.c \ |
| 419 | 417 | $(OBJDIR)/winfile_.c \ |
| 420 | 418 | $(OBJDIR)/winhttp_.c \ |
| 421 | 419 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | ||
| 561 | 559 | $(OBJDIR)/user.o \ |
| 562 | 560 | $(OBJDIR)/utf8.o \ |
| 563 | 561 | $(OBJDIR)/util.o \ |
| 564 | 562 | $(OBJDIR)/verify.o \ |
| 565 | 563 | $(OBJDIR)/vfile.o \ |
| 566 | - $(OBJDIR)/webmail.o \ | |
| 567 | 564 | $(OBJDIR)/wiki.o \ |
| 568 | 565 | $(OBJDIR)/wikiformat.o \ |
| 569 | 566 | $(OBJDIR)/winfile.o \ |
| 570 | 567 | $(OBJDIR)/winhttp.o \ |
| 571 | 568 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | ||
| 901 | 898 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 902 | 899 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 903 | 900 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 904 | 901 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 905 | 902 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 906 | - $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ | |
| 907 | 903 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 908 | 904 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 909 | 905 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 910 | 906 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 911 | 907 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | ||
| 2036 | 2032 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2037 | 2033 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2038 | 2034 | |
| 2039 | 2035 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2040 | 2036 | |
| 2041 | -$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate | |
| 2042 | - $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@ | |
| 2043 | - | |
| 2044 | -$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h | |
| 2045 | - $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c | |
| 2046 | - | |
| 2047 | -$(OBJDIR)/webmail.h: $(OBJDIR)/headers | |
| 2048 | - | |
| 2049 | 2037 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2050 | 2038 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2051 | 2039 | |
| 2052 | 2040 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2053 | 2041 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2054 | 2042 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/user.c \ |
| 155 | $(SRCDIR)/utf8.c \ |
| 156 | $(SRCDIR)/util.c \ |
| 157 | $(SRCDIR)/verify.c \ |
| 158 | $(SRCDIR)/vfile.c \ |
| 159 | $(SRCDIR)/webmail.c \ |
| 160 | $(SRCDIR)/wiki.c \ |
| 161 | $(SRCDIR)/wikiformat.c \ |
| 162 | $(SRCDIR)/winfile.c \ |
| 163 | $(SRCDIR)/winhttp.c \ |
| 164 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | |
| 411 | $(OBJDIR)/user_.c \ |
| 412 | $(OBJDIR)/utf8_.c \ |
| 413 | $(OBJDIR)/util_.c \ |
| 414 | $(OBJDIR)/verify_.c \ |
| 415 | $(OBJDIR)/vfile_.c \ |
| 416 | $(OBJDIR)/webmail_.c \ |
| 417 | $(OBJDIR)/wiki_.c \ |
| 418 | $(OBJDIR)/wikiformat_.c \ |
| 419 | $(OBJDIR)/winfile_.c \ |
| 420 | $(OBJDIR)/winhttp_.c \ |
| 421 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | |
| 561 | $(OBJDIR)/user.o \ |
| 562 | $(OBJDIR)/utf8.o \ |
| 563 | $(OBJDIR)/util.o \ |
| 564 | $(OBJDIR)/verify.o \ |
| 565 | $(OBJDIR)/vfile.o \ |
| 566 | $(OBJDIR)/webmail.o \ |
| 567 | $(OBJDIR)/wiki.o \ |
| 568 | $(OBJDIR)/wikiformat.o \ |
| 569 | $(OBJDIR)/winfile.o \ |
| 570 | $(OBJDIR)/winhttp.o \ |
| 571 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | |
| 901 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 902 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 903 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 904 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 905 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 906 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 907 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 908 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 909 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 910 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 911 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | |
| 2036 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2037 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2038 | |
| 2039 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2040 | |
| 2041 | $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate |
| 2042 | $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@ |
| 2043 | |
| 2044 | $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h |
| 2045 | $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c |
| 2046 | |
| 2047 | $(OBJDIR)/webmail.h: $(OBJDIR)/headers |
| 2048 | |
| 2049 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2050 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2051 | |
| 2052 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2053 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2054 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/user.c \ |
| 155 | $(SRCDIR)/utf8.c \ |
| 156 | $(SRCDIR)/util.c \ |
| 157 | $(SRCDIR)/verify.c \ |
| 158 | $(SRCDIR)/vfile.c \ |
| 159 | $(SRCDIR)/wiki.c \ |
| 160 | $(SRCDIR)/wikiformat.c \ |
| 161 | $(SRCDIR)/winfile.c \ |
| 162 | $(SRCDIR)/winhttp.c \ |
| 163 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | |
| 410 | $(OBJDIR)/user_.c \ |
| 411 | $(OBJDIR)/utf8_.c \ |
| 412 | $(OBJDIR)/util_.c \ |
| 413 | $(OBJDIR)/verify_.c \ |
| 414 | $(OBJDIR)/vfile_.c \ |
| 415 | $(OBJDIR)/wiki_.c \ |
| 416 | $(OBJDIR)/wikiformat_.c \ |
| 417 | $(OBJDIR)/winfile_.c \ |
| 418 | $(OBJDIR)/winhttp_.c \ |
| 419 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | |
| 559 | $(OBJDIR)/user.o \ |
| 560 | $(OBJDIR)/utf8.o \ |
| 561 | $(OBJDIR)/util.o \ |
| 562 | $(OBJDIR)/verify.o \ |
| 563 | $(OBJDIR)/vfile.o \ |
| 564 | $(OBJDIR)/wiki.o \ |
| 565 | $(OBJDIR)/wikiformat.o \ |
| 566 | $(OBJDIR)/winfile.o \ |
| 567 | $(OBJDIR)/winhttp.o \ |
| 568 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | |
| 898 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 899 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 900 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 901 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 902 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 903 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 904 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 905 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 906 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 907 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | |
| 2032 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2033 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2034 | |
| 2035 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2036 | |
| 2037 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2038 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2039 | |
| 2040 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2041 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2042 |
-12
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -154,11 +154,10 @@ | ||
| 154 | 154 | $(SRCDIR)/user.c \ |
| 155 | 155 | $(SRCDIR)/utf8.c \ |
| 156 | 156 | $(SRCDIR)/util.c \ |
| 157 | 157 | $(SRCDIR)/verify.c \ |
| 158 | 158 | $(SRCDIR)/vfile.c \ |
| 159 | - $(SRCDIR)/webmail.c \ | |
| 160 | 159 | $(SRCDIR)/wiki.c \ |
| 161 | 160 | $(SRCDIR)/wikiformat.c \ |
| 162 | 161 | $(SRCDIR)/winfile.c \ |
| 163 | 162 | $(SRCDIR)/winhttp.c \ |
| 164 | 163 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | ||
| 411 | 410 | $(OBJDIR)/user_.c \ |
| 412 | 411 | $(OBJDIR)/utf8_.c \ |
| 413 | 412 | $(OBJDIR)/util_.c \ |
| 414 | 413 | $(OBJDIR)/verify_.c \ |
| 415 | 414 | $(OBJDIR)/vfile_.c \ |
| 416 | - $(OBJDIR)/webmail_.c \ | |
| 417 | 415 | $(OBJDIR)/wiki_.c \ |
| 418 | 416 | $(OBJDIR)/wikiformat_.c \ |
| 419 | 417 | $(OBJDIR)/winfile_.c \ |
| 420 | 418 | $(OBJDIR)/winhttp_.c \ |
| 421 | 419 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | ||
| 561 | 559 | $(OBJDIR)/user.o \ |
| 562 | 560 | $(OBJDIR)/utf8.o \ |
| 563 | 561 | $(OBJDIR)/util.o \ |
| 564 | 562 | $(OBJDIR)/verify.o \ |
| 565 | 563 | $(OBJDIR)/vfile.o \ |
| 566 | - $(OBJDIR)/webmail.o \ | |
| 567 | 564 | $(OBJDIR)/wiki.o \ |
| 568 | 565 | $(OBJDIR)/wikiformat.o \ |
| 569 | 566 | $(OBJDIR)/winfile.o \ |
| 570 | 567 | $(OBJDIR)/winhttp.o \ |
| 571 | 568 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | ||
| 901 | 898 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 902 | 899 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 903 | 900 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 904 | 901 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 905 | 902 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 906 | - $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ | |
| 907 | 903 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 908 | 904 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 909 | 905 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 910 | 906 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 911 | 907 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | ||
| 2036 | 2032 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2037 | 2033 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2038 | 2034 | |
| 2039 | 2035 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2040 | 2036 | |
| 2041 | -$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate | |
| 2042 | - $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@ | |
| 2043 | - | |
| 2044 | -$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h | |
| 2045 | - $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c | |
| 2046 | - | |
| 2047 | -$(OBJDIR)/webmail.h: $(OBJDIR)/headers | |
| 2048 | - | |
| 2049 | 2037 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2050 | 2038 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2051 | 2039 | |
| 2052 | 2040 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2053 | 2041 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2054 | 2042 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/user.c \ |
| 155 | $(SRCDIR)/utf8.c \ |
| 156 | $(SRCDIR)/util.c \ |
| 157 | $(SRCDIR)/verify.c \ |
| 158 | $(SRCDIR)/vfile.c \ |
| 159 | $(SRCDIR)/webmail.c \ |
| 160 | $(SRCDIR)/wiki.c \ |
| 161 | $(SRCDIR)/wikiformat.c \ |
| 162 | $(SRCDIR)/winfile.c \ |
| 163 | $(SRCDIR)/winhttp.c \ |
| 164 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | |
| 411 | $(OBJDIR)/user_.c \ |
| 412 | $(OBJDIR)/utf8_.c \ |
| 413 | $(OBJDIR)/util_.c \ |
| 414 | $(OBJDIR)/verify_.c \ |
| 415 | $(OBJDIR)/vfile_.c \ |
| 416 | $(OBJDIR)/webmail_.c \ |
| 417 | $(OBJDIR)/wiki_.c \ |
| 418 | $(OBJDIR)/wikiformat_.c \ |
| 419 | $(OBJDIR)/winfile_.c \ |
| 420 | $(OBJDIR)/winhttp_.c \ |
| 421 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | |
| 561 | $(OBJDIR)/user.o \ |
| 562 | $(OBJDIR)/utf8.o \ |
| 563 | $(OBJDIR)/util.o \ |
| 564 | $(OBJDIR)/verify.o \ |
| 565 | $(OBJDIR)/vfile.o \ |
| 566 | $(OBJDIR)/webmail.o \ |
| 567 | $(OBJDIR)/wiki.o \ |
| 568 | $(OBJDIR)/wikiformat.o \ |
| 569 | $(OBJDIR)/winfile.o \ |
| 570 | $(OBJDIR)/winhttp.o \ |
| 571 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | |
| 901 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 902 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 903 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 904 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 905 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 906 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 907 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 908 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 909 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 910 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 911 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | |
| 2036 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2037 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2038 | |
| 2039 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2040 | |
| 2041 | $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate |
| 2042 | $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@ |
| 2043 | |
| 2044 | $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h |
| 2045 | $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c |
| 2046 | |
| 2047 | $(OBJDIR)/webmail.h: $(OBJDIR)/headers |
| 2048 | |
| 2049 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2050 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2051 | |
| 2052 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2053 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2054 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/user.c \ |
| 155 | $(SRCDIR)/utf8.c \ |
| 156 | $(SRCDIR)/util.c \ |
| 157 | $(SRCDIR)/verify.c \ |
| 158 | $(SRCDIR)/vfile.c \ |
| 159 | $(SRCDIR)/wiki.c \ |
| 160 | $(SRCDIR)/wikiformat.c \ |
| 161 | $(SRCDIR)/winfile.c \ |
| 162 | $(SRCDIR)/winhttp.c \ |
| 163 | $(SRCDIR)/xfer.c \ |
| @@ -411,11 +410,10 @@ | |
| 410 | $(OBJDIR)/user_.c \ |
| 411 | $(OBJDIR)/utf8_.c \ |
| 412 | $(OBJDIR)/util_.c \ |
| 413 | $(OBJDIR)/verify_.c \ |
| 414 | $(OBJDIR)/vfile_.c \ |
| 415 | $(OBJDIR)/wiki_.c \ |
| 416 | $(OBJDIR)/wikiformat_.c \ |
| 417 | $(OBJDIR)/winfile_.c \ |
| 418 | $(OBJDIR)/winhttp_.c \ |
| 419 | $(OBJDIR)/xfer_.c \ |
| @@ -561,11 +559,10 @@ | |
| 559 | $(OBJDIR)/user.o \ |
| 560 | $(OBJDIR)/utf8.o \ |
| 561 | $(OBJDIR)/util.o \ |
| 562 | $(OBJDIR)/verify.o \ |
| 563 | $(OBJDIR)/vfile.o \ |
| 564 | $(OBJDIR)/wiki.o \ |
| 565 | $(OBJDIR)/wikiformat.o \ |
| 566 | $(OBJDIR)/winfile.o \ |
| 567 | $(OBJDIR)/winhttp.o \ |
| 568 | $(OBJDIR)/xfer.o \ |
| @@ -901,11 +898,10 @@ | |
| 898 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 899 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 900 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 901 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 902 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 903 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 904 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 905 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 906 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 907 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2036,18 +2032,10 @@ | |
| 2032 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2033 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2034 | |
| 2035 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2036 | |
| 2037 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate |
| 2038 | $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ |
| 2039 | |
| 2040 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2041 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2042 |
+38
-11
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1,23 +1,34 @@ | ||
| 1 | 1 | #!/usr/bin/tclsh |
| 2 | 2 | # |
| 3 | -# Run this Tcl script to generate the various makefiles for a variety | |
| 4 | -# of platforms. Files generated include: | |
| 3 | +# ### Run this Tcl script EVERY time you modify it in any way! ### | |
| 4 | +# | |
| 5 | +# This Tcl script generates make files for various platforms. The makefiles | |
| 6 | +# then need to be committed. | |
| 7 | +# | |
| 8 | +# If you modify this file then: | |
| 9 | +# | |
| 10 | +# 1. cd src; tclsh makemake.tcl | |
| 11 | +# | |
| 12 | +# 2. if errors are reported, fix them and go to step 1 | |
| 13 | +# | |
| 14 | +# 3. if "fossil diff" reports changes in any of the generated | |
| 15 | +# files, commit the changed files to the repo | |
| 16 | +# | |
| 17 | +# Files generated include: | |
| 5 | 18 | # |
| 6 | 19 | # src/main.mk # makefile for all unix systems |
| 7 | 20 | # win/Makefile.mingw # makefile for mingw on windows |
| 8 | 21 | # win/Makefile.* # makefiles for other windows compilers |
| 9 | 22 | # |
| 10 | -# Run this script while in the "src" subdirectory. Like this: | |
| 11 | -# | |
| 12 | -# tclsh makemake.tcl | |
| 13 | -# | |
| 14 | 23 | # Add new source files by listing the files (without their .c suffix) |
| 15 | 24 | # in the "src" variable. Add new resource files to the "extra_files" |
| 16 | 25 | # variable. There are other variables that you can alter, down to |
| 17 | 26 | # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need |
| 18 | -# to change. | |
| 27 | +# to change. After modification, go to step 1 above. | |
| 28 | +# | |
| 29 | +# Delete unused source files in the "src" variable, then go to step 1 above. | |
| 19 | 30 | # |
| 20 | 31 | ############################################################################# |
| 21 | 32 | |
| 22 | 33 | # Basenames of all source files that get preprocessed using |
| 23 | 34 | # "translate" and "makeheaders". To add new C-language source files to the |
| @@ -164,11 +175,10 @@ | ||
| 164 | 175 | user |
| 165 | 176 | utf8 |
| 166 | 177 | util |
| 167 | 178 | verify |
| 168 | 179 | vfile |
| 169 | - webmail | |
| 170 | 180 | wiki |
| 171 | 181 | wikiformat |
| 172 | 182 | winfile |
| 173 | 183 | winhttp |
| 174 | 184 | xfer |
| @@ -1468,11 +1478,11 @@ | ||
| 1468 | 1478 | OBJDIR = $(T) |
| 1469 | 1479 | OX = $(OBJDIR) |
| 1470 | 1480 | O = .obj |
| 1471 | 1481 | E = .exe |
| 1472 | 1482 | P = .pdb |
| 1473 | -OPTLEVEL= /Os | |
| 1483 | +DBGOPTS = /Od | |
| 1474 | 1484 | |
| 1475 | 1485 | INSTALLDIR = . |
| 1476 | 1486 | !ifdef DESTDIR |
| 1477 | 1487 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 1478 | 1488 | !endif |
| @@ -1491,10 +1501,15 @@ | ||
| 1491 | 1501 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 1492 | 1502 | # source code. The PERLDIR environment variable, if it exists, should point |
| 1493 | 1503 | # to the directory containing the main Perl executable specified here (i.e. |
| 1494 | 1504 | # "perl.exe"). |
| 1495 | 1505 | PERL = perl.exe |
| 1506 | + | |
| 1507 | +# Enable use of available compiler optimizations? | |
| 1508 | +!ifndef OPTIMIZATIONS | |
| 1509 | +OPTIMIZATIONS = 2 | |
| 1510 | +!endif | |
| 1496 | 1511 | |
| 1497 | 1512 | # Enable debugging symbols? |
| 1498 | 1513 | !ifndef DEBUG |
| 1499 | 1514 | DEBUG = 0 |
| 1500 | 1515 | !endif |
| @@ -1663,16 +1678,28 @@ | ||
| 1663 | 1678 | CRTFLAGS = /MTd |
| 1664 | 1679 | !else |
| 1665 | 1680 | CRTFLAGS = /MT |
| 1666 | 1681 | !endif |
| 1667 | 1682 | !endif |
| 1683 | + | |
| 1684 | +!if $(OPTIMIZATIONS)>3 | |
| 1685 | +RELOPTS = /Os | |
| 1686 | +!elseif $(OPTIMIZATIONS)>2 | |
| 1687 | +RELOPTS = /Ox | |
| 1688 | +!elseif $(OPTIMIZATIONS)>1 | |
| 1689 | +RELOPTS = /O2 | |
| 1690 | +!elseif $(OPTIMIZATIONS)>0 | |
| 1691 | +RELOPTS = /O1 | |
| 1692 | +!else | |
| 1693 | +RELOPTS = | |
| 1694 | +!endif | |
| 1668 | 1695 | |
| 1669 | 1696 | !if $(DEBUG)!=0 |
| 1670 | -CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG | |
| 1697 | +CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG | |
| 1671 | 1698 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1672 | 1699 | !else |
| 1673 | -CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) | |
| 1700 | +CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) | |
| 1674 | 1701 | !endif |
| 1675 | 1702 | |
| 1676 | 1703 | BCC = $(CC) $(CFLAGS) |
| 1677 | 1704 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1678 | 1705 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| 1679 | 1706 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1,23 +1,34 @@ | |
| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # Run this Tcl script to generate the various makefiles for a variety |
| 4 | # of platforms. Files generated include: |
| 5 | # |
| 6 | # src/main.mk # makefile for all unix systems |
| 7 | # win/Makefile.mingw # makefile for mingw on windows |
| 8 | # win/Makefile.* # makefiles for other windows compilers |
| 9 | # |
| 10 | # Run this script while in the "src" subdirectory. Like this: |
| 11 | # |
| 12 | # tclsh makemake.tcl |
| 13 | # |
| 14 | # Add new source files by listing the files (without their .c suffix) |
| 15 | # in the "src" variable. Add new resource files to the "extra_files" |
| 16 | # variable. There are other variables that you can alter, down to |
| 17 | # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need |
| 18 | # to change. |
| 19 | # |
| 20 | ############################################################################# |
| 21 | |
| 22 | # Basenames of all source files that get preprocessed using |
| 23 | # "translate" and "makeheaders". To add new C-language source files to the |
| @@ -164,11 +175,10 @@ | |
| 164 | user |
| 165 | utf8 |
| 166 | util |
| 167 | verify |
| 168 | vfile |
| 169 | webmail |
| 170 | wiki |
| 171 | wikiformat |
| 172 | winfile |
| 173 | winhttp |
| 174 | xfer |
| @@ -1468,11 +1478,11 @@ | |
| 1468 | OBJDIR = $(T) |
| 1469 | OX = $(OBJDIR) |
| 1470 | O = .obj |
| 1471 | E = .exe |
| 1472 | P = .pdb |
| 1473 | OPTLEVEL= /Os |
| 1474 | |
| 1475 | INSTALLDIR = . |
| 1476 | !ifdef DESTDIR |
| 1477 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 1478 | !endif |
| @@ -1491,10 +1501,15 @@ | |
| 1491 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 1492 | # source code. The PERLDIR environment variable, if it exists, should point |
| 1493 | # to the directory containing the main Perl executable specified here (i.e. |
| 1494 | # "perl.exe"). |
| 1495 | PERL = perl.exe |
| 1496 | |
| 1497 | # Enable debugging symbols? |
| 1498 | !ifndef DEBUG |
| 1499 | DEBUG = 0 |
| 1500 | !endif |
| @@ -1663,16 +1678,28 @@ | |
| 1663 | CRTFLAGS = /MTd |
| 1664 | !else |
| 1665 | CRTFLAGS = /MT |
| 1666 | !endif |
| 1667 | !endif |
| 1668 | |
| 1669 | !if $(DEBUG)!=0 |
| 1670 | CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG |
| 1671 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1672 | !else |
| 1673 | CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) |
| 1674 | !endif |
| 1675 | |
| 1676 | BCC = $(CC) $(CFLAGS) |
| 1677 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1678 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| 1679 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1,23 +1,34 @@ | |
| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # ### Run this Tcl script EVERY time you modify it in any way! ### |
| 4 | # |
| 5 | # This Tcl script generates make files for various platforms. The makefiles |
| 6 | # then need to be committed. |
| 7 | # |
| 8 | # If you modify this file then: |
| 9 | # |
| 10 | # 1. cd src; tclsh makemake.tcl |
| 11 | # |
| 12 | # 2. if errors are reported, fix them and go to step 1 |
| 13 | # |
| 14 | # 3. if "fossil diff" reports changes in any of the generated |
| 15 | # files, commit the changed files to the repo |
| 16 | # |
| 17 | # Files generated include: |
| 18 | # |
| 19 | # src/main.mk # makefile for all unix systems |
| 20 | # win/Makefile.mingw # makefile for mingw on windows |
| 21 | # win/Makefile.* # makefiles for other windows compilers |
| 22 | # |
| 23 | # Add new source files by listing the files (without their .c suffix) |
| 24 | # in the "src" variable. Add new resource files to the "extra_files" |
| 25 | # variable. There are other variables that you can alter, down to |
| 26 | # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need |
| 27 | # to change. After modification, go to step 1 above. |
| 28 | # |
| 29 | # Delete unused source files in the "src" variable, then go to step 1 above. |
| 30 | # |
| 31 | ############################################################################# |
| 32 | |
| 33 | # Basenames of all source files that get preprocessed using |
| 34 | # "translate" and "makeheaders". To add new C-language source files to the |
| @@ -164,11 +175,10 @@ | |
| 175 | user |
| 176 | utf8 |
| 177 | util |
| 178 | verify |
| 179 | vfile |
| 180 | wiki |
| 181 | wikiformat |
| 182 | winfile |
| 183 | winhttp |
| 184 | xfer |
| @@ -1468,11 +1478,11 @@ | |
| 1478 | OBJDIR = $(T) |
| 1479 | OX = $(OBJDIR) |
| 1480 | O = .obj |
| 1481 | E = .exe |
| 1482 | P = .pdb |
| 1483 | DBGOPTS = /Od |
| 1484 | |
| 1485 | INSTALLDIR = . |
| 1486 | !ifdef DESTDIR |
| 1487 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 1488 | !endif |
| @@ -1491,10 +1501,15 @@ | |
| 1501 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 1502 | # source code. The PERLDIR environment variable, if it exists, should point |
| 1503 | # to the directory containing the main Perl executable specified here (i.e. |
| 1504 | # "perl.exe"). |
| 1505 | PERL = perl.exe |
| 1506 | |
| 1507 | # Enable use of available compiler optimizations? |
| 1508 | !ifndef OPTIMIZATIONS |
| 1509 | OPTIMIZATIONS = 2 |
| 1510 | !endif |
| 1511 | |
| 1512 | # Enable debugging symbols? |
| 1513 | !ifndef DEBUG |
| 1514 | DEBUG = 0 |
| 1515 | !endif |
| @@ -1663,16 +1678,28 @@ | |
| 1678 | CRTFLAGS = /MTd |
| 1679 | !else |
| 1680 | CRTFLAGS = /MT |
| 1681 | !endif |
| 1682 | !endif |
| 1683 | |
| 1684 | !if $(OPTIMIZATIONS)>3 |
| 1685 | RELOPTS = /Os |
| 1686 | !elseif $(OPTIMIZATIONS)>2 |
| 1687 | RELOPTS = /Ox |
| 1688 | !elseif $(OPTIMIZATIONS)>1 |
| 1689 | RELOPTS = /O2 |
| 1690 | !elseif $(OPTIMIZATIONS)>0 |
| 1691 | RELOPTS = /O1 |
| 1692 | !else |
| 1693 | RELOPTS = |
| 1694 | !endif |
| 1695 | |
| 1696 | !if $(DEBUG)!=0 |
| 1697 | CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG |
| 1698 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 1699 | !else |
| 1700 | CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) |
| 1701 | !endif |
| 1702 | |
| 1703 | BCC = $(CC) $(CFLAGS) |
| 1704 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 1705 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| 1706 |
+2
-2
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -1100,11 +1100,11 @@ | ||
| 1100 | 1100 | /* |
| 1101 | 1101 | ** Avoid calling into the JSON support subsystem if it |
| 1102 | 1102 | ** has not yet been initialized, e.g. early SQLite log |
| 1103 | 1103 | ** messages, etc. |
| 1104 | 1104 | */ |
| 1105 | - assert(json_is_bootstrapped_early()); | |
| 1105 | + if( !json_is_bootstrapped_early() ) json_bootstrap_early(); | |
| 1106 | 1106 | json_err( 0, z, 1 ); |
| 1107 | 1107 | if( g.isHTTP && !g.json.preserveRc ){ |
| 1108 | 1108 | rc = 0 /* avoid HTTP 500 */; |
| 1109 | 1109 | } |
| 1110 | 1110 | if( g.cgiOutput==1 ){ |
| @@ -1230,11 +1230,11 @@ | ||
| 1230 | 1230 | /* |
| 1231 | 1231 | ** Avoid calling into the JSON support subsystem if it |
| 1232 | 1232 | ** has not yet been initialized, e.g. early SQLite log |
| 1233 | 1233 | ** messages, etc. |
| 1234 | 1234 | */ |
| 1235 | - assert(json_is_bootstrapped_early()); | |
| 1235 | + if( !json_is_bootstrapped_early() ) json_bootstrap_early(); | |
| 1236 | 1236 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1237 | 1237 | }else |
| 1238 | 1238 | #endif |
| 1239 | 1239 | { |
| 1240 | 1240 | if( g.cgiOutput==1 ){ |
| 1241 | 1241 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1100,11 +1100,11 @@ | |
| 1100 | /* |
| 1101 | ** Avoid calling into the JSON support subsystem if it |
| 1102 | ** has not yet been initialized, e.g. early SQLite log |
| 1103 | ** messages, etc. |
| 1104 | */ |
| 1105 | assert(json_is_bootstrapped_early()); |
| 1106 | json_err( 0, z, 1 ); |
| 1107 | if( g.isHTTP && !g.json.preserveRc ){ |
| 1108 | rc = 0 /* avoid HTTP 500 */; |
| 1109 | } |
| 1110 | if( g.cgiOutput==1 ){ |
| @@ -1230,11 +1230,11 @@ | |
| 1230 | /* |
| 1231 | ** Avoid calling into the JSON support subsystem if it |
| 1232 | ** has not yet been initialized, e.g. early SQLite log |
| 1233 | ** messages, etc. |
| 1234 | */ |
| 1235 | assert(json_is_bootstrapped_early()); |
| 1236 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1237 | }else |
| 1238 | #endif |
| 1239 | { |
| 1240 | if( g.cgiOutput==1 ){ |
| 1241 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1100,11 +1100,11 @@ | |
| 1100 | /* |
| 1101 | ** Avoid calling into the JSON support subsystem if it |
| 1102 | ** has not yet been initialized, e.g. early SQLite log |
| 1103 | ** messages, etc. |
| 1104 | */ |
| 1105 | if( !json_is_bootstrapped_early() ) json_bootstrap_early(); |
| 1106 | json_err( 0, z, 1 ); |
| 1107 | if( g.isHTTP && !g.json.preserveRc ){ |
| 1108 | rc = 0 /* avoid HTTP 500 */; |
| 1109 | } |
| 1110 | if( g.cgiOutput==1 ){ |
| @@ -1230,11 +1230,11 @@ | |
| 1230 | /* |
| 1231 | ** Avoid calling into the JSON support subsystem if it |
| 1232 | ** has not yet been initialized, e.g. early SQLite log |
| 1233 | ** messages, etc. |
| 1234 | */ |
| 1235 | if( !json_is_bootstrapped_early() ) json_bootstrap_early(); |
| 1236 | json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); |
| 1237 | }else |
| 1238 | #endif |
| 1239 | { |
| 1240 | if( g.cgiOutput==1 ){ |
| 1241 |
+103
-91
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -3662,12 +3662,12 @@ | ||
| 3662 | 3662 | ** set, then a new database is appended to the already existing file. |
| 3663 | 3663 | ** |
| 3664 | 3664 | ** (5) Otherwise, SQLITE_CANTOPEN is returned. |
| 3665 | 3665 | ** |
| 3666 | 3666 | ** To avoid unnecessary complications with the PENDING_BYTE, the size of |
| 3667 | -** the file containing the database is limited to 1GB. (1000013824 bytes) | |
| 3668 | -** This VFS will not read or write past the 1GB mark. This restriction | |
| 3667 | +** the file containing the database is limited to 1GiB. (1073741824 bytes) | |
| 3668 | +** This VFS will not read or write past the 1GiB mark. This restriction | |
| 3669 | 3669 | ** might be lifted in future versions. For now, if you need a larger |
| 3670 | 3670 | ** database, then keep it in a separate file. |
| 3671 | 3671 | ** |
| 3672 | 3672 | ** If the file being opened is a plain database (not an appended one), then |
| 3673 | 3673 | ** this shim is a pass-through into the default underlying VFS. (rule 3) |
| @@ -3692,18 +3692,20 @@ | ||
| 3692 | 3692 | |
| 3693 | 3693 | /* |
| 3694 | 3694 | ** Maximum size of the combined prefix + database + append-mark. This |
| 3695 | 3695 | ** must be less than 0x40000000 to avoid locking issues on Windows. |
| 3696 | 3696 | */ |
| 3697 | -#define APND_MAX_SIZE (65536*15259) | |
| 3697 | +#define APND_MAX_SIZE (0x40000000) | |
| 3698 | 3698 | |
| 3699 | 3699 | /* |
| 3700 | -** Size of storage page upon which to align appendvfs portion. | |
| 3700 | +** Try to align the database to an even multiple of APND_ROUNDUP bytes. | |
| 3701 | 3701 | */ |
| 3702 | -#ifndef APND_ROUNDUP_BITS | |
| 3703 | -#define APND_ROUNDUP_BITS 12 | |
| 3702 | +#ifndef APND_ROUNDUP | |
| 3703 | +#define APND_ROUNDUP 4096 | |
| 3704 | 3704 | #endif |
| 3705 | +#define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1)) | |
| 3706 | +#define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK) | |
| 3705 | 3707 | |
| 3706 | 3708 | /* |
| 3707 | 3709 | ** Forward declaration of objects used by this utility |
| 3708 | 3710 | */ |
| 3709 | 3711 | typedef struct sqlite3_vfs ApndVfs; |
| @@ -3713,43 +3715,49 @@ | ||
| 3713 | 3715 | ** access to randomness, etc. |
| 3714 | 3716 | */ |
| 3715 | 3717 | #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) |
| 3716 | 3718 | #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) |
| 3717 | 3719 | |
| 3718 | -/* Invariants for an open appendvfs file: | |
| 3719 | - * Once an appendvfs file is opened, it will be in one of three states: | |
| 3720 | - * State 0: Never written. Underlying file (if any) is unaltered. | |
| 3721 | - * State 1: Append mark is persisted, content write is in progress. | |
| 3722 | - * State 2: Append mark is persisted, content writes are complete. | |
| 3723 | - * | |
| 3724 | - * State 0 is persistent in the sense that nothing will have been done | |
| 3725 | - * to the underlying file, including any attempt to convert it to an | |
| 3726 | - * appendvfs file. | |
| 3727 | - * | |
| 3728 | - * State 1 is normally transitory. However, if a write operation ends | |
| 3729 | - * abnormally (disk full, power loss, process kill, etc.), then State 1 | |
| 3730 | - * may be persistent on disk with an incomplete content write-out. This | |
| 3731 | - * is logically equivalent to an interrupted write to an ordinary file, | |
| 3732 | - * where some unknown portion of to-be-written data is persisted while | |
| 3733 | - * the remainder is not. Database integrity in such cases is maintained | |
| 3734 | - * (or not) by the same measures available for ordinary file access. | |
| 3735 | - * | |
| 3736 | - * State 2 is persistent under normal circumstances (when there is no | |
| 3737 | - * abnormal termination of a write operation such that data provided | |
| 3738 | - * to the underlying VFS write method has not yet reached storage.) | |
| 3739 | - * | |
| 3740 | - * In order to maintain the state invariant, the append mark is written | |
| 3741 | - * in advance of content writes where any part of such content would | |
| 3742 | - * overwrite an existing (or yet to be written) append mark. | |
| 3743 | - */ | |
| 3720 | +/* An open appendvfs file | |
| 3721 | +** | |
| 3722 | +** An instance of this structure describes the appended database file. | |
| 3723 | +** A separate sqlite3_file object is always appended. The appended | |
| 3724 | +** sqlite3_file object (which can be accessed using ORIGFILE()) describes | |
| 3725 | +** the entire file, including the prefix, the database, and the | |
| 3726 | +** append-mark. | |
| 3727 | +** | |
| 3728 | +** The structure of an AppendVFS database is like this: | |
| 3729 | +** | |
| 3730 | +** +-------------+---------+----------+-------------+ | |
| 3731 | +** | prefix-file | padding | database | append-mark | | |
| 3732 | +** +-------------+---------+----------+-------------+ | |
| 3733 | +** ^ ^ | |
| 3734 | +** | | | |
| 3735 | +** iPgOne iMark | |
| 3736 | +** | |
| 3737 | +** | |
| 3738 | +** "prefix file" - file onto which the database has been appended. | |
| 3739 | +** "padding" - zero or more bytes inserted so that "database" | |
| 3740 | +** starts on an APND_ROUNDUP boundary | |
| 3741 | +** "database" - The SQLite database file | |
| 3742 | +** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates | |
| 3743 | +** the offset from the start of prefix-file to the start | |
| 3744 | +** of "database". | |
| 3745 | +** | |
| 3746 | +** The size of the database is iMark - iPgOne. | |
| 3747 | +** | |
| 3748 | +** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value | |
| 3749 | +** of iPgOne stored as a big-ending 64-bit integer. | |
| 3750 | +** | |
| 3751 | +** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE). | |
| 3752 | +** Or, iMark is -1 to indicate that it has not yet been written. | |
| 3753 | +*/ | |
| 3744 | 3754 | struct ApndFile { |
| 3745 | - /* Access to IO methods of the underlying file */ | |
| 3746 | - sqlite3_file base; | |
| 3747 | - /* File offset to beginning of appended content (unchanging) */ | |
| 3748 | - sqlite3_int64 iPgOne; | |
| 3749 | - /* File offset of written append-mark, or -1 if unwritten */ | |
| 3750 | - sqlite3_int64 iMark; | |
| 3755 | + sqlite3_file base; /* Subclass. MUST BE FIRST! */ | |
| 3756 | + sqlite3_int64 iPgOne; /* Offset to the start of the database */ | |
| 3757 | + sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */ | |
| 3758 | + /* Always followed by another sqlite3_file that describes the whole file */ | |
| 3751 | 3759 | }; |
| 3752 | 3760 | |
| 3753 | 3761 | /* |
| 3754 | 3762 | ** Methods for ApndFile |
| 3755 | 3763 | */ |
| @@ -3875,11 +3883,11 @@ | ||
| 3875 | 3883 | unsigned char a[APND_MARK_SIZE]; |
| 3876 | 3884 | int i = APND_MARK_FOS_SZ; |
| 3877 | 3885 | int rc; |
| 3878 | 3886 | assert(pFile == ORIGFILE(paf)); |
| 3879 | 3887 | memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); |
| 3880 | - while (--i >= 0) { | |
| 3888 | + while( --i >= 0 ){ | |
| 3881 | 3889 | a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff); |
| 3882 | 3890 | iPgOne >>= 8; |
| 3883 | 3891 | } |
| 3884 | 3892 | iWriteEnd += paf->iPgOne; |
| 3885 | 3893 | if( SQLITE_OK==(rc = pFile->pMethods->xWrite |
| @@ -3903,12 +3911,11 @@ | ||
| 3903 | 3911 | if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; |
| 3904 | 3912 | pFile = ORIGFILE(pFile); |
| 3905 | 3913 | /* If append-mark is absent or will be overwritten, write it. */ |
| 3906 | 3914 | if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ |
| 3907 | 3915 | int rc = apndWriteMark(paf, pFile, iWriteEnd); |
| 3908 | - if( SQLITE_OK!=rc ) | |
| 3909 | - return rc; | |
| 3916 | + if( SQLITE_OK!=rc ) return rc; | |
| 3910 | 3917 | } |
| 3911 | 3918 | return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst); |
| 3912 | 3919 | } |
| 3913 | 3920 | |
| 3914 | 3921 | /* |
| @@ -3916,12 +3923,11 @@ | ||
| 3916 | 3923 | */ |
| 3917 | 3924 | static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| 3918 | 3925 | ApndFile *paf = (ApndFile *)pFile; |
| 3919 | 3926 | pFile = ORIGFILE(pFile); |
| 3920 | 3927 | /* The append mark goes out first so truncate failure does not lose it. */ |
| 3921 | - if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) | |
| 3922 | - return SQLITE_IOERR; | |
| 3928 | + if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR; | |
| 3923 | 3929 | /* Truncate underlying file just past append mark */ |
| 3924 | 3930 | return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE); |
| 3925 | 3931 | } |
| 3926 | 3932 | |
| 3927 | 3933 | /* |
| @@ -4033,12 +4039,13 @@ | ||
| 4033 | 4039 | sqlite3_int64 iOfst, |
| 4034 | 4040 | int iAmt, |
| 4035 | 4041 | void **pp |
| 4036 | 4042 | ){ |
| 4037 | 4043 | ApndFile *p = (ApndFile *)pFile; |
| 4038 | - if( p->iMark < 0 || iOfst+iAmt > p->iMark) | |
| 4044 | + if( p->iMark < 0 || iOfst+iAmt > p->iMark ){ | |
| 4039 | 4045 | return SQLITE_IOERR; /* Cannot read what is not yet there. */ |
| 4046 | + } | |
| 4040 | 4047 | pFile = ORIGFILE(pFile); |
| 4041 | 4048 | return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); |
| 4042 | 4049 | } |
| 4043 | 4050 | |
| 4044 | 4051 | /* Release a memory-mapped page */ |
| @@ -4085,18 +4092,22 @@ | ||
| 4085 | 4092 | static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ |
| 4086 | 4093 | int rc; |
| 4087 | 4094 | char zHdr[16]; |
| 4088 | 4095 | sqlite3_int64 iMark = apndReadMark(sz, pFile); |
| 4089 | 4096 | if( iMark>=0 ){ |
| 4090 | - /* If file has right end-marker, the expected odd size, and the | |
| 4091 | - * SQLite DB type marker where the end-marker puts it, then it | |
| 4092 | - * is an appendvfs database (to be treated as such.) | |
| 4093 | - */ | |
| 4097 | + /* If file has the correct end-marker, the expected odd size, and the | |
| 4098 | + ** SQLite DB type marker where the end-marker puts it, then it | |
| 4099 | + ** is an appendvfs database. | |
| 4100 | + */ | |
| 4094 | 4101 | rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark); |
| 4095 | - if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 | |
| 4096 | - && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE ) | |
| 4102 | + if( SQLITE_OK==rc | |
| 4103 | + && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 | |
| 4104 | + && (sz & 0x1ff) == APND_MARK_SIZE | |
| 4105 | + && sz>=512+APND_MARK_SIZE | |
| 4106 | + ){ | |
| 4097 | 4107 | return 1; /* It's an appendvfs database */ |
| 4108 | + } | |
| 4098 | 4109 | } |
| 4099 | 4110 | return 0; |
| 4100 | 4111 | } |
| 4101 | 4112 | |
| 4102 | 4113 | /* |
| @@ -4114,70 +4125,69 @@ | ||
| 4114 | 4125 | }else{ |
| 4115 | 4126 | return 1; |
| 4116 | 4127 | } |
| 4117 | 4128 | } |
| 4118 | 4129 | |
| 4119 | -/* Round-up used to get appendvfs portion to begin at a page boundary. */ | |
| 4120 | -#define APND_ALIGN_MASK(nbits) ((1<<nbits)-1) | |
| 4121 | -#define APND_START_ROUNDUP(fsz, nbits) \ | |
| 4122 | - ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) ) | |
| 4123 | - | |
| 4124 | 4130 | /* |
| 4125 | 4131 | ** Open an apnd file handle. |
| 4126 | 4132 | */ |
| 4127 | 4133 | static int apndOpen( |
| 4128 | - sqlite3_vfs *pVfs, | |
| 4134 | + sqlite3_vfs *pApndVfs, | |
| 4129 | 4135 | const char *zName, |
| 4130 | 4136 | sqlite3_file *pFile, |
| 4131 | 4137 | int flags, |
| 4132 | 4138 | int *pOutFlags |
| 4133 | 4139 | ){ |
| 4134 | - ApndFile *p; | |
| 4135 | - sqlite3_file *pSubFile; | |
| 4136 | - sqlite3_vfs *pSubVfs; | |
| 4140 | + ApndFile *pApndFile = (ApndFile*)pFile; | |
| 4141 | + sqlite3_file *pBaseFile = ORIGFILE(pFile); | |
| 4142 | + sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs); | |
| 4137 | 4143 | int rc; |
| 4138 | - sqlite3_int64 sz; | |
| 4139 | - pSubVfs = ORIGVFS(pVfs); | |
| 4144 | + sqlite3_int64 sz = 0; | |
| 4140 | 4145 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ |
| 4141 | - /* The appendvfs is not to be used for transient or temporary databases. */ | |
| 4142 | - return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); | |
| 4146 | + /* The appendvfs is not to be used for transient or temporary databases. | |
| 4147 | + ** Just use the base VFS open to initialize the given file object and | |
| 4148 | + ** open the underlying file. (Appendvfs is then unused for this file.) | |
| 4149 | + */ | |
| 4150 | + return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags); | |
| 4143 | 4151 | } |
| 4144 | - p = (ApndFile*)pFile; | |
| 4145 | - memset(p, 0, sizeof(*p)); | |
| 4146 | - pSubFile = ORIGFILE(pFile); | |
| 4152 | + memset(pApndFile, 0, sizeof(ApndFile)); | |
| 4147 | 4153 | pFile->pMethods = &apnd_io_methods; |
| 4148 | - rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); | |
| 4149 | - if( rc ) goto apnd_open_done; | |
| 4150 | - rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); | |
| 4154 | + pApndFile->iMark = -1; /* Append mark not yet written */ | |
| 4155 | + | |
| 4156 | + rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags); | |
| 4157 | + if( rc==SQLITE_OK ){ | |
| 4158 | + rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz); | |
| 4159 | + } | |
| 4151 | 4160 | if( rc ){ |
| 4152 | - pSubFile->pMethods->xClose(pSubFile); | |
| 4153 | - goto apnd_open_done; | |
| 4161 | + pBaseFile->pMethods->xClose(pBaseFile); | |
| 4162 | + pFile->pMethods = 0; | |
| 4163 | + return rc; | |
| 4154 | 4164 | } |
| 4155 | - if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){ | |
| 4156 | - memmove(pFile, pSubFile, pSubVfs->szOsFile); | |
| 4165 | + if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){ | |
| 4166 | + /* The file being opened appears to be just an ordinary DB. Copy | |
| 4167 | + ** the base dispatch-table so this instance mimics the base VFS. | |
| 4168 | + */ | |
| 4169 | + memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile); | |
| 4157 | 4170 | return SQLITE_OK; |
| 4158 | 4171 | } |
| 4159 | - /* Record that append mark has not been written until seen otherwise. */ | |
| 4160 | - p->iMark = -1; | |
| 4161 | - p->iPgOne = apndReadMark(sz, pFile); | |
| 4162 | - if( p->iPgOne>=0 ){ | |
| 4163 | - /* Append mark was found, infer its offset */ | |
| 4164 | - p->iMark = sz - p->iPgOne - APND_MARK_SIZE; | |
| 4172 | + pApndFile->iPgOne = apndReadMark(sz, pFile); | |
| 4173 | + if( pApndFile->iPgOne>=0 ){ | |
| 4174 | + pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */ | |
| 4165 | 4175 | return SQLITE_OK; |
| 4166 | 4176 | } |
| 4167 | 4177 | if( (flags & SQLITE_OPEN_CREATE)==0 ){ |
| 4168 | - pSubFile->pMethods->xClose(pSubFile); | |
| 4178 | + pBaseFile->pMethods->xClose(pBaseFile); | |
| 4169 | 4179 | rc = SQLITE_CANTOPEN; |
| 4170 | - } | |
| 4171 | - /* Round newly added appendvfs location to #define'd page boundary. | |
| 4172 | - * Note that nothing has yet been written to the underlying file. | |
| 4173 | - * The append mark will be written along with first content write. | |
| 4174 | - * Until then, the p->iMark value indicates it is not yet written. | |
| 4175 | - */ | |
| 4176 | - p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS); | |
| 4177 | -apnd_open_done: | |
| 4178 | - if( rc ) pFile->pMethods = 0; | |
| 4180 | + pFile->pMethods = 0; | |
| 4181 | + }else{ | |
| 4182 | + /* Round newly added appendvfs location to #define'd page boundary. | |
| 4183 | + ** Note that nothing has yet been written to the underlying file. | |
| 4184 | + ** The append mark will be written along with first content write. | |
| 4185 | + ** Until then, paf->iMark value indicates it is not yet written. | |
| 4186 | + */ | |
| 4187 | + pApndFile->iPgOne = APND_START_ROUNDUP(sz); | |
| 4188 | + } | |
| 4179 | 4189 | return rc; |
| 4180 | 4190 | } |
| 4181 | 4191 | |
| 4182 | 4192 | /* |
| 4183 | 4193 | ** Delete an apnd file. |
| @@ -13207,10 +13217,11 @@ | ||
| 13207 | 13217 | |
| 13208 | 13218 | rc = sqlite3_step(pStmt); |
| 13209 | 13219 | if( rc!=SQLITE_ROW ) return; |
| 13210 | 13220 | nColumn = sqlite3_column_count(pStmt); |
| 13211 | 13221 | nAlloc = nColumn*4; |
| 13222 | + if( nAlloc<=0 ) nAlloc = 1; | |
| 13212 | 13223 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 13213 | 13224 | if( azData==0 ) shell_out_of_memory(); |
| 13214 | 13225 | for(i=0; i<nColumn; i++){ |
| 13215 | 13226 | azData[i] = strdup(sqlite3_column_name(pStmt,i)); |
| 13216 | 13227 | } |
| @@ -13246,10 +13257,11 @@ | ||
| 13246 | 13257 | n = strlenChar(z); |
| 13247 | 13258 | j = i%nColumn; |
| 13248 | 13259 | if( n>p->actualWidth[j] ) p->actualWidth[j] = n; |
| 13249 | 13260 | } |
| 13250 | 13261 | if( seenInterrupt ) goto columnar_end; |
| 13262 | + if( nColumn==0 ) goto columnar_end; | |
| 13251 | 13263 | switch( p->cMode ){ |
| 13252 | 13264 | case MODE_Column: { |
| 13253 | 13265 | colSep = " "; |
| 13254 | 13266 | rowSep = "\n"; |
| 13255 | 13267 | if( p->showHeader ){ |
| @@ -14035,17 +14047,17 @@ | ||
| 14035 | 14047 | ".check GLOB Fail if output since .testcase does not match", |
| 14036 | 14048 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 14037 | 14049 | ".databases List names and files of attached databases", |
| 14038 | 14050 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 14039 | 14051 | ".dbinfo ?DB? Show status information about the database", |
| 14040 | - ".dump ?TABLE? Render database content as SQL", | |
| 14052 | + ".dump ?OBJECTS? Render database content as SQL", | |
| 14041 | 14053 | " Options:", |
| 14042 | 14054 | " --data-only Output only INSERT statements", |
| 14043 | 14055 | " --newlines Allow unescaped newline characters in output", |
| 14044 | 14056 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| 14045 | 14057 | " --preserve-rowids Include ROWID values in the output", |
| 14046 | - " TABLE is a LIKE pattern for the tables to dump", | |
| 14058 | + " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", | |
| 14047 | 14059 | " Additional LIKE patterns can be given in subsequent arguments", |
| 14048 | 14060 | ".echo on|off Turn command echo on or off", |
| 14049 | 14061 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 14050 | 14062 | " Other Modes:", |
| 14051 | 14063 | #ifdef SQLITE_DEBUG |
| 14052 | 14064 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -3662,12 +3662,12 @@ | |
| 3662 | ** set, then a new database is appended to the already existing file. |
| 3663 | ** |
| 3664 | ** (5) Otherwise, SQLITE_CANTOPEN is returned. |
| 3665 | ** |
| 3666 | ** To avoid unnecessary complications with the PENDING_BYTE, the size of |
| 3667 | ** the file containing the database is limited to 1GB. (1000013824 bytes) |
| 3668 | ** This VFS will not read or write past the 1GB mark. This restriction |
| 3669 | ** might be lifted in future versions. For now, if you need a larger |
| 3670 | ** database, then keep it in a separate file. |
| 3671 | ** |
| 3672 | ** If the file being opened is a plain database (not an appended one), then |
| 3673 | ** this shim is a pass-through into the default underlying VFS. (rule 3) |
| @@ -3692,18 +3692,20 @@ | |
| 3692 | |
| 3693 | /* |
| 3694 | ** Maximum size of the combined prefix + database + append-mark. This |
| 3695 | ** must be less than 0x40000000 to avoid locking issues on Windows. |
| 3696 | */ |
| 3697 | #define APND_MAX_SIZE (65536*15259) |
| 3698 | |
| 3699 | /* |
| 3700 | ** Size of storage page upon which to align appendvfs portion. |
| 3701 | */ |
| 3702 | #ifndef APND_ROUNDUP_BITS |
| 3703 | #define APND_ROUNDUP_BITS 12 |
| 3704 | #endif |
| 3705 | |
| 3706 | /* |
| 3707 | ** Forward declaration of objects used by this utility |
| 3708 | */ |
| 3709 | typedef struct sqlite3_vfs ApndVfs; |
| @@ -3713,43 +3715,49 @@ | |
| 3713 | ** access to randomness, etc. |
| 3714 | */ |
| 3715 | #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) |
| 3716 | #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) |
| 3717 | |
| 3718 | /* Invariants for an open appendvfs file: |
| 3719 | * Once an appendvfs file is opened, it will be in one of three states: |
| 3720 | * State 0: Never written. Underlying file (if any) is unaltered. |
| 3721 | * State 1: Append mark is persisted, content write is in progress. |
| 3722 | * State 2: Append mark is persisted, content writes are complete. |
| 3723 | * |
| 3724 | * State 0 is persistent in the sense that nothing will have been done |
| 3725 | * to the underlying file, including any attempt to convert it to an |
| 3726 | * appendvfs file. |
| 3727 | * |
| 3728 | * State 1 is normally transitory. However, if a write operation ends |
| 3729 | * abnormally (disk full, power loss, process kill, etc.), then State 1 |
| 3730 | * may be persistent on disk with an incomplete content write-out. This |
| 3731 | * is logically equivalent to an interrupted write to an ordinary file, |
| 3732 | * where some unknown portion of to-be-written data is persisted while |
| 3733 | * the remainder is not. Database integrity in such cases is maintained |
| 3734 | * (or not) by the same measures available for ordinary file access. |
| 3735 | * |
| 3736 | * State 2 is persistent under normal circumstances (when there is no |
| 3737 | * abnormal termination of a write operation such that data provided |
| 3738 | * to the underlying VFS write method has not yet reached storage.) |
| 3739 | * |
| 3740 | * In order to maintain the state invariant, the append mark is written |
| 3741 | * in advance of content writes where any part of such content would |
| 3742 | * overwrite an existing (or yet to be written) append mark. |
| 3743 | */ |
| 3744 | struct ApndFile { |
| 3745 | /* Access to IO methods of the underlying file */ |
| 3746 | sqlite3_file base; |
| 3747 | /* File offset to beginning of appended content (unchanging) */ |
| 3748 | sqlite3_int64 iPgOne; |
| 3749 | /* File offset of written append-mark, or -1 if unwritten */ |
| 3750 | sqlite3_int64 iMark; |
| 3751 | }; |
| 3752 | |
| 3753 | /* |
| 3754 | ** Methods for ApndFile |
| 3755 | */ |
| @@ -3875,11 +3883,11 @@ | |
| 3875 | unsigned char a[APND_MARK_SIZE]; |
| 3876 | int i = APND_MARK_FOS_SZ; |
| 3877 | int rc; |
| 3878 | assert(pFile == ORIGFILE(paf)); |
| 3879 | memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); |
| 3880 | while (--i >= 0) { |
| 3881 | a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff); |
| 3882 | iPgOne >>= 8; |
| 3883 | } |
| 3884 | iWriteEnd += paf->iPgOne; |
| 3885 | if( SQLITE_OK==(rc = pFile->pMethods->xWrite |
| @@ -3903,12 +3911,11 @@ | |
| 3903 | if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; |
| 3904 | pFile = ORIGFILE(pFile); |
| 3905 | /* If append-mark is absent or will be overwritten, write it. */ |
| 3906 | if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ |
| 3907 | int rc = apndWriteMark(paf, pFile, iWriteEnd); |
| 3908 | if( SQLITE_OK!=rc ) |
| 3909 | return rc; |
| 3910 | } |
| 3911 | return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst); |
| 3912 | } |
| 3913 | |
| 3914 | /* |
| @@ -3916,12 +3923,11 @@ | |
| 3916 | */ |
| 3917 | static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| 3918 | ApndFile *paf = (ApndFile *)pFile; |
| 3919 | pFile = ORIGFILE(pFile); |
| 3920 | /* The append mark goes out first so truncate failure does not lose it. */ |
| 3921 | if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) |
| 3922 | return SQLITE_IOERR; |
| 3923 | /* Truncate underlying file just past append mark */ |
| 3924 | return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE); |
| 3925 | } |
| 3926 | |
| 3927 | /* |
| @@ -4033,12 +4039,13 @@ | |
| 4033 | sqlite3_int64 iOfst, |
| 4034 | int iAmt, |
| 4035 | void **pp |
| 4036 | ){ |
| 4037 | ApndFile *p = (ApndFile *)pFile; |
| 4038 | if( p->iMark < 0 || iOfst+iAmt > p->iMark) |
| 4039 | return SQLITE_IOERR; /* Cannot read what is not yet there. */ |
| 4040 | pFile = ORIGFILE(pFile); |
| 4041 | return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); |
| 4042 | } |
| 4043 | |
| 4044 | /* Release a memory-mapped page */ |
| @@ -4085,18 +4092,22 @@ | |
| 4085 | static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ |
| 4086 | int rc; |
| 4087 | char zHdr[16]; |
| 4088 | sqlite3_int64 iMark = apndReadMark(sz, pFile); |
| 4089 | if( iMark>=0 ){ |
| 4090 | /* If file has right end-marker, the expected odd size, and the |
| 4091 | * SQLite DB type marker where the end-marker puts it, then it |
| 4092 | * is an appendvfs database (to be treated as such.) |
| 4093 | */ |
| 4094 | rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark); |
| 4095 | if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 |
| 4096 | && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE ) |
| 4097 | return 1; /* It's an appendvfs database */ |
| 4098 | } |
| 4099 | return 0; |
| 4100 | } |
| 4101 | |
| 4102 | /* |
| @@ -4114,70 +4125,69 @@ | |
| 4114 | }else{ |
| 4115 | return 1; |
| 4116 | } |
| 4117 | } |
| 4118 | |
| 4119 | /* Round-up used to get appendvfs portion to begin at a page boundary. */ |
| 4120 | #define APND_ALIGN_MASK(nbits) ((1<<nbits)-1) |
| 4121 | #define APND_START_ROUNDUP(fsz, nbits) \ |
| 4122 | ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) ) |
| 4123 | |
| 4124 | /* |
| 4125 | ** Open an apnd file handle. |
| 4126 | */ |
| 4127 | static int apndOpen( |
| 4128 | sqlite3_vfs *pVfs, |
| 4129 | const char *zName, |
| 4130 | sqlite3_file *pFile, |
| 4131 | int flags, |
| 4132 | int *pOutFlags |
| 4133 | ){ |
| 4134 | ApndFile *p; |
| 4135 | sqlite3_file *pSubFile; |
| 4136 | sqlite3_vfs *pSubVfs; |
| 4137 | int rc; |
| 4138 | sqlite3_int64 sz; |
| 4139 | pSubVfs = ORIGVFS(pVfs); |
| 4140 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ |
| 4141 | /* The appendvfs is not to be used for transient or temporary databases. */ |
| 4142 | return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); |
| 4143 | } |
| 4144 | p = (ApndFile*)pFile; |
| 4145 | memset(p, 0, sizeof(*p)); |
| 4146 | pSubFile = ORIGFILE(pFile); |
| 4147 | pFile->pMethods = &apnd_io_methods; |
| 4148 | rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); |
| 4149 | if( rc ) goto apnd_open_done; |
| 4150 | rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); |
| 4151 | if( rc ){ |
| 4152 | pSubFile->pMethods->xClose(pSubFile); |
| 4153 | goto apnd_open_done; |
| 4154 | } |
| 4155 | if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){ |
| 4156 | memmove(pFile, pSubFile, pSubVfs->szOsFile); |
| 4157 | return SQLITE_OK; |
| 4158 | } |
| 4159 | /* Record that append mark has not been written until seen otherwise. */ |
| 4160 | p->iMark = -1; |
| 4161 | p->iPgOne = apndReadMark(sz, pFile); |
| 4162 | if( p->iPgOne>=0 ){ |
| 4163 | /* Append mark was found, infer its offset */ |
| 4164 | p->iMark = sz - p->iPgOne - APND_MARK_SIZE; |
| 4165 | return SQLITE_OK; |
| 4166 | } |
| 4167 | if( (flags & SQLITE_OPEN_CREATE)==0 ){ |
| 4168 | pSubFile->pMethods->xClose(pSubFile); |
| 4169 | rc = SQLITE_CANTOPEN; |
| 4170 | } |
| 4171 | /* Round newly added appendvfs location to #define'd page boundary. |
| 4172 | * Note that nothing has yet been written to the underlying file. |
| 4173 | * The append mark will be written along with first content write. |
| 4174 | * Until then, the p->iMark value indicates it is not yet written. |
| 4175 | */ |
| 4176 | p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS); |
| 4177 | apnd_open_done: |
| 4178 | if( rc ) pFile->pMethods = 0; |
| 4179 | return rc; |
| 4180 | } |
| 4181 | |
| 4182 | /* |
| 4183 | ** Delete an apnd file. |
| @@ -13207,10 +13217,11 @@ | |
| 13207 | |
| 13208 | rc = sqlite3_step(pStmt); |
| 13209 | if( rc!=SQLITE_ROW ) return; |
| 13210 | nColumn = sqlite3_column_count(pStmt); |
| 13211 | nAlloc = nColumn*4; |
| 13212 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 13213 | if( azData==0 ) shell_out_of_memory(); |
| 13214 | for(i=0; i<nColumn; i++){ |
| 13215 | azData[i] = strdup(sqlite3_column_name(pStmt,i)); |
| 13216 | } |
| @@ -13246,10 +13257,11 @@ | |
| 13246 | n = strlenChar(z); |
| 13247 | j = i%nColumn; |
| 13248 | if( n>p->actualWidth[j] ) p->actualWidth[j] = n; |
| 13249 | } |
| 13250 | if( seenInterrupt ) goto columnar_end; |
| 13251 | switch( p->cMode ){ |
| 13252 | case MODE_Column: { |
| 13253 | colSep = " "; |
| 13254 | rowSep = "\n"; |
| 13255 | if( p->showHeader ){ |
| @@ -14035,17 +14047,17 @@ | |
| 14035 | ".check GLOB Fail if output since .testcase does not match", |
| 14036 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 14037 | ".databases List names and files of attached databases", |
| 14038 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 14039 | ".dbinfo ?DB? Show status information about the database", |
| 14040 | ".dump ?TABLE? Render database content as SQL", |
| 14041 | " Options:", |
| 14042 | " --data-only Output only INSERT statements", |
| 14043 | " --newlines Allow unescaped newline characters in output", |
| 14044 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| 14045 | " --preserve-rowids Include ROWID values in the output", |
| 14046 | " TABLE is a LIKE pattern for the tables to dump", |
| 14047 | " Additional LIKE patterns can be given in subsequent arguments", |
| 14048 | ".echo on|off Turn command echo on or off", |
| 14049 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 14050 | " Other Modes:", |
| 14051 | #ifdef SQLITE_DEBUG |
| 14052 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -3662,12 +3662,12 @@ | |
| 3662 | ** set, then a new database is appended to the already existing file. |
| 3663 | ** |
| 3664 | ** (5) Otherwise, SQLITE_CANTOPEN is returned. |
| 3665 | ** |
| 3666 | ** To avoid unnecessary complications with the PENDING_BYTE, the size of |
| 3667 | ** the file containing the database is limited to 1GiB. (1073741824 bytes) |
| 3668 | ** This VFS will not read or write past the 1GiB mark. This restriction |
| 3669 | ** might be lifted in future versions. For now, if you need a larger |
| 3670 | ** database, then keep it in a separate file. |
| 3671 | ** |
| 3672 | ** If the file being opened is a plain database (not an appended one), then |
| 3673 | ** this shim is a pass-through into the default underlying VFS. (rule 3) |
| @@ -3692,18 +3692,20 @@ | |
| 3692 | |
| 3693 | /* |
| 3694 | ** Maximum size of the combined prefix + database + append-mark. This |
| 3695 | ** must be less than 0x40000000 to avoid locking issues on Windows. |
| 3696 | */ |
| 3697 | #define APND_MAX_SIZE (0x40000000) |
| 3698 | |
| 3699 | /* |
| 3700 | ** Try to align the database to an even multiple of APND_ROUNDUP bytes. |
| 3701 | */ |
| 3702 | #ifndef APND_ROUNDUP |
| 3703 | #define APND_ROUNDUP 4096 |
| 3704 | #endif |
| 3705 | #define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1)) |
| 3706 | #define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK) |
| 3707 | |
| 3708 | /* |
| 3709 | ** Forward declaration of objects used by this utility |
| 3710 | */ |
| 3711 | typedef struct sqlite3_vfs ApndVfs; |
| @@ -3713,43 +3715,49 @@ | |
| 3715 | ** access to randomness, etc. |
| 3716 | */ |
| 3717 | #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) |
| 3718 | #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1)) |
| 3719 | |
| 3720 | /* An open appendvfs file |
| 3721 | ** |
| 3722 | ** An instance of this structure describes the appended database file. |
| 3723 | ** A separate sqlite3_file object is always appended. The appended |
| 3724 | ** sqlite3_file object (which can be accessed using ORIGFILE()) describes |
| 3725 | ** the entire file, including the prefix, the database, and the |
| 3726 | ** append-mark. |
| 3727 | ** |
| 3728 | ** The structure of an AppendVFS database is like this: |
| 3729 | ** |
| 3730 | ** +-------------+---------+----------+-------------+ |
| 3731 | ** | prefix-file | padding | database | append-mark | |
| 3732 | ** +-------------+---------+----------+-------------+ |
| 3733 | ** ^ ^ |
| 3734 | ** | | |
| 3735 | ** iPgOne iMark |
| 3736 | ** |
| 3737 | ** |
| 3738 | ** "prefix file" - file onto which the database has been appended. |
| 3739 | ** "padding" - zero or more bytes inserted so that "database" |
| 3740 | ** starts on an APND_ROUNDUP boundary |
| 3741 | ** "database" - The SQLite database file |
| 3742 | ** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates |
| 3743 | ** the offset from the start of prefix-file to the start |
| 3744 | ** of "database". |
| 3745 | ** |
| 3746 | ** The size of the database is iMark - iPgOne. |
| 3747 | ** |
| 3748 | ** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value |
| 3749 | ** of iPgOne stored as a big-ending 64-bit integer. |
| 3750 | ** |
| 3751 | ** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE). |
| 3752 | ** Or, iMark is -1 to indicate that it has not yet been written. |
| 3753 | */ |
| 3754 | struct ApndFile { |
| 3755 | sqlite3_file base; /* Subclass. MUST BE FIRST! */ |
| 3756 | sqlite3_int64 iPgOne; /* Offset to the start of the database */ |
| 3757 | sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */ |
| 3758 | /* Always followed by another sqlite3_file that describes the whole file */ |
| 3759 | }; |
| 3760 | |
| 3761 | /* |
| 3762 | ** Methods for ApndFile |
| 3763 | */ |
| @@ -3875,11 +3883,11 @@ | |
| 3883 | unsigned char a[APND_MARK_SIZE]; |
| 3884 | int i = APND_MARK_FOS_SZ; |
| 3885 | int rc; |
| 3886 | assert(pFile == ORIGFILE(paf)); |
| 3887 | memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ); |
| 3888 | while( --i >= 0 ){ |
| 3889 | a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff); |
| 3890 | iPgOne >>= 8; |
| 3891 | } |
| 3892 | iWriteEnd += paf->iPgOne; |
| 3893 | if( SQLITE_OK==(rc = pFile->pMethods->xWrite |
| @@ -3903,12 +3911,11 @@ | |
| 3911 | if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL; |
| 3912 | pFile = ORIGFILE(pFile); |
| 3913 | /* If append-mark is absent or will be overwritten, write it. */ |
| 3914 | if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){ |
| 3915 | int rc = apndWriteMark(paf, pFile, iWriteEnd); |
| 3916 | if( SQLITE_OK!=rc ) return rc; |
| 3917 | } |
| 3918 | return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst); |
| 3919 | } |
| 3920 | |
| 3921 | /* |
| @@ -3916,12 +3923,11 @@ | |
| 3923 | */ |
| 3924 | static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){ |
| 3925 | ApndFile *paf = (ApndFile *)pFile; |
| 3926 | pFile = ORIGFILE(pFile); |
| 3927 | /* The append mark goes out first so truncate failure does not lose it. */ |
| 3928 | if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR; |
| 3929 | /* Truncate underlying file just past append mark */ |
| 3930 | return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE); |
| 3931 | } |
| 3932 | |
| 3933 | /* |
| @@ -4033,12 +4039,13 @@ | |
| 4039 | sqlite3_int64 iOfst, |
| 4040 | int iAmt, |
| 4041 | void **pp |
| 4042 | ){ |
| 4043 | ApndFile *p = (ApndFile *)pFile; |
| 4044 | if( p->iMark < 0 || iOfst+iAmt > p->iMark ){ |
| 4045 | return SQLITE_IOERR; /* Cannot read what is not yet there. */ |
| 4046 | } |
| 4047 | pFile = ORIGFILE(pFile); |
| 4048 | return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp); |
| 4049 | } |
| 4050 | |
| 4051 | /* Release a memory-mapped page */ |
| @@ -4085,18 +4092,22 @@ | |
| 4092 | static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){ |
| 4093 | int rc; |
| 4094 | char zHdr[16]; |
| 4095 | sqlite3_int64 iMark = apndReadMark(sz, pFile); |
| 4096 | if( iMark>=0 ){ |
| 4097 | /* If file has the correct end-marker, the expected odd size, and the |
| 4098 | ** SQLite DB type marker where the end-marker puts it, then it |
| 4099 | ** is an appendvfs database. |
| 4100 | */ |
| 4101 | rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark); |
| 4102 | if( SQLITE_OK==rc |
| 4103 | && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0 |
| 4104 | && (sz & 0x1ff) == APND_MARK_SIZE |
| 4105 | && sz>=512+APND_MARK_SIZE |
| 4106 | ){ |
| 4107 | return 1; /* It's an appendvfs database */ |
| 4108 | } |
| 4109 | } |
| 4110 | return 0; |
| 4111 | } |
| 4112 | |
| 4113 | /* |
| @@ -4114,70 +4125,69 @@ | |
| 4125 | }else{ |
| 4126 | return 1; |
| 4127 | } |
| 4128 | } |
| 4129 | |
| 4130 | /* |
| 4131 | ** Open an apnd file handle. |
| 4132 | */ |
| 4133 | static int apndOpen( |
| 4134 | sqlite3_vfs *pApndVfs, |
| 4135 | const char *zName, |
| 4136 | sqlite3_file *pFile, |
| 4137 | int flags, |
| 4138 | int *pOutFlags |
| 4139 | ){ |
| 4140 | ApndFile *pApndFile = (ApndFile*)pFile; |
| 4141 | sqlite3_file *pBaseFile = ORIGFILE(pFile); |
| 4142 | sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs); |
| 4143 | int rc; |
| 4144 | sqlite3_int64 sz = 0; |
| 4145 | if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){ |
| 4146 | /* The appendvfs is not to be used for transient or temporary databases. |
| 4147 | ** Just use the base VFS open to initialize the given file object and |
| 4148 | ** open the underlying file. (Appendvfs is then unused for this file.) |
| 4149 | */ |
| 4150 | return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags); |
| 4151 | } |
| 4152 | memset(pApndFile, 0, sizeof(ApndFile)); |
| 4153 | pFile->pMethods = &apnd_io_methods; |
| 4154 | pApndFile->iMark = -1; /* Append mark not yet written */ |
| 4155 | |
| 4156 | rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags); |
| 4157 | if( rc==SQLITE_OK ){ |
| 4158 | rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz); |
| 4159 | } |
| 4160 | if( rc ){ |
| 4161 | pBaseFile->pMethods->xClose(pBaseFile); |
| 4162 | pFile->pMethods = 0; |
| 4163 | return rc; |
| 4164 | } |
| 4165 | if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){ |
| 4166 | /* The file being opened appears to be just an ordinary DB. Copy |
| 4167 | ** the base dispatch-table so this instance mimics the base VFS. |
| 4168 | */ |
| 4169 | memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile); |
| 4170 | return SQLITE_OK; |
| 4171 | } |
| 4172 | pApndFile->iPgOne = apndReadMark(sz, pFile); |
| 4173 | if( pApndFile->iPgOne>=0 ){ |
| 4174 | pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */ |
| 4175 | return SQLITE_OK; |
| 4176 | } |
| 4177 | if( (flags & SQLITE_OPEN_CREATE)==0 ){ |
| 4178 | pBaseFile->pMethods->xClose(pBaseFile); |
| 4179 | rc = SQLITE_CANTOPEN; |
| 4180 | pFile->pMethods = 0; |
| 4181 | }else{ |
| 4182 | /* Round newly added appendvfs location to #define'd page boundary. |
| 4183 | ** Note that nothing has yet been written to the underlying file. |
| 4184 | ** The append mark will be written along with first content write. |
| 4185 | ** Until then, paf->iMark value indicates it is not yet written. |
| 4186 | */ |
| 4187 | pApndFile->iPgOne = APND_START_ROUNDUP(sz); |
| 4188 | } |
| 4189 | return rc; |
| 4190 | } |
| 4191 | |
| 4192 | /* |
| 4193 | ** Delete an apnd file. |
| @@ -13207,10 +13217,11 @@ | |
| 13217 | |
| 13218 | rc = sqlite3_step(pStmt); |
| 13219 | if( rc!=SQLITE_ROW ) return; |
| 13220 | nColumn = sqlite3_column_count(pStmt); |
| 13221 | nAlloc = nColumn*4; |
| 13222 | if( nAlloc<=0 ) nAlloc = 1; |
| 13223 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 13224 | if( azData==0 ) shell_out_of_memory(); |
| 13225 | for(i=0; i<nColumn; i++){ |
| 13226 | azData[i] = strdup(sqlite3_column_name(pStmt,i)); |
| 13227 | } |
| @@ -13246,10 +13257,11 @@ | |
| 13257 | n = strlenChar(z); |
| 13258 | j = i%nColumn; |
| 13259 | if( n>p->actualWidth[j] ) p->actualWidth[j] = n; |
| 13260 | } |
| 13261 | if( seenInterrupt ) goto columnar_end; |
| 13262 | if( nColumn==0 ) goto columnar_end; |
| 13263 | switch( p->cMode ){ |
| 13264 | case MODE_Column: { |
| 13265 | colSep = " "; |
| 13266 | rowSep = "\n"; |
| 13267 | if( p->showHeader ){ |
| @@ -14035,17 +14047,17 @@ | |
| 14047 | ".check GLOB Fail if output since .testcase does not match", |
| 14048 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 14049 | ".databases List names and files of attached databases", |
| 14050 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 14051 | ".dbinfo ?DB? Show status information about the database", |
| 14052 | ".dump ?OBJECTS? Render database content as SQL", |
| 14053 | " Options:", |
| 14054 | " --data-only Output only INSERT statements", |
| 14055 | " --newlines Allow unescaped newline characters in output", |
| 14056 | " --nosys Omit system tables (ex: \"sqlite_stat1\")", |
| 14057 | " --preserve-rowids Include ROWID values in the output", |
| 14058 | " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", |
| 14059 | " Additional LIKE patterns can be given in subsequent arguments", |
| 14060 | ".echo on|off Turn command echo on or off", |
| 14061 | ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", |
| 14062 | " Other Modes:", |
| 14063 | #ifdef SQLITE_DEBUG |
| 14064 |
-1038
| --- src/smtp.c | ||
| +++ src/smtp.c | ||
| @@ -646,1043 +646,5 @@ | ||
| 646 | 646 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 647 | 647 | } |
| 648 | 648 | smtp_session_free(p); |
| 649 | 649 | blob_reset(&body); |
| 650 | 650 | } |
| 651 | - | |
| 652 | -/***************************************************************************** | |
| 653 | -** Server implementation | |
| 654 | -*****************************************************************************/ | |
| 655 | - | |
| 656 | -/* | |
| 657 | -** Schema used by the email processing system. | |
| 658 | -*/ | |
| 659 | -static const char zEmailSchema[] = | |
| 660 | -@ -- bulk storage is in this table. This table can store either | |
| 661 | -@ -- the body of email messages or transcripts of an smtp session. | |
| 662 | -@ CREATE TABLE IF NOT EXISTS repository.emailblob( | |
| 663 | -@ emailid INTEGER PRIMARY KEY AUTOINCREMENT, -- numeric idea for the entry | |
| 664 | -@ enref INT, -- Number of references to this blob | |
| 665 | -@ ets INT, -- Corresponding transcript, or NULL | |
| 666 | -@ etime INT, -- insertion time, secs since 1970 | |
| 667 | -@ esz INT, -- uncompressed content size | |
| 668 | -@ etxt TEXT -- content of this entry | |
| 669 | -@ ); | |
| 670 | -@ | |
| 671 | -@ -- One row for each mailbox entry. All users emails are stored in | |
| 672 | -@ -- this same table. | |
| 673 | -@ CREATE TABLE IF NOT EXISTS repository.emailbox( | |
| 674 | -@ ebid INTEGER PRIMARY KEY, -- Unique id for each mailbox entry | |
| 675 | -@ euser TEXT, -- User who received this email | |
| 676 | -@ edate INT, -- Date received. Seconds since 1970 | |
| 677 | -@ efrom TEXT, -- Who is the email from | |
| 678 | -@ emsgid INT, -- Raw email text | |
| 679 | -@ estate INT, -- 0: Unread, 1: read, 2: trash 3: sent | |
| 680 | -@ esubject TEXT, -- Subject line for display | |
| 681 | -@ etags TEXT -- zero or more tags | |
| 682 | -@ ); | |
| 683 | -@ | |
| 684 | -@ -- Information on how to deliver incoming email. | |
| 685 | -@ CREATE TABLE IF NOT EXISTS repository.emailroute( | |
| 686 | -@ eaddr TEXT PRIMARY KEY, -- Email address | |
| 687 | -@ epolicy TEXT -- How to handle email sent to this address | |
| 688 | -@ ) WITHOUT ROWID; | |
| 689 | -@ | |
| 690 | -@ -- Outgoing email queue | |
| 691 | -@ CREATE TABLE IF NOT EXISTS repository.emailoutq( | |
| 692 | -@ edomain TEXT, -- Destination domain. (ex: "fossil-scm.org") | |
| 693 | -@ efrom TEXT, -- Sender email address (envelope "from") | |
| 694 | -@ eto TEXT, -- Recipient email address (envelope "to") | |
| 695 | -@ emsgid INT, -- Message body in the emailblob table | |
| 696 | -@ ectime INT, -- Time enqueued. Seconds since 1970 | |
| 697 | -@ emtime INT, -- Time of last send attempt. Sec since 1970 | |
| 698 | -@ ensend INT, -- Number of send attempts | |
| 699 | -@ ets INT -- Transcript of last failed attempt | |
| 700 | -@ ); | |
| 701 | -@ | |
| 702 | -@ -- Triggers to automatically keep the emailblob.enref field up to date | |
| 703 | -@ -- as entries in the emailblob, emailbox, and emailoutq tables are | |
| 704 | -@ -- deleted. | |
| 705 | -@ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1 | |
| 706 | -@ AFTER DELETE ON emailblob BEGIN | |
| 707 | -@ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets; | |
| 708 | -@ END; | |
| 709 | -@ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1 | |
| 710 | -@ AFTER DELETE ON emailbox BEGIN | |
| 711 | -@ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid; | |
| 712 | -@ END; | |
| 713 | -@ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1 | |
| 714 | -@ AFTER DELETE ON emailoutq BEGIN | |
| 715 | -@ UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid); | |
| 716 | -@ END; | |
| 717 | -@ | |
| 718 | -@ -- An index on the emailblob entries which are unreferenced. | |
| 719 | -@ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref) | |
| 720 | -@ WHERE enref<=0; | |
| 721 | -; | |
| 722 | - | |
| 723 | -/* | |
| 724 | -** Code used to delete the email tables. | |
| 725 | -*/ | |
| 726 | -static const char zEmailDrop[] = | |
| 727 | -@ DROP TABLE IF EXISTS emailblob; | |
| 728 | -@ DROP TABLE IF EXISTS emailbox; | |
| 729 | -@ DROP TABLE IF EXISTS emailroute; | |
| 730 | -@ DROP TABLE IF EXISTS emailqueue; | |
| 731 | -; | |
| 732 | - | |
| 733 | -#if INTERFACE | |
| 734 | -/* | |
| 735 | -** Mailbox message states | |
| 736 | -*/ | |
| 737 | -#define MSG_UNREAD 0 | |
| 738 | -#define MSG_READ 1 | |
| 739 | -#define MSG_TRASH 2 | |
| 740 | -#endif /* INTERFACE */ | |
| 741 | - | |
| 742 | - | |
| 743 | -/* | |
| 744 | -** Populate the schema of a database. | |
| 745 | -** | |
| 746 | -** eForce==0 Fast | |
| 747 | -** eForce==1 Run CREATE TABLE statements every time | |
| 748 | -** eForce==2 DROP then rerun CREATE TABLE | |
| 749 | -*/ | |
| 750 | -void smtp_server_schema(int eForce){ | |
| 751 | - if( eForce==2 ){ | |
| 752 | - db_multi_exec(zEmailDrop/*works-like:""*/); | |
| 753 | - } | |
| 754 | - if( eForce==1 || !db_table_exists("repository","emailblob") ){ | |
| 755 | - db_multi_exec(zEmailSchema/*works-like:""*/); | |
| 756 | - } | |
| 757 | -} | |
| 758 | - | |
| 759 | -/* | |
| 760 | -** WEBPAGE: setup_smtp | |
| 761 | -** | |
| 762 | -** Administrative page for configuring and controlling inbound email and | |
| 763 | -** output email queuing. This page is available to administrators | |
| 764 | -** only via the /Admin/EmailServer menu. | |
| 765 | -*/ | |
| 766 | -void setup_smtp(void){ | |
| 767 | - Stmt q; | |
| 768 | - login_check_credentials(); | |
| 769 | - if( !g.perm.Setup ){ | |
| 770 | - login_needed(0); | |
| 771 | - return; | |
| 772 | - } | |
| 773 | - db_begin_transaction(); | |
| 774 | - style_set_current_feature("smtp"); | |
| 775 | - style_header("Email Server Setup"); | |
| 776 | - if( db_table_exists("repository","emailroute") ){ | |
| 777 | - style_submenu_element("emailblob table", "%R/emailblob"); | |
| 778 | - style_submenu_element("emailoutq table", "%R/emailoutq"); | |
| 779 | - db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1"); | |
| 780 | - }else{ | |
| 781 | - db_prepare(&q, "SELECT null, null WHERE false"); | |
| 782 | - } | |
| 783 | - @ <h1>Email Routing Table</h1> | |
| 784 | - @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0"> | |
| 785 | - @ <thead> | |
| 786 | - @ <tr> | |
| 787 | - @ <th>Email Address | |
| 788 | - @ <th>Routing | |
| 789 | - @ <th> | |
| 790 | - @ </tr> | |
| 791 | - @ </thead><tbody> | |
| 792 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 793 | - const char *zEAddr = db_column_text(&q, 0); | |
| 794 | - const char *zEPolicy = db_column_text(&q, 1); | |
| 795 | - @ <tr> | |
| 796 | - @ <td valign="top">%h(zEAddr)</td> | |
| 797 | - @ <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td> | |
| 798 | - @ <td valign="top"><form method="POST" action="%R/setup_smtp_route"> | |
| 799 | - @ <input type="hidden" name="oaddr" value="%h(zEAddr)"> | |
| 800 | - @ <input type="submit" value="Edit"> | |
| 801 | - @ </form> | |
| 802 | - } | |
| 803 | - db_finalize(&q); | |
| 804 | - @ <tr> | |
| 805 | - @ <td colspan="3"> | |
| 806 | - @ <form method="POST" action="%R/setup_smtp_route"> | |
| 807 | - @ <input type="submit" value="New"> | |
| 808 | - @ ← Add a new email address | |
| 809 | - @ </form> | |
| 810 | - @ </table> | |
| 811 | - style_finish_page(); | |
| 812 | - db_end_transaction(0); | |
| 813 | -} | |
| 814 | - | |
| 815 | -/* | |
| 816 | -** WEBPAGE: setup_smtp_route | |
| 817 | -** | |
| 818 | -** Edit a single entry in the emailroute table. | |
| 819 | -** Query parameters: | |
| 820 | -** | |
| 821 | -** eaddr=ADDR ADDR is the email address as edited. | |
| 822 | -** | |
| 823 | -** oaddr=ADDR The original email address prior to editing. | |
| 824 | -** Omit to add a new address. | |
| 825 | -** | |
| 826 | -** epolicy=TXT The routing policy. | |
| 827 | -*/ | |
| 828 | -void setup_smtp_route(void){ | |
| 829 | - char *zEAddr = PT("eaddr"); /* new email address */ | |
| 830 | - char *zEPolicy = PT("epolicy"); /* new routing policy */ | |
| 831 | - char *zOAddr = PT("oaddr"); /* original email address */ | |
| 832 | - char *zErr = 0; | |
| 833 | - int iErr = 0; | |
| 834 | - login_check_credentials(); | |
| 835 | - if( !g.perm.Setup ){ | |
| 836 | - login_needed(0); | |
| 837 | - return; | |
| 838 | - } | |
| 839 | - style_set_current_feature("smtp"); | |
| 840 | - style_header("Email Route Editor"); | |
| 841 | - | |
| 842 | - if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){ | |
| 843 | - smtp_server_schema(0); | |
| 844 | - if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){ | |
| 845 | - /* New or changed email address */ | |
| 846 | - if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){ | |
| 847 | - iErr = 1; | |
| 848 | - zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr); | |
| 849 | - goto smtp_route_edit; | |
| 850 | - } | |
| 851 | - if( zEPolicy[0]==0 ){ | |
| 852 | - iErr = 2; | |
| 853 | - zErr = mprintf("empty route"); | |
| 854 | - goto smtp_route_edit; | |
| 855 | - } | |
| 856 | - } | |
| 857 | - /* If the email address has changed, or if the new policy is blank, | |
| 858 | - ** delete the old address and route information | |
| 859 | - */ | |
| 860 | - db_begin_transaction(); | |
| 861 | - if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){ | |
| 862 | - db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr); | |
| 863 | - } | |
| 864 | - if( zEPolicy[0] ){ | |
| 865 | - /* Insert the new address and route */ | |
| 866 | - db_multi_exec( | |
| 867 | - "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)", | |
| 868 | - zEAddr, zEPolicy | |
| 869 | - ); | |
| 870 | - } | |
| 871 | - db_end_transaction(0); | |
| 872 | - cgi_redirectf("%R/setup_smtp"); | |
| 873 | - } | |
| 874 | - if( P("cancel")!=0 ){ | |
| 875 | - cgi_redirectf("%R/setup_smtp"); | |
| 876 | - } | |
| 877 | - | |
| 878 | -smtp_route_edit: | |
| 879 | - if( zEAddr==0 ) zEAddr = zOAddr; | |
| 880 | - if( zEPolicy==0 && db_table_exists("repository","emailroute") ){ | |
| 881 | - zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q", | |
| 882 | - zEAddr); | |
| 883 | - } | |
| 884 | - if( zEPolicy==0 ) zEPolicy = ""; | |
| 885 | - @ <form method="POST" action="%R/setup_smtp_route"> | |
| 886 | - if( zOAddr ){ | |
| 887 | - @ <input type="hidden" name="oaddr" value="%h(zOAddr)"> | |
| 888 | - } | |
| 889 | - @ <table class="label-value"> | |
| 890 | - @ <tr> | |
| 891 | - @ <th>Email Address:</th> | |
| 892 | - @ <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)"> | |
| 893 | - if( iErr==1 ){ | |
| 894 | - @ <td><span class="generalError">← %z(zErr)</span> | |
| 895 | - } | |
| 896 | - @ </tr> | |
| 897 | - if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){ | |
| 898 | - @ <tr> | |
| 899 | - @ <th>Original Address:</th> | |
| 900 | - @ <td>%h(zOAddr) | |
| 901 | - @ </tr> | |
| 902 | - } | |
| 903 | - @ <tr> | |
| 904 | - @ <th>Routing:</th> | |
| 905 | - @ <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea> | |
| 906 | - if( iErr==2 ){ | |
| 907 | - @ <td valign="top"><span class="generalError">← %z(zErr)</span> | |
| 908 | - } | |
| 909 | - @ </tr> | |
| 910 | - @ <tr> | |
| 911 | - @ <td> | |
| 912 | - @ <td><input type="submit" name="edit" value="Apply"> | |
| 913 | - @ <input type="submit" name="cancel" value="Cancel"> | |
| 914 | - @ </tr> | |
| 915 | - @ </table> | |
| 916 | - @ <hr> | |
| 917 | - @ <h1>Instructions</h1> | |
| 918 | - @ | |
| 919 | - @ <p>The "Routing" field consists of zero or more lines where each | |
| 920 | - @ line is an "action" followed by an "argument". Available actions: | |
| 921 | - @ <ul> | |
| 922 | - @ <li><p><b>forward</b> <i>email-address</i> | |
| 923 | - @ <p>Forward the message to <i>email-address</i>. | |
| 924 | - @ <li><p><b>mbox</b> <i>login-name</i> | |
| 925 | - @ <p>Store the message in the local mailbox for the user | |
| 926 | - @ with USER.LOGIN=<i>login-name</i>. | |
| 927 | - @ </ul> | |
| 928 | - @ | |
| 929 | - @ <p>To delete a route → erase all text from the "Routing" field then | |
| 930 | - @ press the "Apply" button. | |
| 931 | - style_finish_page(); | |
| 932 | -} | |
| 933 | - | |
| 934 | -#if LOCAL_INTERFACE | |
| 935 | -/* | |
| 936 | -** State information for the server | |
| 937 | -*/ | |
| 938 | -struct SmtpServer { | |
| 939 | - sqlite3_int64 idTranscript; /* Transcript ID number */ | |
| 940 | - sqlite3_int64 idMsg; /* Message ID number */ | |
| 941 | - const char *zIpAddr; /* Remote IP address */ | |
| 942 | - char *zEhlo; /* Client domain on the EHLO line */ | |
| 943 | - char *zFrom; /* MAIL FROM: argument */ | |
| 944 | - int nTo; /* Number of RCPT TO: lines seen */ | |
| 945 | - struct SmtpTo { | |
| 946 | - char *z; /* Address in each RCPT TO line */ | |
| 947 | - int okRemote; /* zTo can be in another domain */ | |
| 948 | - } *aTo; | |
| 949 | - u32 srvrFlags; /* Control flags */ | |
| 950 | - int nEts; /* Number of references to the transcript */ | |
| 951 | - int nRef; /* Number of references to idMsg */ | |
| 952 | - Blob msg; /* Content following DATA */ | |
| 953 | - Blob transcript; /* Session transcript */ | |
| 954 | -}; | |
| 955 | - | |
| 956 | -#define SMTPSRV_CLEAR_MSG 1 /* smtp_server_clear() last message only */ | |
| 957 | -#define SMTPSRV_CLEAR_ALL 2 /* smtp_server_clear() everything */ | |
| 958 | -#define SMTPSRV_LOG 0x001 /* Record a transcript of the interaction */ | |
| 959 | -#define SMTPSRV_STDERR 0x002 /* Transcription written to stderr */ | |
| 960 | -#define SMTPSRV_DRYRUN 0x004 /* Do not record anything in database */ | |
| 961 | - | |
| 962 | -#endif /* LOCAL_INTERFACE */ | |
| 963 | - | |
| 964 | -/* | |
| 965 | -** Clear the SmtpServer object. Deallocate resources. | |
| 966 | -** How much to clear depends on eHowMuch | |
| 967 | -*/ | |
| 968 | -static void smtp_server_clear(SmtpServer *p, int eHowMuch){ | |
| 969 | - int i; | |
| 970 | - if( eHowMuch>=SMTPSRV_CLEAR_MSG ){ | |
| 971 | - fossil_free(p->zFrom); | |
| 972 | - p->zFrom = 0; | |
| 973 | - for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z); | |
| 974 | - fossil_free(p->aTo); | |
| 975 | - p->aTo = 0; | |
| 976 | - p->nTo = 0; | |
| 977 | - blob_reset(&p->msg); | |
| 978 | - p->idMsg = 0; | |
| 979 | - } | |
| 980 | - if( eHowMuch>=SMTPSRV_CLEAR_ALL ){ | |
| 981 | - blob_reset(&p->transcript); | |
| 982 | - p->idTranscript = 0; | |
| 983 | - fossil_free(p->zEhlo); | |
| 984 | - p->zEhlo = 0; | |
| 985 | - } | |
| 986 | -} | |
| 987 | - | |
| 988 | -/* | |
| 989 | -** Turn raw memory into an SmtpServer object. | |
| 990 | -*/ | |
| 991 | -static void smtp_server_init(SmtpServer *p){ | |
| 992 | - memset(p, 0, sizeof(*p)); | |
| 993 | - blob_init(&p->msg, 0, 0); | |
| 994 | - blob_init(&p->transcript, 0, 0); | |
| 995 | -} | |
| 996 | - | |
| 997 | -/* | |
| 998 | -** Append a new TO entry to the SmtpServer object. Do not do the | |
| 999 | -** append if the same entry is already on the list. | |
| 1000 | -** | |
| 1001 | -** The zAddr argument is obtained from fossil_malloc(). This | |
| 1002 | -** routine assumes ownership of the allocation. | |
| 1003 | -*/ | |
| 1004 | -static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){ | |
| 1005 | - int i; | |
| 1006 | - for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); } | |
| 1007 | - for(i=0; i<p->nTo; i++){ | |
| 1008 | - if( strcmp(zAddr, p->aTo[i].z)==0 ){ | |
| 1009 | - fossil_free(zAddr); | |
| 1010 | - if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote; | |
| 1011 | - return; | |
| 1012 | - } | |
| 1013 | - } | |
| 1014 | - p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0])); | |
| 1015 | - p->aTo[p->nTo].z = zAddr; | |
| 1016 | - p->aTo[p->nTo].okRemote = okRemote; | |
| 1017 | - p->nTo++; | |
| 1018 | -} | |
| 1019 | - | |
| 1020 | -/* | |
| 1021 | -** Send a single line of output from the server to the client. | |
| 1022 | -*/ | |
| 1023 | -static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){ | |
| 1024 | - Blob b = empty_blob; | |
| 1025 | - va_list ap; | |
| 1026 | - char *z; | |
| 1027 | - int n; | |
| 1028 | - va_start(ap, zFormat); | |
| 1029 | - blob_vappendf(&b, zFormat, ap); | |
| 1030 | - va_end(ap); | |
| 1031 | - z = blob_buffer(&b); | |
| 1032 | - n = blob_size(&b); | |
| 1033 | - assert( n>=2 ); | |
| 1034 | - assert( z[n-1]=='\n' ); | |
| 1035 | - assert( z[n-2]=='\r' ); | |
| 1036 | - if( p->srvrFlags & SMTPSRV_LOG ){ | |
| 1037 | - blob_appendf(&p->transcript, "S: %.*s\n", n-2, z); | |
| 1038 | - } | |
| 1039 | - if( p->srvrFlags & SMTPSRV_STDERR ){ | |
| 1040 | - fprintf(stderr, "S: %.*s\n", n-2, z); | |
| 1041 | - } | |
| 1042 | - fwrite(z, n, 1, stdout); | |
| 1043 | - fflush(stdout); | |
| 1044 | - blob_reset(&b); | |
| 1045 | -} | |
| 1046 | - | |
| 1047 | -/* | |
| 1048 | -** Read a single line from the client. | |
| 1049 | -*/ | |
| 1050 | -static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){ | |
| 1051 | - int rc = fgets(aBuf, nBuf, stdin)!=0; | |
| 1052 | - if( rc ){ | |
| 1053 | - if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){ | |
| 1054 | - blob_appendf(&p->transcript, "C: %s", aBuf); | |
| 1055 | - } | |
| 1056 | - if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){ | |
| 1057 | - fprintf(stderr, "C: %s", aBuf); | |
| 1058 | - } | |
| 1059 | - } | |
| 1060 | - return rc; | |
| 1061 | -} | |
| 1062 | - | |
| 1063 | -/* | |
| 1064 | -** RFC-5321 requires certain content be prepended to an email header | |
| 1065 | -** as that email is received. | |
| 1066 | -*/ | |
| 1067 | -static void smtp_server_prepend_header_lines(SmtpServer *p){ | |
| 1068 | - blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr); | |
| 1069 | -} | |
| 1070 | - | |
| 1071 | -/* | |
| 1072 | -** Capture the incoming email data into the p->msg blob. Dequote | |
| 1073 | -** lines of "..\r\n" into just ".\r\n". | |
| 1074 | -*/ | |
| 1075 | -static void smtp_server_capture_data(SmtpServer *p, char *z, int n){ | |
| 1076 | - int nLine = 0; | |
| 1077 | - while( fgets(z, n, stdin) ){ | |
| 1078 | - if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break; | |
| 1079 | - nLine++; | |
| 1080 | - if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){ | |
| 1081 | - memmove(z, z+1, 4); | |
| 1082 | - } | |
| 1083 | - blob_append(&p->msg, z, -1); | |
| 1084 | - } | |
| 1085 | - if( p->srvrFlags & SMTPSRV_LOG ){ | |
| 1086 | - blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n", | |
| 1087 | - nLine, blob_size(&p->msg)); | |
| 1088 | - } | |
| 1089 | - if( p->srvrFlags & SMTPSRV_STDERR ){ | |
| 1090 | - fprintf(stderr, "C: # %d lines, %d bytes of content\n", | |
| 1091 | - nLine, blob_size(&p->msg)); | |
| 1092 | - } | |
| 1093 | -} | |
| 1094 | - | |
| 1095 | -/* | |
| 1096 | -** Send an email to a single email addess that is registered with | |
| 1097 | -** this system, according to the instructions in emailroute. If | |
| 1098 | -** zAddr is not in the emailroute table, then this routine is a | |
| 1099 | -** no-op. Or if zAddr has already been processed, then this | |
| 1100 | -** routine is a no-op. | |
| 1101 | -*/ | |
| 1102 | -static void smtp_server_send_one_user( | |
| 1103 | - SmtpServer *p, /* The current inbound email */ | |
| 1104 | - const char *zAddr, /* Who to forward this to */ | |
| 1105 | - int okRemote /* True if ok to foward to another domain */ | |
| 1106 | -){ | |
| 1107 | - char *zPolicy; | |
| 1108 | - Blob policy, line, token, tail; | |
| 1109 | - | |
| 1110 | - zPolicy = db_text(0, | |
| 1111 | - "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr); | |
| 1112 | - if( zPolicy==0 ){ | |
| 1113 | - if( okRemote ){ | |
| 1114 | - int i; | |
| 1115 | - for(i=0; zAddr[i] && zAddr[i]!='@'; i++){} | |
| 1116 | - if( zAddr[i]=='@' && zAddr[i+1]!=0 ){ | |
| 1117 | - db_multi_exec( | |
| 1118 | - "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime," | |
| 1119 | - "emtime,ensend)" | |
| 1120 | - "VALUES(%Q,%Q,%Q,%lld,now(),0,0)", | |
| 1121 | - zAddr+i+1, p->zFrom, zAddr, p->idMsg | |
| 1122 | - ); | |
| 1123 | - p->nRef++; | |
| 1124 | - } | |
| 1125 | - } | |
| 1126 | - return; | |
| 1127 | - } | |
| 1128 | - blob_init(&policy, zPolicy, -1); | |
| 1129 | - while( blob_line(&policy, &line) ){ | |
| 1130 | - blob_trim(&line); | |
| 1131 | - blob_token(&line, &token); | |
| 1132 | - blob_tail(&line, &tail); | |
| 1133 | - if( blob_size(&tail)==0 ) continue; | |
| 1134 | - if( blob_eq_str(&token, "mbox", 4) ){ | |
| 1135 | - Blob subj; | |
| 1136 | - email_header_value(&p->msg, "subject", &subj); | |
| 1137 | - db_multi_exec( | |
| 1138 | - "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)" | |
| 1139 | - " VALUES(%Q,now(),%Q,%lld,0,%Q)", | |
| 1140 | - blob_str(&tail), p->zFrom, p->idMsg, | |
| 1141 | - blob_str(&subj) | |
| 1142 | - ); | |
| 1143 | - blob_reset(&subj); | |
| 1144 | - p->nRef++; | |
| 1145 | - } | |
| 1146 | - if( blob_eq_str(&token, "forward", 7) ){ | |
| 1147 | - smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1); | |
| 1148 | - } | |
| 1149 | - blob_reset(&tail); | |
| 1150 | - } | |
| 1151 | -} | |
| 1152 | - | |
| 1153 | -/* | |
| 1154 | -** The SmtpServer object contains a complete incoming email. | |
| 1155 | -** Add this email to the database. | |
| 1156 | -*/ | |
| 1157 | -static void smtp_server_route_incoming(SmtpServer *p, int bFinish){ | |
| 1158 | - Stmt s; | |
| 1159 | - int i; | |
| 1160 | - int nEtsStart = p->nEts; | |
| 1161 | - if( p->zFrom | |
| 1162 | - && p->nTo | |
| 1163 | - && blob_size(&p->msg) | |
| 1164 | - && (p->srvrFlags & SMTPSRV_DRYRUN)==0 | |
| 1165 | - ){ | |
| 1166 | - db_begin_write(); | |
| 1167 | - if( p->idTranscript==0 ) smtp_server_schema(0); | |
| 1168 | - p->nRef = 0; | |
| 1169 | - db_prepare(&s, | |
| 1170 | - "INSERT INTO emailblob(ets,etime,etxt,enref,esz)" | |
| 1171 | - " VALUES(:ets,now(),compress(:etxt),0,:esz)" | |
| 1172 | - ); | |
| 1173 | - p->nEts++; | |
| 1174 | - if( !bFinish && p->idTranscript==0 ){ | |
| 1175 | - db_bind_null(&s, ":ets"); | |
| 1176 | - db_bind_null(&s, ":etxt"); | |
| 1177 | - db_bind_null(&s, ":esz"); | |
| 1178 | - db_step(&s); | |
| 1179 | - db_reset(&s); | |
| 1180 | - p->idTranscript = db_last_insert_rowid(); | |
| 1181 | - }else if( bFinish ){ | |
| 1182 | - if( p->idTranscript ){ | |
| 1183 | - db_multi_exec( | |
| 1184 | - "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d" | |
| 1185 | - " WHERE emailid=%lld", | |
| 1186 | - blob_str(&p->transcript), p->nEts, blob_size(&p->transcript), | |
| 1187 | - p->idTranscript); | |
| 1188 | - }else{ | |
| 1189 | - db_bind_null(&s, ":ets"); | |
| 1190 | - db_bind_str(&s, ":etxt", &p->transcript); | |
| 1191 | - db_bind_int(&s, ":esz", blob_size(&p->transcript)); | |
| 1192 | - db_step(&s); | |
| 1193 | - db_reset(&s); | |
| 1194 | - p->idTranscript = db_last_insert_rowid(); | |
| 1195 | - db_multi_exec( | |
| 1196 | - "UPDATE emailblob SET enref=%d WHERE emailid=%lld", | |
| 1197 | - p->nEts, p->idTranscript); | |
| 1198 | - } | |
| 1199 | - /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n", | |
| 1200 | - ** p->idTranscript, p->nEts); */ | |
| 1201 | - } | |
| 1202 | - db_bind_int64(&s, ":ets", p->idTranscript); | |
| 1203 | - db_bind_str(&s, ":etxt", &p->msg); | |
| 1204 | - db_bind_int(&s, ":esz", blob_size(&p->msg)); | |
| 1205 | - db_step(&s); | |
| 1206 | - db_finalize(&s); | |
| 1207 | - p->idMsg = db_last_insert_rowid(); | |
| 1208 | - | |
| 1209 | - /* make entries in emailbox and emailoutq */ | |
| 1210 | - for(i=0; i<p->nTo; i++){ | |
| 1211 | - int okRemote = p->aTo[i].okRemote; | |
| 1212 | - p->aTo[i].okRemote = 1; | |
| 1213 | - smtp_server_send_one_user(p, p->aTo[i].z, okRemote); | |
| 1214 | - } | |
| 1215 | - | |
| 1216 | - /* Fix up the emailblob.enref field of the email message body */ | |
| 1217 | - if( p->nRef ){ | |
| 1218 | - db_multi_exec( | |
| 1219 | - "UPDATE emailblob SET enref=%d WHERE emailid=%lld", | |
| 1220 | - p->nRef, p->idMsg | |
| 1221 | - ); | |
| 1222 | - }else{ | |
| 1223 | - db_multi_exec( | |
| 1224 | - "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg | |
| 1225 | - ); | |
| 1226 | - p->nEts = nEtsStart; | |
| 1227 | - } | |
| 1228 | - | |
| 1229 | - /* Clean out legacy entries */ | |
| 1230 | - if( bFinish ){ | |
| 1231 | - db_multi_exec("DELETE FROM emailblob WHERE enref<=0"); | |
| 1232 | - } | |
| 1233 | - | |
| 1234 | - /* Finish the transaction after all changes are implemented */ | |
| 1235 | - db_commit_transaction(); | |
| 1236 | - } | |
| 1237 | - smtp_server_clear(p, SMTPSRV_CLEAR_MSG); | |
| 1238 | -} | |
| 1239 | - | |
| 1240 | -/* | |
| 1241 | -** Remove stale content from the emailblob table. | |
| 1242 | -*/ | |
| 1243 | -int smtp_cleanup(void){ | |
| 1244 | - int nAction = 0; | |
| 1245 | - if( db_table_exists("repository","emailblob") ){ | |
| 1246 | - db_begin_transaction(); | |
| 1247 | - db_multi_exec( | |
| 1248 | - "UPDATE emailblob SET ets=NULL WHERE enref<=0;" | |
| 1249 | - "DELETE FROM emailblob WHERE enref<=0;" | |
| 1250 | - ); | |
| 1251 | - nAction = db_changes(); | |
| 1252 | - db_end_transaction(0); | |
| 1253 | - } | |
| 1254 | - return nAction; | |
| 1255 | -} | |
| 1256 | - | |
| 1257 | -/* | |
| 1258 | -** COMMAND: test-emailblob-refcheck | |
| 1259 | -** | |
| 1260 | -** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean] | |
| 1261 | -** | |
| 1262 | -** Verify that the emailblob.enref field is correct. Report any errors. | |
| 1263 | -** Use the --repair command to fix up the enref field. The --full option | |
| 1264 | -** gives a full report showing the enref value on all entries in the | |
| 1265 | -** emailblob table. If the --clean flags is used together with --repair, | |
| 1266 | -** then emailblob table entires with enref==0 are removed. | |
| 1267 | -*/ | |
| 1268 | -void test_refcheck_emailblob(void){ | |
| 1269 | - int doRepair; | |
| 1270 | - int fullReport; | |
| 1271 | - int doClean; | |
| 1272 | - Blob sql; | |
| 1273 | - Stmt q; | |
| 1274 | - int nErr = 0; | |
| 1275 | - db_find_and_open_repository(0, 0); | |
| 1276 | - fullReport = find_option("full",0,0)!=0; | |
| 1277 | - doRepair = find_option("repair",0,0)!=0; | |
| 1278 | - doClean = find_option("clean",0,0)!=0; | |
| 1279 | - verify_all_options(); | |
| 1280 | - if( !db_table_exists("repository","emailblob") ){ | |
| 1281 | - fossil_print("emailblob table is not configured - nothing to check\n"); | |
| 1282 | - return; | |
| 1283 | - } | |
| 1284 | - db_multi_exec( | |
| 1285 | - "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);" | |
| 1286 | - "INSERT INTO refcnt SELECT ets, count(*) FROM (" | |
| 1287 | - " SELECT ets FROM emailblob" | |
| 1288 | - " UNION ALL" | |
| 1289 | - " SELECT emsgid FROM emailbox" | |
| 1290 | - " UNION ALL" | |
| 1291 | - " SELECT emsgid FROM emailoutq" | |
| 1292 | - ") WHERE ets IS NOT NULL GROUP BY 1;" | |
| 1293 | - "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;" | |
| 1294 | - ); | |
| 1295 | - if( doRepair ){ | |
| 1296 | - db_multi_exec( | |
| 1297 | - "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)" | |
| 1298 | - ); | |
| 1299 | - if( doClean ){ | |
| 1300 | - smtp_cleanup(); | |
| 1301 | - } | |
| 1302 | - } | |
| 1303 | - blob_init(&sql, 0, 0); | |
| 1304 | - blob_append_sql(&sql, | |
| 1305 | - "SELECT a.emailid, a.enref, b.n" | |
| 1306 | - " FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id" | |
| 1307 | - ); | |
| 1308 | - if( !fullReport ){ | |
| 1309 | - blob_append_sql(&sql, " WHERE a.enref!=b.n"); | |
| 1310 | - } | |
| 1311 | - db_prepare_blob(&q, &sql); | |
| 1312 | - blob_reset(&sql); | |
| 1313 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1314 | - sqlite3_int64 id = db_column_int64(&q,0); | |
| 1315 | - int n1 = db_column_int(&q, 1); | |
| 1316 | - int n2 = db_column_int(&q, 2); | |
| 1317 | - if( n1!=n2 ) nErr++; | |
| 1318 | - fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : ""); | |
| 1319 | - } | |
| 1320 | - db_finalize(&q); | |
| 1321 | - if( nErr ){ | |
| 1322 | - fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr); | |
| 1323 | - } | |
| 1324 | -} | |
| 1325 | - | |
| 1326 | - | |
| 1327 | -/* | |
| 1328 | -** COMMAND: smtpd* | |
| 1329 | -** | |
| 1330 | -** Usage: %fossil smtpd [OPTIONS] REPOSITORY | |
| 1331 | -** | |
| 1332 | -** Begin a SMTP conversation with a client using stdin/stdout. The | |
| 1333 | -** received email is stored in REPOSITORY. | |
| 1334 | -** | |
| 1335 | -** Options: | |
| 1336 | -** | |
| 1337 | -** --dryrun Do not record any emails in the database | |
| 1338 | -** | |
| 1339 | -** --trace Print a transcript of the conversation on stderr | |
| 1340 | -** for debugging and analysis | |
| 1341 | -** | |
| 1342 | -** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR | |
| 1343 | -** is the name of an environment variable, the address | |
| 1344 | -** is taken from that environment variable. | |
| 1345 | -*/ | |
| 1346 | -void smtp_server(void){ | |
| 1347 | - char *zDbName; | |
| 1348 | - const char *zDomain; | |
| 1349 | - SmtpServer x; | |
| 1350 | - char z[5000]; | |
| 1351 | - | |
| 1352 | - smtp_server_init(&x); | |
| 1353 | - zDomain = find_option("domain",0,1); | |
| 1354 | - if( zDomain==0 ) zDomain = ""; | |
| 1355 | - x.srvrFlags = SMTPSRV_LOG; | |
| 1356 | - if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; | |
| 1357 | - if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; | |
| 1358 | - x.zIpAddr = find_option("ipaddr",0,1); | |
| 1359 | - if( x.zIpAddr ){ | |
| 1360 | - const char *zNew = fossil_getenv(x.zIpAddr); | |
| 1361 | - if( zNew && zNew[0] ) x.zIpAddr = zNew; | |
| 1362 | - } | |
| 1363 | - if( x.zIpAddr==0 ){ | |
| 1364 | - x.zIpAddr = cgi_remote_ip(0); | |
| 1365 | - if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?"; | |
| 1366 | - } | |
| 1367 | - verify_all_options(); | |
| 1368 | - if( g.argc!=3 ) usage("DBNAME"); | |
| 1369 | - zDbName = g.argv[2]; | |
| 1370 | - zDbName = enter_chroot_jail(zDbName, 0); | |
| 1371 | - db_open_repository(zDbName); | |
| 1372 | - add_content_sql_commands(g.db); | |
| 1373 | - smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n", | |
| 1374 | - zDomain, MANIFEST_VERSION); | |
| 1375 | - while( smtp_server_gets(&x, z, sizeof(z)) ){ | |
| 1376 | - if( strncmp(z, "EHLO", 4)==0 && fossil_isspace(z[4]) ){ | |
| 1377 | - smtp_server_send(&x, "250 ok\r\n"); | |
| 1378 | - }else | |
| 1379 | - if( strncmp(z, "HELO", 4)==0 && fossil_isspace(z[4]) ){ | |
| 1380 | - smtp_server_send(&x, "250 ok\r\n"); | |
| 1381 | - }else | |
| 1382 | - if( strncmp(z, "MAIL FROM:<", 11)==0 ){ | |
| 1383 | - smtp_server_route_incoming(&x, 0); | |
| 1384 | - smtp_server_clear(&x, SMTPSRV_CLEAR_MSG); | |
| 1385 | - x.zFrom = email_copy_addr(z+11,'>'); | |
| 1386 | - if( x.zFrom==0 ){ | |
| 1387 | - smtp_server_send(&x, "500 unacceptable email address\r\n"); | |
| 1388 | - }else{ | |
| 1389 | - smtp_server_send(&x, "250 ok\r\n"); | |
| 1390 | - } | |
| 1391 | - }else | |
| 1392 | - if( strncmp(z, "RCPT TO:<", 9)==0 ){ | |
| 1393 | - char *zAddr; | |
| 1394 | - if( x.zFrom==0 ){ | |
| 1395 | - smtp_server_send(&x, "500 missing MAIL FROM\r\n"); | |
| 1396 | - continue; | |
| 1397 | - } | |
| 1398 | - zAddr = email_copy_addr(z+9, '>'); | |
| 1399 | - if( zAddr==0 ){ | |
| 1400 | - smtp_server_send(&x, "505 no such user\r\n"); | |
| 1401 | - continue; | |
| 1402 | - } | |
| 1403 | - smtp_append_to(&x, zAddr, 0); | |
| 1404 | - if( x.nTo>=100 ){ | |
| 1405 | - smtp_server_send(&x, "452 too many recipients\r\n"); | |
| 1406 | - continue; | |
| 1407 | - } | |
| 1408 | - smtp_server_send(&x, "250 ok\r\n"); | |
| 1409 | - }else | |
| 1410 | - if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){ | |
| 1411 | - if( x.zFrom==0 || x.nTo==0 ){ | |
| 1412 | - smtp_server_send(&x, "500 missing RCPT TO\r\n"); | |
| 1413 | - continue; | |
| 1414 | - } | |
| 1415 | - smtp_server_send(&x, "354 ready\r\n"); | |
| 1416 | - smtp_server_prepend_header_lines(&x); | |
| 1417 | - smtp_server_capture_data(&x, z, sizeof(z)); | |
| 1418 | - smtp_server_send(&x, "250 ok\r\n"); | |
| 1419 | - }else | |
| 1420 | - if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ | |
| 1421 | - smtp_server_route_incoming(&x, 1); | |
| 1422 | - smtp_server_send(&x, "221 closing connection\r\n"); | |
| 1423 | - break; | |
| 1424 | - }else | |
| 1425 | - { | |
| 1426 | - smtp_server_send(&x, "500 unknown command\r\n"); | |
| 1427 | - } | |
| 1428 | - } | |
| 1429 | - smtp_server_clear(&x, SMTPSRV_CLEAR_ALL); | |
| 1430 | -} | |
| 1431 | - | |
| 1432 | -/* | |
| 1433 | -** Zero-terminate the argument. Return a pointer the start of the | |
| 1434 | -** next argument, or to NULL if there are no more arguments. | |
| 1435 | -*/ | |
| 1436 | -static char *pop3d_arg(char *z){ | |
| 1437 | - if( z[0]==0 || fossil_isspace(z[0]) ){ | |
| 1438 | - return 0; | |
| 1439 | - } | |
| 1440 | - z++; | |
| 1441 | - while( z[0] && !fossil_isspace(z[0]) ){ z++; } | |
| 1442 | - if( z[0]==0 ) return 0; | |
| 1443 | - z[0] = 0; | |
| 1444 | - z++; | |
| 1445 | - if( z[0]==0 || fossil_isspace(z[0]) ) return 0; | |
| 1446 | - return z; | |
| 1447 | -} | |
| 1448 | - | |
| 1449 | -/* | |
| 1450 | -** Write formatted output back to the pop3 client, and also to the | |
| 1451 | -** log file, if there is a log file. | |
| 1452 | -*/ | |
| 1453 | -static void pop3_print(FILE *pLog, const char *zFormat, ...){ | |
| 1454 | - va_list ap; | |
| 1455 | - char zLine[500]; | |
| 1456 | - va_start(ap, zFormat); | |
| 1457 | - sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap); | |
| 1458 | - va_end(ap); | |
| 1459 | - printf("%s\r\n", zLine); | |
| 1460 | - fflush(stdout); | |
| 1461 | - if( pLog ) fprintf(pLog, "S: %s\n", zLine); | |
| 1462 | -} | |
| 1463 | - | |
| 1464 | -/* | |
| 1465 | -** Try to log in for zUser and zPass. | |
| 1466 | -** | |
| 1467 | -** zUser can either point to a Fossil user name or to an email address | |
| 1468 | -** found in the user table's info field, in angle brackets. | |
| 1469 | -*/ | |
| 1470 | -static int pop3_login(const char *zUser, char *zPass){ | |
| 1471 | - return login_search_uid(&zUser, zPass) != 0; | |
| 1472 | -} | |
| 1473 | - | |
| 1474 | -/* | |
| 1475 | -** COMMAND: pop3d* | |
| 1476 | -** | |
| 1477 | -** Usage: %fossil pop3d [OPTIONS] REPOSITORY | |
| 1478 | -** | |
| 1479 | -** Begin a POP3 conversation with a client using stdin/stdout using | |
| 1480 | -** the mailboxes stored in REPOSITORY. | |
| 1481 | -** | |
| 1482 | -** If launched as root, the process first enters a chroot jail using | |
| 1483 | -** the directory of REPOSITORY as root, then drops all privileges and | |
| 1484 | -** assumes the user and group of REPOSITORY before reading any content | |
| 1485 | -** off of the wire. | |
| 1486 | -** | |
| 1487 | -** --logdir DIR Each pop3d session creates a new logfile | |
| 1488 | -** in the directory DIR and records a transcript | |
| 1489 | -** of the session there. The logfile is opened | |
| 1490 | -** before entering the chroot jail. | |
| 1491 | -*/ | |
| 1492 | -void pop3d_command(void){ | |
| 1493 | - char *zDbName; | |
| 1494 | - char *zA1, *zA2, *zCmd, *z; | |
| 1495 | - int inAuth = 1; | |
| 1496 | - int i; | |
| 1497 | - FILE *pLog = 0; | |
| 1498 | - const char *zDir; | |
| 1499 | - Stmt q; | |
| 1500 | - char zIn[1000]; | |
| 1501 | - char zUser[100]; | |
| 1502 | - zDir = find_option("logdir",0,1); | |
| 1503 | - if( zDir ){ | |
| 1504 | - char *zFile = file_time_tempname(zDir, ".txt"); | |
| 1505 | - pLog = fossil_fopen(zFile, "w"); | |
| 1506 | - fossil_free(zFile); | |
| 1507 | - } | |
| 1508 | - verify_all_options(); | |
| 1509 | - if( g.argc!=3 ) usage("DBNAME"); | |
| 1510 | - zDbName = g.argv[2]; | |
| 1511 | - zDbName = enter_chroot_jail(zDbName, 0); | |
| 1512 | - db_open_repository(zDbName); | |
| 1513 | - add_content_sql_commands(g.db); | |
| 1514 | - pop3_print(pLog, "+OK POP3 server ready"); | |
| 1515 | - while( fgets(zIn, sizeof(zIn), stdin) ){ | |
| 1516 | - if( pLog ) fprintf(pLog, "C: %s", zIn); | |
| 1517 | - zCmd = zIn; | |
| 1518 | - zA1 = pop3d_arg(zCmd); | |
| 1519 | - zA2 = zA1 ? pop3d_arg(zA1) : 0; | |
| 1520 | - for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); } | |
| 1521 | - if( strcmp(zCmd,"quit")==0 ){ | |
| 1522 | - if( !inAuth ){ | |
| 1523 | - db_multi_exec( | |
| 1524 | - "UPDATE emailbox SET estate=2" | |
| 1525 | - " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);" | |
| 1526 | - ); | |
| 1527 | - } | |
| 1528 | - pop3_print(pLog, "+OK"); | |
| 1529 | - break; | |
| 1530 | - } | |
| 1531 | - if( strcmp(zCmd,"capa")==0 ){ | |
| 1532 | - static const char *const azCap[] = { | |
| 1533 | - "TOP", "USER", "UIDL", | |
| 1534 | - }; | |
| 1535 | - int i; | |
| 1536 | - pop3_print(pLog, "+OK"); | |
| 1537 | - for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){ | |
| 1538 | - pop3_print(pLog, "%s", azCap[i]); | |
| 1539 | - } | |
| 1540 | - pop3_print(pLog, "."); | |
| 1541 | - continue; | |
| 1542 | - } | |
| 1543 | - if( inAuth ){ | |
| 1544 | - if( strcmp(zCmd,"user")==0 ){ | |
| 1545 | - if( zA1==0 || zA2!=0 ) goto cmd_error; | |
| 1546 | - sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1); | |
| 1547 | - goto cmd_ok; | |
| 1548 | - } | |
| 1549 | - if( strcmp(zCmd,"pass")==0 ){ | |
| 1550 | - if( zA1==0 || zA2!=0 ) goto cmd_error; | |
| 1551 | - if( pop3_login(zUser,zA1)==0 ){ | |
| 1552 | - goto cmd_error; | |
| 1553 | - }else{ | |
| 1554 | - inAuth = 0; | |
| 1555 | - db_multi_exec( | |
| 1556 | - "CREATE TEMP TABLE pop3(" | |
| 1557 | - " id INTEGER PRIMARY KEY," | |
| 1558 | - " emailid INT," | |
| 1559 | - " ebid INT," | |
| 1560 | - " isDel INT," | |
| 1561 | - " esz INT" | |
| 1562 | - ");" | |
| 1563 | - "INSERT INTO pop3(id,emailid,ebid,isDel,esz)" | |
| 1564 | - " SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox" | |
| 1565 | - " WHERE emailid=emsgid AND euser=%Q AND estate<=1" | |
| 1566 | - " ORDER BY edate;", | |
| 1567 | - zUser | |
| 1568 | - ); | |
| 1569 | - goto cmd_ok; | |
| 1570 | - } | |
| 1571 | - } | |
| 1572 | - /* Fossil cannot process APOP since the users clear-text password is | |
| 1573 | - ** unknown. */ | |
| 1574 | - goto cmd_error; | |
| 1575 | - }else{ | |
| 1576 | - if( strcmp(zCmd,"stat")==0 ){ | |
| 1577 | - db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel"); | |
| 1578 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 1579 | - pop3_print(pLog, "+OK %d %d", | |
| 1580 | - db_column_int(&q,0), db_column_int(&q,1)); | |
| 1581 | - }else{ | |
| 1582 | - pop3_print(pLog,"-ERR"); | |
| 1583 | - } | |
| 1584 | - db_finalize(&q); | |
| 1585 | - continue; | |
| 1586 | - } | |
| 1587 | - if( strcmp(zCmd,"list")==0 ){ | |
| 1588 | - if( zA1 ){ | |
| 1589 | - db_prepare(&q, "SELECT id, esz FROM pop3" | |
| 1590 | - " WHERE id=%d AND NOT isDel", atoi(zA1)); | |
| 1591 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 1592 | - pop3_print(pLog, "+OK %d %d", | |
| 1593 | - db_column_int(&q,0), db_column_int(&q,1)); | |
| 1594 | - }else{ | |
| 1595 | - pop3_print(pLog, "-ERR"); | |
| 1596 | - } | |
| 1597 | - }else{ | |
| 1598 | - pop3_print(pLog, "+OK"); | |
| 1599 | - db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel"); | |
| 1600 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1601 | - pop3_print(pLog, "%d %d", | |
| 1602 | - db_column_int(&q,0), db_column_int(&q,1)); | |
| 1603 | - } | |
| 1604 | - pop3_print(pLog, "."); | |
| 1605 | - } | |
| 1606 | - db_finalize(&q); | |
| 1607 | - continue; | |
| 1608 | - } | |
| 1609 | - if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){ | |
| 1610 | - Blob all, line; | |
| 1611 | - int nLine = 0; | |
| 1612 | - int iLimit; | |
| 1613 | - int hdrPending = 1; | |
| 1614 | - if( zA1==0 ) goto cmd_error; | |
| 1615 | - iLimit = zA2 ? atoi(zA2) : 2147483647; | |
| 1616 | - if( iLimit<0 ) goto cmd_error; | |
| 1617 | - z = db_text(0, "SELECT decompress(emailblob.etxt) " | |
| 1618 | - " FROM emailblob, pop3" | |
| 1619 | - " WHERE emailblob.emailid=pop3.emailid" | |
| 1620 | - " AND pop3.id=%d AND NOT pop3.isDel", | |
| 1621 | - atoi(zA1)); | |
| 1622 | - if( z==0 ) goto cmd_error; | |
| 1623 | - pop3_print(pLog, "+OK"); | |
| 1624 | - blob_init(&all, z, -1); | |
| 1625 | - while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){ | |
| 1626 | - char c = blob_buffer(&line)[0]; | |
| 1627 | - if( c=='.' ){ | |
| 1628 | - fputc('.', stdout); | |
| 1629 | - }else if( c=='\r' || c=='\n' ){ | |
| 1630 | - hdrPending = 0; | |
| 1631 | - } | |
| 1632 | - fwrite(blob_buffer(&line), 1, blob_size(&line), stdout); | |
| 1633 | - nLine++; | |
| 1634 | - if( !hdrPending ) iLimit--; | |
| 1635 | - } | |
| 1636 | - if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine); | |
| 1637 | - pop3_print(pLog, "."); | |
| 1638 | - fossil_free(z); | |
| 1639 | - blob_reset(&all); | |
| 1640 | - blob_reset(&line); | |
| 1641 | - fflush(stdout); | |
| 1642 | - continue; | |
| 1643 | - } | |
| 1644 | - if( strcmp(zCmd,"dele")==0 ){ | |
| 1645 | - if( zA1==0 ) goto cmd_error; | |
| 1646 | - db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1)); | |
| 1647 | - goto cmd_ok; | |
| 1648 | - } | |
| 1649 | - if( strcmp(zCmd,"rset")==0 ){ | |
| 1650 | - db_multi_exec("UPDATE pop3 SET isDel=0"); | |
| 1651 | - goto cmd_ok; | |
| 1652 | - } | |
| 1653 | - if( strcmp(zCmd,"uidl")==0 ){ | |
| 1654 | - if( zA1 ){ | |
| 1655 | - db_prepare(&q, "SELECT id, emailid FROM pop3" | |
| 1656 | - " WHERE id=%d AND NOT isDel", atoi(zA1)); | |
| 1657 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 1658 | - pop3_print(pLog, "+OK %d %d", | |
| 1659 | - db_column_int(&q,0), db_column_int(&q,1)); | |
| 1660 | - }else{ | |
| 1661 | - pop3_print(pLog,"-ERR"); | |
| 1662 | - } | |
| 1663 | - }else{ | |
| 1664 | - pop3_print(pLog, "+OK"); | |
| 1665 | - db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel"); | |
| 1666 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1667 | - pop3_print(pLog, "%d %d", | |
| 1668 | - db_column_int(&q,0), db_column_int(&q,1)); | |
| 1669 | - } | |
| 1670 | - pop3_print(pLog, "."); | |
| 1671 | - } | |
| 1672 | - db_finalize(&q); | |
| 1673 | - continue; | |
| 1674 | - } | |
| 1675 | - if( strcmp(zCmd,"noop")==0 ){ | |
| 1676 | - goto cmd_ok; | |
| 1677 | - } | |
| 1678 | - /* Else, fall through into cmd_error */ | |
| 1679 | - } | |
| 1680 | - cmd_error: | |
| 1681 | - pop3_print(pLog, "-ERR"); | |
| 1682 | - continue; | |
| 1683 | - cmd_ok: | |
| 1684 | - pop3_print(pLog, "+OK"); | |
| 1685 | - continue; | |
| 1686 | - } | |
| 1687 | - if( pLog ) fclose(pLog); | |
| 1688 | -} | |
| 1689 | 651 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -646,1043 +646,5 @@ | |
| 646 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 647 | } |
| 648 | smtp_session_free(p); |
| 649 | blob_reset(&body); |
| 650 | } |
| 651 | |
| 652 | /***************************************************************************** |
| 653 | ** Server implementation |
| 654 | *****************************************************************************/ |
| 655 | |
| 656 | /* |
| 657 | ** Schema used by the email processing system. |
| 658 | */ |
| 659 | static const char zEmailSchema[] = |
| 660 | @ -- bulk storage is in this table. This table can store either |
| 661 | @ -- the body of email messages or transcripts of an smtp session. |
| 662 | @ CREATE TABLE IF NOT EXISTS repository.emailblob( |
| 663 | @ emailid INTEGER PRIMARY KEY AUTOINCREMENT, -- numeric idea for the entry |
| 664 | @ enref INT, -- Number of references to this blob |
| 665 | @ ets INT, -- Corresponding transcript, or NULL |
| 666 | @ etime INT, -- insertion time, secs since 1970 |
| 667 | @ esz INT, -- uncompressed content size |
| 668 | @ etxt TEXT -- content of this entry |
| 669 | @ ); |
| 670 | @ |
| 671 | @ -- One row for each mailbox entry. All users emails are stored in |
| 672 | @ -- this same table. |
| 673 | @ CREATE TABLE IF NOT EXISTS repository.emailbox( |
| 674 | @ ebid INTEGER PRIMARY KEY, -- Unique id for each mailbox entry |
| 675 | @ euser TEXT, -- User who received this email |
| 676 | @ edate INT, -- Date received. Seconds since 1970 |
| 677 | @ efrom TEXT, -- Who is the email from |
| 678 | @ emsgid INT, -- Raw email text |
| 679 | @ estate INT, -- 0: Unread, 1: read, 2: trash 3: sent |
| 680 | @ esubject TEXT, -- Subject line for display |
| 681 | @ etags TEXT -- zero or more tags |
| 682 | @ ); |
| 683 | @ |
| 684 | @ -- Information on how to deliver incoming email. |
| 685 | @ CREATE TABLE IF NOT EXISTS repository.emailroute( |
| 686 | @ eaddr TEXT PRIMARY KEY, -- Email address |
| 687 | @ epolicy TEXT -- How to handle email sent to this address |
| 688 | @ ) WITHOUT ROWID; |
| 689 | @ |
| 690 | @ -- Outgoing email queue |
| 691 | @ CREATE TABLE IF NOT EXISTS repository.emailoutq( |
| 692 | @ edomain TEXT, -- Destination domain. (ex: "fossil-scm.org") |
| 693 | @ efrom TEXT, -- Sender email address (envelope "from") |
| 694 | @ eto TEXT, -- Recipient email address (envelope "to") |
| 695 | @ emsgid INT, -- Message body in the emailblob table |
| 696 | @ ectime INT, -- Time enqueued. Seconds since 1970 |
| 697 | @ emtime INT, -- Time of last send attempt. Sec since 1970 |
| 698 | @ ensend INT, -- Number of send attempts |
| 699 | @ ets INT -- Transcript of last failed attempt |
| 700 | @ ); |
| 701 | @ |
| 702 | @ -- Triggers to automatically keep the emailblob.enref field up to date |
| 703 | @ -- as entries in the emailblob, emailbox, and emailoutq tables are |
| 704 | @ -- deleted. |
| 705 | @ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1 |
| 706 | @ AFTER DELETE ON emailblob BEGIN |
| 707 | @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets; |
| 708 | @ END; |
| 709 | @ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1 |
| 710 | @ AFTER DELETE ON emailbox BEGIN |
| 711 | @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid; |
| 712 | @ END; |
| 713 | @ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1 |
| 714 | @ AFTER DELETE ON emailoutq BEGIN |
| 715 | @ UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid); |
| 716 | @ END; |
| 717 | @ |
| 718 | @ -- An index on the emailblob entries which are unreferenced. |
| 719 | @ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref) |
| 720 | @ WHERE enref<=0; |
| 721 | ; |
| 722 | |
| 723 | /* |
| 724 | ** Code used to delete the email tables. |
| 725 | */ |
| 726 | static const char zEmailDrop[] = |
| 727 | @ DROP TABLE IF EXISTS emailblob; |
| 728 | @ DROP TABLE IF EXISTS emailbox; |
| 729 | @ DROP TABLE IF EXISTS emailroute; |
| 730 | @ DROP TABLE IF EXISTS emailqueue; |
| 731 | ; |
| 732 | |
| 733 | #if INTERFACE |
| 734 | /* |
| 735 | ** Mailbox message states |
| 736 | */ |
| 737 | #define MSG_UNREAD 0 |
| 738 | #define MSG_READ 1 |
| 739 | #define MSG_TRASH 2 |
| 740 | #endif /* INTERFACE */ |
| 741 | |
| 742 | |
| 743 | /* |
| 744 | ** Populate the schema of a database. |
| 745 | ** |
| 746 | ** eForce==0 Fast |
| 747 | ** eForce==1 Run CREATE TABLE statements every time |
| 748 | ** eForce==2 DROP then rerun CREATE TABLE |
| 749 | */ |
| 750 | void smtp_server_schema(int eForce){ |
| 751 | if( eForce==2 ){ |
| 752 | db_multi_exec(zEmailDrop/*works-like:""*/); |
| 753 | } |
| 754 | if( eForce==1 || !db_table_exists("repository","emailblob") ){ |
| 755 | db_multi_exec(zEmailSchema/*works-like:""*/); |
| 756 | } |
| 757 | } |
| 758 | |
| 759 | /* |
| 760 | ** WEBPAGE: setup_smtp |
| 761 | ** |
| 762 | ** Administrative page for configuring and controlling inbound email and |
| 763 | ** output email queuing. This page is available to administrators |
| 764 | ** only via the /Admin/EmailServer menu. |
| 765 | */ |
| 766 | void setup_smtp(void){ |
| 767 | Stmt q; |
| 768 | login_check_credentials(); |
| 769 | if( !g.perm.Setup ){ |
| 770 | login_needed(0); |
| 771 | return; |
| 772 | } |
| 773 | db_begin_transaction(); |
| 774 | style_set_current_feature("smtp"); |
| 775 | style_header("Email Server Setup"); |
| 776 | if( db_table_exists("repository","emailroute") ){ |
| 777 | style_submenu_element("emailblob table", "%R/emailblob"); |
| 778 | style_submenu_element("emailoutq table", "%R/emailoutq"); |
| 779 | db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1"); |
| 780 | }else{ |
| 781 | db_prepare(&q, "SELECT null, null WHERE false"); |
| 782 | } |
| 783 | @ <h1>Email Routing Table</h1> |
| 784 | @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0"> |
| 785 | @ <thead> |
| 786 | @ <tr> |
| 787 | @ <th>Email Address |
| 788 | @ <th>Routing |
| 789 | @ <th> |
| 790 | @ </tr> |
| 791 | @ </thead><tbody> |
| 792 | while( db_step(&q)==SQLITE_ROW ){ |
| 793 | const char *zEAddr = db_column_text(&q, 0); |
| 794 | const char *zEPolicy = db_column_text(&q, 1); |
| 795 | @ <tr> |
| 796 | @ <td valign="top">%h(zEAddr)</td> |
| 797 | @ <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td> |
| 798 | @ <td valign="top"><form method="POST" action="%R/setup_smtp_route"> |
| 799 | @ <input type="hidden" name="oaddr" value="%h(zEAddr)"> |
| 800 | @ <input type="submit" value="Edit"> |
| 801 | @ </form> |
| 802 | } |
| 803 | db_finalize(&q); |
| 804 | @ <tr> |
| 805 | @ <td colspan="3"> |
| 806 | @ <form method="POST" action="%R/setup_smtp_route"> |
| 807 | @ <input type="submit" value="New"> |
| 808 | @ ← Add a new email address |
| 809 | @ </form> |
| 810 | @ </table> |
| 811 | style_finish_page(); |
| 812 | db_end_transaction(0); |
| 813 | } |
| 814 | |
| 815 | /* |
| 816 | ** WEBPAGE: setup_smtp_route |
| 817 | ** |
| 818 | ** Edit a single entry in the emailroute table. |
| 819 | ** Query parameters: |
| 820 | ** |
| 821 | ** eaddr=ADDR ADDR is the email address as edited. |
| 822 | ** |
| 823 | ** oaddr=ADDR The original email address prior to editing. |
| 824 | ** Omit to add a new address. |
| 825 | ** |
| 826 | ** epolicy=TXT The routing policy. |
| 827 | */ |
| 828 | void setup_smtp_route(void){ |
| 829 | char *zEAddr = PT("eaddr"); /* new email address */ |
| 830 | char *zEPolicy = PT("epolicy"); /* new routing policy */ |
| 831 | char *zOAddr = PT("oaddr"); /* original email address */ |
| 832 | char *zErr = 0; |
| 833 | int iErr = 0; |
| 834 | login_check_credentials(); |
| 835 | if( !g.perm.Setup ){ |
| 836 | login_needed(0); |
| 837 | return; |
| 838 | } |
| 839 | style_set_current_feature("smtp"); |
| 840 | style_header("Email Route Editor"); |
| 841 | |
| 842 | if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){ |
| 843 | smtp_server_schema(0); |
| 844 | if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){ |
| 845 | /* New or changed email address */ |
| 846 | if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){ |
| 847 | iErr = 1; |
| 848 | zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr); |
| 849 | goto smtp_route_edit; |
| 850 | } |
| 851 | if( zEPolicy[0]==0 ){ |
| 852 | iErr = 2; |
| 853 | zErr = mprintf("empty route"); |
| 854 | goto smtp_route_edit; |
| 855 | } |
| 856 | } |
| 857 | /* If the email address has changed, or if the new policy is blank, |
| 858 | ** delete the old address and route information |
| 859 | */ |
| 860 | db_begin_transaction(); |
| 861 | if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){ |
| 862 | db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr); |
| 863 | } |
| 864 | if( zEPolicy[0] ){ |
| 865 | /* Insert the new address and route */ |
| 866 | db_multi_exec( |
| 867 | "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)", |
| 868 | zEAddr, zEPolicy |
| 869 | ); |
| 870 | } |
| 871 | db_end_transaction(0); |
| 872 | cgi_redirectf("%R/setup_smtp"); |
| 873 | } |
| 874 | if( P("cancel")!=0 ){ |
| 875 | cgi_redirectf("%R/setup_smtp"); |
| 876 | } |
| 877 | |
| 878 | smtp_route_edit: |
| 879 | if( zEAddr==0 ) zEAddr = zOAddr; |
| 880 | if( zEPolicy==0 && db_table_exists("repository","emailroute") ){ |
| 881 | zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q", |
| 882 | zEAddr); |
| 883 | } |
| 884 | if( zEPolicy==0 ) zEPolicy = ""; |
| 885 | @ <form method="POST" action="%R/setup_smtp_route"> |
| 886 | if( zOAddr ){ |
| 887 | @ <input type="hidden" name="oaddr" value="%h(zOAddr)"> |
| 888 | } |
| 889 | @ <table class="label-value"> |
| 890 | @ <tr> |
| 891 | @ <th>Email Address:</th> |
| 892 | @ <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)"> |
| 893 | if( iErr==1 ){ |
| 894 | @ <td><span class="generalError">← %z(zErr)</span> |
| 895 | } |
| 896 | @ </tr> |
| 897 | if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){ |
| 898 | @ <tr> |
| 899 | @ <th>Original Address:</th> |
| 900 | @ <td>%h(zOAddr) |
| 901 | @ </tr> |
| 902 | } |
| 903 | @ <tr> |
| 904 | @ <th>Routing:</th> |
| 905 | @ <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea> |
| 906 | if( iErr==2 ){ |
| 907 | @ <td valign="top"><span class="generalError">← %z(zErr)</span> |
| 908 | } |
| 909 | @ </tr> |
| 910 | @ <tr> |
| 911 | @ <td> |
| 912 | @ <td><input type="submit" name="edit" value="Apply"> |
| 913 | @ <input type="submit" name="cancel" value="Cancel"> |
| 914 | @ </tr> |
| 915 | @ </table> |
| 916 | @ <hr> |
| 917 | @ <h1>Instructions</h1> |
| 918 | @ |
| 919 | @ <p>The "Routing" field consists of zero or more lines where each |
| 920 | @ line is an "action" followed by an "argument". Available actions: |
| 921 | @ <ul> |
| 922 | @ <li><p><b>forward</b> <i>email-address</i> |
| 923 | @ <p>Forward the message to <i>email-address</i>. |
| 924 | @ <li><p><b>mbox</b> <i>login-name</i> |
| 925 | @ <p>Store the message in the local mailbox for the user |
| 926 | @ with USER.LOGIN=<i>login-name</i>. |
| 927 | @ </ul> |
| 928 | @ |
| 929 | @ <p>To delete a route → erase all text from the "Routing" field then |
| 930 | @ press the "Apply" button. |
| 931 | style_finish_page(); |
| 932 | } |
| 933 | |
| 934 | #if LOCAL_INTERFACE |
| 935 | /* |
| 936 | ** State information for the server |
| 937 | */ |
| 938 | struct SmtpServer { |
| 939 | sqlite3_int64 idTranscript; /* Transcript ID number */ |
| 940 | sqlite3_int64 idMsg; /* Message ID number */ |
| 941 | const char *zIpAddr; /* Remote IP address */ |
| 942 | char *zEhlo; /* Client domain on the EHLO line */ |
| 943 | char *zFrom; /* MAIL FROM: argument */ |
| 944 | int nTo; /* Number of RCPT TO: lines seen */ |
| 945 | struct SmtpTo { |
| 946 | char *z; /* Address in each RCPT TO line */ |
| 947 | int okRemote; /* zTo can be in another domain */ |
| 948 | } *aTo; |
| 949 | u32 srvrFlags; /* Control flags */ |
| 950 | int nEts; /* Number of references to the transcript */ |
| 951 | int nRef; /* Number of references to idMsg */ |
| 952 | Blob msg; /* Content following DATA */ |
| 953 | Blob transcript; /* Session transcript */ |
| 954 | }; |
| 955 | |
| 956 | #define SMTPSRV_CLEAR_MSG 1 /* smtp_server_clear() last message only */ |
| 957 | #define SMTPSRV_CLEAR_ALL 2 /* smtp_server_clear() everything */ |
| 958 | #define SMTPSRV_LOG 0x001 /* Record a transcript of the interaction */ |
| 959 | #define SMTPSRV_STDERR 0x002 /* Transcription written to stderr */ |
| 960 | #define SMTPSRV_DRYRUN 0x004 /* Do not record anything in database */ |
| 961 | |
| 962 | #endif /* LOCAL_INTERFACE */ |
| 963 | |
| 964 | /* |
| 965 | ** Clear the SmtpServer object. Deallocate resources. |
| 966 | ** How much to clear depends on eHowMuch |
| 967 | */ |
| 968 | static void smtp_server_clear(SmtpServer *p, int eHowMuch){ |
| 969 | int i; |
| 970 | if( eHowMuch>=SMTPSRV_CLEAR_MSG ){ |
| 971 | fossil_free(p->zFrom); |
| 972 | p->zFrom = 0; |
| 973 | for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z); |
| 974 | fossil_free(p->aTo); |
| 975 | p->aTo = 0; |
| 976 | p->nTo = 0; |
| 977 | blob_reset(&p->msg); |
| 978 | p->idMsg = 0; |
| 979 | } |
| 980 | if( eHowMuch>=SMTPSRV_CLEAR_ALL ){ |
| 981 | blob_reset(&p->transcript); |
| 982 | p->idTranscript = 0; |
| 983 | fossil_free(p->zEhlo); |
| 984 | p->zEhlo = 0; |
| 985 | } |
| 986 | } |
| 987 | |
| 988 | /* |
| 989 | ** Turn raw memory into an SmtpServer object. |
| 990 | */ |
| 991 | static void smtp_server_init(SmtpServer *p){ |
| 992 | memset(p, 0, sizeof(*p)); |
| 993 | blob_init(&p->msg, 0, 0); |
| 994 | blob_init(&p->transcript, 0, 0); |
| 995 | } |
| 996 | |
| 997 | /* |
| 998 | ** Append a new TO entry to the SmtpServer object. Do not do the |
| 999 | ** append if the same entry is already on the list. |
| 1000 | ** |
| 1001 | ** The zAddr argument is obtained from fossil_malloc(). This |
| 1002 | ** routine assumes ownership of the allocation. |
| 1003 | */ |
| 1004 | static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){ |
| 1005 | int i; |
| 1006 | for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); } |
| 1007 | for(i=0; i<p->nTo; i++){ |
| 1008 | if( strcmp(zAddr, p->aTo[i].z)==0 ){ |
| 1009 | fossil_free(zAddr); |
| 1010 | if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote; |
| 1011 | return; |
| 1012 | } |
| 1013 | } |
| 1014 | p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0])); |
| 1015 | p->aTo[p->nTo].z = zAddr; |
| 1016 | p->aTo[p->nTo].okRemote = okRemote; |
| 1017 | p->nTo++; |
| 1018 | } |
| 1019 | |
| 1020 | /* |
| 1021 | ** Send a single line of output from the server to the client. |
| 1022 | */ |
| 1023 | static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){ |
| 1024 | Blob b = empty_blob; |
| 1025 | va_list ap; |
| 1026 | char *z; |
| 1027 | int n; |
| 1028 | va_start(ap, zFormat); |
| 1029 | blob_vappendf(&b, zFormat, ap); |
| 1030 | va_end(ap); |
| 1031 | z = blob_buffer(&b); |
| 1032 | n = blob_size(&b); |
| 1033 | assert( n>=2 ); |
| 1034 | assert( z[n-1]=='\n' ); |
| 1035 | assert( z[n-2]=='\r' ); |
| 1036 | if( p->srvrFlags & SMTPSRV_LOG ){ |
| 1037 | blob_appendf(&p->transcript, "S: %.*s\n", n-2, z); |
| 1038 | } |
| 1039 | if( p->srvrFlags & SMTPSRV_STDERR ){ |
| 1040 | fprintf(stderr, "S: %.*s\n", n-2, z); |
| 1041 | } |
| 1042 | fwrite(z, n, 1, stdout); |
| 1043 | fflush(stdout); |
| 1044 | blob_reset(&b); |
| 1045 | } |
| 1046 | |
| 1047 | /* |
| 1048 | ** Read a single line from the client. |
| 1049 | */ |
| 1050 | static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){ |
| 1051 | int rc = fgets(aBuf, nBuf, stdin)!=0; |
| 1052 | if( rc ){ |
| 1053 | if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){ |
| 1054 | blob_appendf(&p->transcript, "C: %s", aBuf); |
| 1055 | } |
| 1056 | if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){ |
| 1057 | fprintf(stderr, "C: %s", aBuf); |
| 1058 | } |
| 1059 | } |
| 1060 | return rc; |
| 1061 | } |
| 1062 | |
| 1063 | /* |
| 1064 | ** RFC-5321 requires certain content be prepended to an email header |
| 1065 | ** as that email is received. |
| 1066 | */ |
| 1067 | static void smtp_server_prepend_header_lines(SmtpServer *p){ |
| 1068 | blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr); |
| 1069 | } |
| 1070 | |
| 1071 | /* |
| 1072 | ** Capture the incoming email data into the p->msg blob. Dequote |
| 1073 | ** lines of "..\r\n" into just ".\r\n". |
| 1074 | */ |
| 1075 | static void smtp_server_capture_data(SmtpServer *p, char *z, int n){ |
| 1076 | int nLine = 0; |
| 1077 | while( fgets(z, n, stdin) ){ |
| 1078 | if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break; |
| 1079 | nLine++; |
| 1080 | if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){ |
| 1081 | memmove(z, z+1, 4); |
| 1082 | } |
| 1083 | blob_append(&p->msg, z, -1); |
| 1084 | } |
| 1085 | if( p->srvrFlags & SMTPSRV_LOG ){ |
| 1086 | blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n", |
| 1087 | nLine, blob_size(&p->msg)); |
| 1088 | } |
| 1089 | if( p->srvrFlags & SMTPSRV_STDERR ){ |
| 1090 | fprintf(stderr, "C: # %d lines, %d bytes of content\n", |
| 1091 | nLine, blob_size(&p->msg)); |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | /* |
| 1096 | ** Send an email to a single email addess that is registered with |
| 1097 | ** this system, according to the instructions in emailroute. If |
| 1098 | ** zAddr is not in the emailroute table, then this routine is a |
| 1099 | ** no-op. Or if zAddr has already been processed, then this |
| 1100 | ** routine is a no-op. |
| 1101 | */ |
| 1102 | static void smtp_server_send_one_user( |
| 1103 | SmtpServer *p, /* The current inbound email */ |
| 1104 | const char *zAddr, /* Who to forward this to */ |
| 1105 | int okRemote /* True if ok to foward to another domain */ |
| 1106 | ){ |
| 1107 | char *zPolicy; |
| 1108 | Blob policy, line, token, tail; |
| 1109 | |
| 1110 | zPolicy = db_text(0, |
| 1111 | "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr); |
| 1112 | if( zPolicy==0 ){ |
| 1113 | if( okRemote ){ |
| 1114 | int i; |
| 1115 | for(i=0; zAddr[i] && zAddr[i]!='@'; i++){} |
| 1116 | if( zAddr[i]=='@' && zAddr[i+1]!=0 ){ |
| 1117 | db_multi_exec( |
| 1118 | "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime," |
| 1119 | "emtime,ensend)" |
| 1120 | "VALUES(%Q,%Q,%Q,%lld,now(),0,0)", |
| 1121 | zAddr+i+1, p->zFrom, zAddr, p->idMsg |
| 1122 | ); |
| 1123 | p->nRef++; |
| 1124 | } |
| 1125 | } |
| 1126 | return; |
| 1127 | } |
| 1128 | blob_init(&policy, zPolicy, -1); |
| 1129 | while( blob_line(&policy, &line) ){ |
| 1130 | blob_trim(&line); |
| 1131 | blob_token(&line, &token); |
| 1132 | blob_tail(&line, &tail); |
| 1133 | if( blob_size(&tail)==0 ) continue; |
| 1134 | if( blob_eq_str(&token, "mbox", 4) ){ |
| 1135 | Blob subj; |
| 1136 | email_header_value(&p->msg, "subject", &subj); |
| 1137 | db_multi_exec( |
| 1138 | "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)" |
| 1139 | " VALUES(%Q,now(),%Q,%lld,0,%Q)", |
| 1140 | blob_str(&tail), p->zFrom, p->idMsg, |
| 1141 | blob_str(&subj) |
| 1142 | ); |
| 1143 | blob_reset(&subj); |
| 1144 | p->nRef++; |
| 1145 | } |
| 1146 | if( blob_eq_str(&token, "forward", 7) ){ |
| 1147 | smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1); |
| 1148 | } |
| 1149 | blob_reset(&tail); |
| 1150 | } |
| 1151 | } |
| 1152 | |
| 1153 | /* |
| 1154 | ** The SmtpServer object contains a complete incoming email. |
| 1155 | ** Add this email to the database. |
| 1156 | */ |
| 1157 | static void smtp_server_route_incoming(SmtpServer *p, int bFinish){ |
| 1158 | Stmt s; |
| 1159 | int i; |
| 1160 | int nEtsStart = p->nEts; |
| 1161 | if( p->zFrom |
| 1162 | && p->nTo |
| 1163 | && blob_size(&p->msg) |
| 1164 | && (p->srvrFlags & SMTPSRV_DRYRUN)==0 |
| 1165 | ){ |
| 1166 | db_begin_write(); |
| 1167 | if( p->idTranscript==0 ) smtp_server_schema(0); |
| 1168 | p->nRef = 0; |
| 1169 | db_prepare(&s, |
| 1170 | "INSERT INTO emailblob(ets,etime,etxt,enref,esz)" |
| 1171 | " VALUES(:ets,now(),compress(:etxt),0,:esz)" |
| 1172 | ); |
| 1173 | p->nEts++; |
| 1174 | if( !bFinish && p->idTranscript==0 ){ |
| 1175 | db_bind_null(&s, ":ets"); |
| 1176 | db_bind_null(&s, ":etxt"); |
| 1177 | db_bind_null(&s, ":esz"); |
| 1178 | db_step(&s); |
| 1179 | db_reset(&s); |
| 1180 | p->idTranscript = db_last_insert_rowid(); |
| 1181 | }else if( bFinish ){ |
| 1182 | if( p->idTranscript ){ |
| 1183 | db_multi_exec( |
| 1184 | "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d" |
| 1185 | " WHERE emailid=%lld", |
| 1186 | blob_str(&p->transcript), p->nEts, blob_size(&p->transcript), |
| 1187 | p->idTranscript); |
| 1188 | }else{ |
| 1189 | db_bind_null(&s, ":ets"); |
| 1190 | db_bind_str(&s, ":etxt", &p->transcript); |
| 1191 | db_bind_int(&s, ":esz", blob_size(&p->transcript)); |
| 1192 | db_step(&s); |
| 1193 | db_reset(&s); |
| 1194 | p->idTranscript = db_last_insert_rowid(); |
| 1195 | db_multi_exec( |
| 1196 | "UPDATE emailblob SET enref=%d WHERE emailid=%lld", |
| 1197 | p->nEts, p->idTranscript); |
| 1198 | } |
| 1199 | /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n", |
| 1200 | ** p->idTranscript, p->nEts); */ |
| 1201 | } |
| 1202 | db_bind_int64(&s, ":ets", p->idTranscript); |
| 1203 | db_bind_str(&s, ":etxt", &p->msg); |
| 1204 | db_bind_int(&s, ":esz", blob_size(&p->msg)); |
| 1205 | db_step(&s); |
| 1206 | db_finalize(&s); |
| 1207 | p->idMsg = db_last_insert_rowid(); |
| 1208 | |
| 1209 | /* make entries in emailbox and emailoutq */ |
| 1210 | for(i=0; i<p->nTo; i++){ |
| 1211 | int okRemote = p->aTo[i].okRemote; |
| 1212 | p->aTo[i].okRemote = 1; |
| 1213 | smtp_server_send_one_user(p, p->aTo[i].z, okRemote); |
| 1214 | } |
| 1215 | |
| 1216 | /* Fix up the emailblob.enref field of the email message body */ |
| 1217 | if( p->nRef ){ |
| 1218 | db_multi_exec( |
| 1219 | "UPDATE emailblob SET enref=%d WHERE emailid=%lld", |
| 1220 | p->nRef, p->idMsg |
| 1221 | ); |
| 1222 | }else{ |
| 1223 | db_multi_exec( |
| 1224 | "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg |
| 1225 | ); |
| 1226 | p->nEts = nEtsStart; |
| 1227 | } |
| 1228 | |
| 1229 | /* Clean out legacy entries */ |
| 1230 | if( bFinish ){ |
| 1231 | db_multi_exec("DELETE FROM emailblob WHERE enref<=0"); |
| 1232 | } |
| 1233 | |
| 1234 | /* Finish the transaction after all changes are implemented */ |
| 1235 | db_commit_transaction(); |
| 1236 | } |
| 1237 | smtp_server_clear(p, SMTPSRV_CLEAR_MSG); |
| 1238 | } |
| 1239 | |
| 1240 | /* |
| 1241 | ** Remove stale content from the emailblob table. |
| 1242 | */ |
| 1243 | int smtp_cleanup(void){ |
| 1244 | int nAction = 0; |
| 1245 | if( db_table_exists("repository","emailblob") ){ |
| 1246 | db_begin_transaction(); |
| 1247 | db_multi_exec( |
| 1248 | "UPDATE emailblob SET ets=NULL WHERE enref<=0;" |
| 1249 | "DELETE FROM emailblob WHERE enref<=0;" |
| 1250 | ); |
| 1251 | nAction = db_changes(); |
| 1252 | db_end_transaction(0); |
| 1253 | } |
| 1254 | return nAction; |
| 1255 | } |
| 1256 | |
| 1257 | /* |
| 1258 | ** COMMAND: test-emailblob-refcheck |
| 1259 | ** |
| 1260 | ** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean] |
| 1261 | ** |
| 1262 | ** Verify that the emailblob.enref field is correct. Report any errors. |
| 1263 | ** Use the --repair command to fix up the enref field. The --full option |
| 1264 | ** gives a full report showing the enref value on all entries in the |
| 1265 | ** emailblob table. If the --clean flags is used together with --repair, |
| 1266 | ** then emailblob table entires with enref==0 are removed. |
| 1267 | */ |
| 1268 | void test_refcheck_emailblob(void){ |
| 1269 | int doRepair; |
| 1270 | int fullReport; |
| 1271 | int doClean; |
| 1272 | Blob sql; |
| 1273 | Stmt q; |
| 1274 | int nErr = 0; |
| 1275 | db_find_and_open_repository(0, 0); |
| 1276 | fullReport = find_option("full",0,0)!=0; |
| 1277 | doRepair = find_option("repair",0,0)!=0; |
| 1278 | doClean = find_option("clean",0,0)!=0; |
| 1279 | verify_all_options(); |
| 1280 | if( !db_table_exists("repository","emailblob") ){ |
| 1281 | fossil_print("emailblob table is not configured - nothing to check\n"); |
| 1282 | return; |
| 1283 | } |
| 1284 | db_multi_exec( |
| 1285 | "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);" |
| 1286 | "INSERT INTO refcnt SELECT ets, count(*) FROM (" |
| 1287 | " SELECT ets FROM emailblob" |
| 1288 | " UNION ALL" |
| 1289 | " SELECT emsgid FROM emailbox" |
| 1290 | " UNION ALL" |
| 1291 | " SELECT emsgid FROM emailoutq" |
| 1292 | ") WHERE ets IS NOT NULL GROUP BY 1;" |
| 1293 | "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;" |
| 1294 | ); |
| 1295 | if( doRepair ){ |
| 1296 | db_multi_exec( |
| 1297 | "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)" |
| 1298 | ); |
| 1299 | if( doClean ){ |
| 1300 | smtp_cleanup(); |
| 1301 | } |
| 1302 | } |
| 1303 | blob_init(&sql, 0, 0); |
| 1304 | blob_append_sql(&sql, |
| 1305 | "SELECT a.emailid, a.enref, b.n" |
| 1306 | " FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id" |
| 1307 | ); |
| 1308 | if( !fullReport ){ |
| 1309 | blob_append_sql(&sql, " WHERE a.enref!=b.n"); |
| 1310 | } |
| 1311 | db_prepare_blob(&q, &sql); |
| 1312 | blob_reset(&sql); |
| 1313 | while( db_step(&q)==SQLITE_ROW ){ |
| 1314 | sqlite3_int64 id = db_column_int64(&q,0); |
| 1315 | int n1 = db_column_int(&q, 1); |
| 1316 | int n2 = db_column_int(&q, 2); |
| 1317 | if( n1!=n2 ) nErr++; |
| 1318 | fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : ""); |
| 1319 | } |
| 1320 | db_finalize(&q); |
| 1321 | if( nErr ){ |
| 1322 | fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr); |
| 1323 | } |
| 1324 | } |
| 1325 | |
| 1326 | |
| 1327 | /* |
| 1328 | ** COMMAND: smtpd* |
| 1329 | ** |
| 1330 | ** Usage: %fossil smtpd [OPTIONS] REPOSITORY |
| 1331 | ** |
| 1332 | ** Begin a SMTP conversation with a client using stdin/stdout. The |
| 1333 | ** received email is stored in REPOSITORY. |
| 1334 | ** |
| 1335 | ** Options: |
| 1336 | ** |
| 1337 | ** --dryrun Do not record any emails in the database |
| 1338 | ** |
| 1339 | ** --trace Print a transcript of the conversation on stderr |
| 1340 | ** for debugging and analysis |
| 1341 | ** |
| 1342 | ** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR |
| 1343 | ** is the name of an environment variable, the address |
| 1344 | ** is taken from that environment variable. |
| 1345 | */ |
| 1346 | void smtp_server(void){ |
| 1347 | char *zDbName; |
| 1348 | const char *zDomain; |
| 1349 | SmtpServer x; |
| 1350 | char z[5000]; |
| 1351 | |
| 1352 | smtp_server_init(&x); |
| 1353 | zDomain = find_option("domain",0,1); |
| 1354 | if( zDomain==0 ) zDomain = ""; |
| 1355 | x.srvrFlags = SMTPSRV_LOG; |
| 1356 | if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; |
| 1357 | if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; |
| 1358 | x.zIpAddr = find_option("ipaddr",0,1); |
| 1359 | if( x.zIpAddr ){ |
| 1360 | const char *zNew = fossil_getenv(x.zIpAddr); |
| 1361 | if( zNew && zNew[0] ) x.zIpAddr = zNew; |
| 1362 | } |
| 1363 | if( x.zIpAddr==0 ){ |
| 1364 | x.zIpAddr = cgi_remote_ip(0); |
| 1365 | if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?"; |
| 1366 | } |
| 1367 | verify_all_options(); |
| 1368 | if( g.argc!=3 ) usage("DBNAME"); |
| 1369 | zDbName = g.argv[2]; |
| 1370 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1371 | db_open_repository(zDbName); |
| 1372 | add_content_sql_commands(g.db); |
| 1373 | smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n", |
| 1374 | zDomain, MANIFEST_VERSION); |
| 1375 | while( smtp_server_gets(&x, z, sizeof(z)) ){ |
| 1376 | if( strncmp(z, "EHLO", 4)==0 && fossil_isspace(z[4]) ){ |
| 1377 | smtp_server_send(&x, "250 ok\r\n"); |
| 1378 | }else |
| 1379 | if( strncmp(z, "HELO", 4)==0 && fossil_isspace(z[4]) ){ |
| 1380 | smtp_server_send(&x, "250 ok\r\n"); |
| 1381 | }else |
| 1382 | if( strncmp(z, "MAIL FROM:<", 11)==0 ){ |
| 1383 | smtp_server_route_incoming(&x, 0); |
| 1384 | smtp_server_clear(&x, SMTPSRV_CLEAR_MSG); |
| 1385 | x.zFrom = email_copy_addr(z+11,'>'); |
| 1386 | if( x.zFrom==0 ){ |
| 1387 | smtp_server_send(&x, "500 unacceptable email address\r\n"); |
| 1388 | }else{ |
| 1389 | smtp_server_send(&x, "250 ok\r\n"); |
| 1390 | } |
| 1391 | }else |
| 1392 | if( strncmp(z, "RCPT TO:<", 9)==0 ){ |
| 1393 | char *zAddr; |
| 1394 | if( x.zFrom==0 ){ |
| 1395 | smtp_server_send(&x, "500 missing MAIL FROM\r\n"); |
| 1396 | continue; |
| 1397 | } |
| 1398 | zAddr = email_copy_addr(z+9, '>'); |
| 1399 | if( zAddr==0 ){ |
| 1400 | smtp_server_send(&x, "505 no such user\r\n"); |
| 1401 | continue; |
| 1402 | } |
| 1403 | smtp_append_to(&x, zAddr, 0); |
| 1404 | if( x.nTo>=100 ){ |
| 1405 | smtp_server_send(&x, "452 too many recipients\r\n"); |
| 1406 | continue; |
| 1407 | } |
| 1408 | smtp_server_send(&x, "250 ok\r\n"); |
| 1409 | }else |
| 1410 | if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){ |
| 1411 | if( x.zFrom==0 || x.nTo==0 ){ |
| 1412 | smtp_server_send(&x, "500 missing RCPT TO\r\n"); |
| 1413 | continue; |
| 1414 | } |
| 1415 | smtp_server_send(&x, "354 ready\r\n"); |
| 1416 | smtp_server_prepend_header_lines(&x); |
| 1417 | smtp_server_capture_data(&x, z, sizeof(z)); |
| 1418 | smtp_server_send(&x, "250 ok\r\n"); |
| 1419 | }else |
| 1420 | if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ |
| 1421 | smtp_server_route_incoming(&x, 1); |
| 1422 | smtp_server_send(&x, "221 closing connection\r\n"); |
| 1423 | break; |
| 1424 | }else |
| 1425 | { |
| 1426 | smtp_server_send(&x, "500 unknown command\r\n"); |
| 1427 | } |
| 1428 | } |
| 1429 | smtp_server_clear(&x, SMTPSRV_CLEAR_ALL); |
| 1430 | } |
| 1431 | |
| 1432 | /* |
| 1433 | ** Zero-terminate the argument. Return a pointer the start of the |
| 1434 | ** next argument, or to NULL if there are no more arguments. |
| 1435 | */ |
| 1436 | static char *pop3d_arg(char *z){ |
| 1437 | if( z[0]==0 || fossil_isspace(z[0]) ){ |
| 1438 | return 0; |
| 1439 | } |
| 1440 | z++; |
| 1441 | while( z[0] && !fossil_isspace(z[0]) ){ z++; } |
| 1442 | if( z[0]==0 ) return 0; |
| 1443 | z[0] = 0; |
| 1444 | z++; |
| 1445 | if( z[0]==0 || fossil_isspace(z[0]) ) return 0; |
| 1446 | return z; |
| 1447 | } |
| 1448 | |
| 1449 | /* |
| 1450 | ** Write formatted output back to the pop3 client, and also to the |
| 1451 | ** log file, if there is a log file. |
| 1452 | */ |
| 1453 | static void pop3_print(FILE *pLog, const char *zFormat, ...){ |
| 1454 | va_list ap; |
| 1455 | char zLine[500]; |
| 1456 | va_start(ap, zFormat); |
| 1457 | sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap); |
| 1458 | va_end(ap); |
| 1459 | printf("%s\r\n", zLine); |
| 1460 | fflush(stdout); |
| 1461 | if( pLog ) fprintf(pLog, "S: %s\n", zLine); |
| 1462 | } |
| 1463 | |
| 1464 | /* |
| 1465 | ** Try to log in for zUser and zPass. |
| 1466 | ** |
| 1467 | ** zUser can either point to a Fossil user name or to an email address |
| 1468 | ** found in the user table's info field, in angle brackets. |
| 1469 | */ |
| 1470 | static int pop3_login(const char *zUser, char *zPass){ |
| 1471 | return login_search_uid(&zUser, zPass) != 0; |
| 1472 | } |
| 1473 | |
| 1474 | /* |
| 1475 | ** COMMAND: pop3d* |
| 1476 | ** |
| 1477 | ** Usage: %fossil pop3d [OPTIONS] REPOSITORY |
| 1478 | ** |
| 1479 | ** Begin a POP3 conversation with a client using stdin/stdout using |
| 1480 | ** the mailboxes stored in REPOSITORY. |
| 1481 | ** |
| 1482 | ** If launched as root, the process first enters a chroot jail using |
| 1483 | ** the directory of REPOSITORY as root, then drops all privileges and |
| 1484 | ** assumes the user and group of REPOSITORY before reading any content |
| 1485 | ** off of the wire. |
| 1486 | ** |
| 1487 | ** --logdir DIR Each pop3d session creates a new logfile |
| 1488 | ** in the directory DIR and records a transcript |
| 1489 | ** of the session there. The logfile is opened |
| 1490 | ** before entering the chroot jail. |
| 1491 | */ |
| 1492 | void pop3d_command(void){ |
| 1493 | char *zDbName; |
| 1494 | char *zA1, *zA2, *zCmd, *z; |
| 1495 | int inAuth = 1; |
| 1496 | int i; |
| 1497 | FILE *pLog = 0; |
| 1498 | const char *zDir; |
| 1499 | Stmt q; |
| 1500 | char zIn[1000]; |
| 1501 | char zUser[100]; |
| 1502 | zDir = find_option("logdir",0,1); |
| 1503 | if( zDir ){ |
| 1504 | char *zFile = file_time_tempname(zDir, ".txt"); |
| 1505 | pLog = fossil_fopen(zFile, "w"); |
| 1506 | fossil_free(zFile); |
| 1507 | } |
| 1508 | verify_all_options(); |
| 1509 | if( g.argc!=3 ) usage("DBNAME"); |
| 1510 | zDbName = g.argv[2]; |
| 1511 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1512 | db_open_repository(zDbName); |
| 1513 | add_content_sql_commands(g.db); |
| 1514 | pop3_print(pLog, "+OK POP3 server ready"); |
| 1515 | while( fgets(zIn, sizeof(zIn), stdin) ){ |
| 1516 | if( pLog ) fprintf(pLog, "C: %s", zIn); |
| 1517 | zCmd = zIn; |
| 1518 | zA1 = pop3d_arg(zCmd); |
| 1519 | zA2 = zA1 ? pop3d_arg(zA1) : 0; |
| 1520 | for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); } |
| 1521 | if( strcmp(zCmd,"quit")==0 ){ |
| 1522 | if( !inAuth ){ |
| 1523 | db_multi_exec( |
| 1524 | "UPDATE emailbox SET estate=2" |
| 1525 | " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);" |
| 1526 | ); |
| 1527 | } |
| 1528 | pop3_print(pLog, "+OK"); |
| 1529 | break; |
| 1530 | } |
| 1531 | if( strcmp(zCmd,"capa")==0 ){ |
| 1532 | static const char *const azCap[] = { |
| 1533 | "TOP", "USER", "UIDL", |
| 1534 | }; |
| 1535 | int i; |
| 1536 | pop3_print(pLog, "+OK"); |
| 1537 | for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){ |
| 1538 | pop3_print(pLog, "%s", azCap[i]); |
| 1539 | } |
| 1540 | pop3_print(pLog, "."); |
| 1541 | continue; |
| 1542 | } |
| 1543 | if( inAuth ){ |
| 1544 | if( strcmp(zCmd,"user")==0 ){ |
| 1545 | if( zA1==0 || zA2!=0 ) goto cmd_error; |
| 1546 | sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1); |
| 1547 | goto cmd_ok; |
| 1548 | } |
| 1549 | if( strcmp(zCmd,"pass")==0 ){ |
| 1550 | if( zA1==0 || zA2!=0 ) goto cmd_error; |
| 1551 | if( pop3_login(zUser,zA1)==0 ){ |
| 1552 | goto cmd_error; |
| 1553 | }else{ |
| 1554 | inAuth = 0; |
| 1555 | db_multi_exec( |
| 1556 | "CREATE TEMP TABLE pop3(" |
| 1557 | " id INTEGER PRIMARY KEY," |
| 1558 | " emailid INT," |
| 1559 | " ebid INT," |
| 1560 | " isDel INT," |
| 1561 | " esz INT" |
| 1562 | ");" |
| 1563 | "INSERT INTO pop3(id,emailid,ebid,isDel,esz)" |
| 1564 | " SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox" |
| 1565 | " WHERE emailid=emsgid AND euser=%Q AND estate<=1" |
| 1566 | " ORDER BY edate;", |
| 1567 | zUser |
| 1568 | ); |
| 1569 | goto cmd_ok; |
| 1570 | } |
| 1571 | } |
| 1572 | /* Fossil cannot process APOP since the users clear-text password is |
| 1573 | ** unknown. */ |
| 1574 | goto cmd_error; |
| 1575 | }else{ |
| 1576 | if( strcmp(zCmd,"stat")==0 ){ |
| 1577 | db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel"); |
| 1578 | if( db_step(&q)==SQLITE_ROW ){ |
| 1579 | pop3_print(pLog, "+OK %d %d", |
| 1580 | db_column_int(&q,0), db_column_int(&q,1)); |
| 1581 | }else{ |
| 1582 | pop3_print(pLog,"-ERR"); |
| 1583 | } |
| 1584 | db_finalize(&q); |
| 1585 | continue; |
| 1586 | } |
| 1587 | if( strcmp(zCmd,"list")==0 ){ |
| 1588 | if( zA1 ){ |
| 1589 | db_prepare(&q, "SELECT id, esz FROM pop3" |
| 1590 | " WHERE id=%d AND NOT isDel", atoi(zA1)); |
| 1591 | if( db_step(&q)==SQLITE_ROW ){ |
| 1592 | pop3_print(pLog, "+OK %d %d", |
| 1593 | db_column_int(&q,0), db_column_int(&q,1)); |
| 1594 | }else{ |
| 1595 | pop3_print(pLog, "-ERR"); |
| 1596 | } |
| 1597 | }else{ |
| 1598 | pop3_print(pLog, "+OK"); |
| 1599 | db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel"); |
| 1600 | while( db_step(&q)==SQLITE_ROW ){ |
| 1601 | pop3_print(pLog, "%d %d", |
| 1602 | db_column_int(&q,0), db_column_int(&q,1)); |
| 1603 | } |
| 1604 | pop3_print(pLog, "."); |
| 1605 | } |
| 1606 | db_finalize(&q); |
| 1607 | continue; |
| 1608 | } |
| 1609 | if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){ |
| 1610 | Blob all, line; |
| 1611 | int nLine = 0; |
| 1612 | int iLimit; |
| 1613 | int hdrPending = 1; |
| 1614 | if( zA1==0 ) goto cmd_error; |
| 1615 | iLimit = zA2 ? atoi(zA2) : 2147483647; |
| 1616 | if( iLimit<0 ) goto cmd_error; |
| 1617 | z = db_text(0, "SELECT decompress(emailblob.etxt) " |
| 1618 | " FROM emailblob, pop3" |
| 1619 | " WHERE emailblob.emailid=pop3.emailid" |
| 1620 | " AND pop3.id=%d AND NOT pop3.isDel", |
| 1621 | atoi(zA1)); |
| 1622 | if( z==0 ) goto cmd_error; |
| 1623 | pop3_print(pLog, "+OK"); |
| 1624 | blob_init(&all, z, -1); |
| 1625 | while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){ |
| 1626 | char c = blob_buffer(&line)[0]; |
| 1627 | if( c=='.' ){ |
| 1628 | fputc('.', stdout); |
| 1629 | }else if( c=='\r' || c=='\n' ){ |
| 1630 | hdrPending = 0; |
| 1631 | } |
| 1632 | fwrite(blob_buffer(&line), 1, blob_size(&line), stdout); |
| 1633 | nLine++; |
| 1634 | if( !hdrPending ) iLimit--; |
| 1635 | } |
| 1636 | if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine); |
| 1637 | pop3_print(pLog, "."); |
| 1638 | fossil_free(z); |
| 1639 | blob_reset(&all); |
| 1640 | blob_reset(&line); |
| 1641 | fflush(stdout); |
| 1642 | continue; |
| 1643 | } |
| 1644 | if( strcmp(zCmd,"dele")==0 ){ |
| 1645 | if( zA1==0 ) goto cmd_error; |
| 1646 | db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1)); |
| 1647 | goto cmd_ok; |
| 1648 | } |
| 1649 | if( strcmp(zCmd,"rset")==0 ){ |
| 1650 | db_multi_exec("UPDATE pop3 SET isDel=0"); |
| 1651 | goto cmd_ok; |
| 1652 | } |
| 1653 | if( strcmp(zCmd,"uidl")==0 ){ |
| 1654 | if( zA1 ){ |
| 1655 | db_prepare(&q, "SELECT id, emailid FROM pop3" |
| 1656 | " WHERE id=%d AND NOT isDel", atoi(zA1)); |
| 1657 | if( db_step(&q)==SQLITE_ROW ){ |
| 1658 | pop3_print(pLog, "+OK %d %d", |
| 1659 | db_column_int(&q,0), db_column_int(&q,1)); |
| 1660 | }else{ |
| 1661 | pop3_print(pLog,"-ERR"); |
| 1662 | } |
| 1663 | }else{ |
| 1664 | pop3_print(pLog, "+OK"); |
| 1665 | db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel"); |
| 1666 | while( db_step(&q)==SQLITE_ROW ){ |
| 1667 | pop3_print(pLog, "%d %d", |
| 1668 | db_column_int(&q,0), db_column_int(&q,1)); |
| 1669 | } |
| 1670 | pop3_print(pLog, "."); |
| 1671 | } |
| 1672 | db_finalize(&q); |
| 1673 | continue; |
| 1674 | } |
| 1675 | if( strcmp(zCmd,"noop")==0 ){ |
| 1676 | goto cmd_ok; |
| 1677 | } |
| 1678 | /* Else, fall through into cmd_error */ |
| 1679 | } |
| 1680 | cmd_error: |
| 1681 | pop3_print(pLog, "-ERR"); |
| 1682 | continue; |
| 1683 | cmd_ok: |
| 1684 | pop3_print(pLog, "+OK"); |
| 1685 | continue; |
| 1686 | } |
| 1687 | if( pLog ) fclose(pLog); |
| 1688 | } |
| 1689 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -646,1043 +646,5 @@ | |
| 646 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 647 | } |
| 648 | smtp_session_free(p); |
| 649 | blob_reset(&body); |
| 650 | } |
| 651 |
+159
-98
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | -** version 3.35.0. By combining all the individual C code files into this | |
| 3 | +** version 3.35.3. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -1184,13 +1184,13 @@ | ||
| 1184 | 1184 | ** |
| 1185 | 1185 | ** See also: [sqlite3_libversion()], |
| 1186 | 1186 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1187 | 1187 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1188 | 1188 | */ |
| 1189 | -#define SQLITE_VERSION "3.35.0" | |
| 1190 | -#define SQLITE_VERSION_NUMBER 3035000 | |
| 1191 | -#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b" | |
| 1189 | +#define SQLITE_VERSION "3.35.3" | |
| 1190 | +#define SQLITE_VERSION_NUMBER 3035003 | |
| 1191 | +#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c" | |
| 1192 | 1192 | |
| 1193 | 1193 | /* |
| 1194 | 1194 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1195 | 1195 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1196 | 1196 | ** |
| @@ -17005,11 +17005,14 @@ | ||
| 17005 | 17005 | u8 iDb; /* Which db file is being initialized */ |
| 17006 | 17006 | u8 busy; /* TRUE if currently initializing */ |
| 17007 | 17007 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 17008 | 17008 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 17009 | 17009 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| 17010 | + unsigned bDropColumn : 1; /* Doing schema check after DROP COLUMN */ | |
| 17010 | 17011 | char **azInit; /* "type", "name", and "tbl_name" columns */ |
| 17012 | + /* or if bDropColumn, then azInit[0] is the */ | |
| 17013 | + /* name of the column being dropped */ | |
| 17011 | 17014 | } init; |
| 17012 | 17015 | int nVdbeActive; /* Number of VDBEs currently running */ |
| 17013 | 17016 | int nVdbeRead; /* Number of active VDBEs that read or write */ |
| 17014 | 17017 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 17015 | 17018 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| @@ -21066,10 +21069,11 @@ | ||
| 21066 | 21069 | u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ |
| 21067 | 21070 | #endif |
| 21068 | 21071 | Bool isEphemeral:1; /* True for an ephemeral table */ |
| 21069 | 21072 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 21070 | 21073 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 21074 | + Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ | |
| 21071 | 21075 | u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 21072 | 21076 | Btree *pBtx; /* Separate file holding temporary table */ |
| 21073 | 21077 | i64 seqCount; /* Sequence counter */ |
| 21074 | 21078 | u32 *aAltMap; /* Mapping from table to index column numbers */ |
| 21075 | 21079 | |
| @@ -22866,10 +22870,11 @@ | ||
| 22866 | 22870 | int i, n; |
| 22867 | 22871 | const unsigned char *z; |
| 22868 | 22872 | int eType; |
| 22869 | 22873 | memset(p, 0, sizeof(*p)); |
| 22870 | 22874 | if( argc==0 ){ |
| 22875 | + if( !sqlite3NotPureFunc(context) ) return 1; | |
| 22871 | 22876 | return setDateTimeToCurrent(context, p); |
| 22872 | 22877 | } |
| 22873 | 22878 | if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT |
| 22874 | 22879 | || eType==SQLITE_INTEGER ){ |
| 22875 | 22880 | setRawDateNumber(p, sqlite3_value_double(argv[0])); |
| @@ -40214,11 +40219,12 @@ | ||
| 40214 | 40219 | */ |
| 40215 | 40220 | static int unixBackupDir(const char *z, int *pJ){ |
| 40216 | 40221 | int j = *pJ; |
| 40217 | 40222 | int i; |
| 40218 | 40223 | if( j<=0 ) return 0; |
| 40219 | - for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} | |
| 40224 | + for(i=j-1; i>0 && z[i-1]!='/'; i--){} | |
| 40225 | + if( i==0 ) return 0; | |
| 40220 | 40226 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 40221 | 40227 | *pJ = i-1; |
| 40222 | 40228 | return 1; |
| 40223 | 40229 | } |
| 40224 | 40230 | |
| @@ -64271,11 +64277,11 @@ | ||
| 64271 | 64277 | u8 sharable; /* True if we can share pBt with another db */ |
| 64272 | 64278 | u8 locked; /* True if db currently has pBt locked */ |
| 64273 | 64279 | u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ |
| 64274 | 64280 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 64275 | 64281 | int nBackup; /* Number of backup operations reading this btree */ |
| 64276 | - u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ | |
| 64282 | + u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */ | |
| 64277 | 64283 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 64278 | 64284 | Btree *pPrev; /* Back pointer of the same list */ |
| 64279 | 64285 | #ifdef SQLITE_DEBUG |
| 64280 | 64286 | u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */ |
| 64281 | 64287 | #endif |
| @@ -67684,23 +67690,27 @@ | ||
| 67684 | 67690 | /* |
| 67685 | 67691 | ** Close an open database and invalidate all cursors. |
| 67686 | 67692 | */ |
| 67687 | 67693 | SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ |
| 67688 | 67694 | BtShared *pBt = p->pBt; |
| 67689 | - BtCursor *pCur; | |
| 67690 | 67695 | |
| 67691 | 67696 | /* Close all cursors opened via this handle. */ |
| 67692 | 67697 | assert( sqlite3_mutex_held(p->db->mutex) ); |
| 67693 | 67698 | sqlite3BtreeEnter(p); |
| 67694 | - pCur = pBt->pCursor; | |
| 67695 | - while( pCur ){ | |
| 67696 | - BtCursor *pTmp = pCur; | |
| 67697 | - pCur = pCur->pNext; | |
| 67698 | - if( pTmp->pBtree==p ){ | |
| 67699 | - sqlite3BtreeCloseCursor(pTmp); | |
| 67699 | + | |
| 67700 | + /* Verify that no other cursors have this Btree open */ | |
| 67701 | +#ifdef SQLITE_DEBUG | |
| 67702 | + { | |
| 67703 | + BtCursor *pCur = pBt->pCursor; | |
| 67704 | + while( pCur ){ | |
| 67705 | + BtCursor *pTmp = pCur; | |
| 67706 | + pCur = pCur->pNext; | |
| 67707 | + assert( pTmp->pBtree!=p ); | |
| 67708 | + | |
| 67700 | 67709 | } |
| 67701 | 67710 | } |
| 67711 | +#endif | |
| 67702 | 67712 | |
| 67703 | 67713 | /* Rollback any active transaction and free the handle structure. |
| 67704 | 67714 | ** The call to sqlite3BtreeRollback() drops any table-locks held by |
| 67705 | 67715 | ** this handle. |
| 67706 | 67716 | */ |
| @@ -69078,11 +69088,11 @@ | ||
| 69078 | 69088 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 69079 | 69089 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 69080 | 69090 | sqlite3BtreeLeave(p); |
| 69081 | 69091 | return rc; |
| 69082 | 69092 | } |
| 69083 | - p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ | |
| 69093 | + p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ | |
| 69084 | 69094 | pBt->inTransaction = TRANS_READ; |
| 69085 | 69095 | btreeClearHasContent(pBt); |
| 69086 | 69096 | } |
| 69087 | 69097 | |
| 69088 | 69098 | btreeEndTransaction(p); |
| @@ -69488,11 +69498,18 @@ | ||
| 69488 | 69498 | } |
| 69489 | 69499 | btreeReleaseAllCursorPages(pCur); |
| 69490 | 69500 | unlockBtreeIfUnused(pBt); |
| 69491 | 69501 | sqlite3_free(pCur->aOverflow); |
| 69492 | 69502 | sqlite3_free(pCur->pKey); |
| 69493 | - sqlite3BtreeLeave(pBtree); | |
| 69503 | + if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ | |
| 69504 | + /* Since the BtShared is not sharable, there is no need to | |
| 69505 | + ** worry about the missing sqlite3BtreeLeave() call here. */ | |
| 69506 | + assert( pBtree->sharable==0 ); | |
| 69507 | + sqlite3BtreeClose(pBtree); | |
| 69508 | + }else{ | |
| 69509 | + sqlite3BtreeLeave(pBtree); | |
| 69510 | + } | |
| 69494 | 69511 | pCur->pBtree = 0; |
| 69495 | 69512 | } |
| 69496 | 69513 | return SQLITE_OK; |
| 69497 | 69514 | } |
| 69498 | 69515 | |
| @@ -74601,11 +74618,11 @@ | ||
| 74601 | 74618 | assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) ); |
| 74602 | 74619 | assert( pBt->pPage1 ); |
| 74603 | 74620 | assert( idx>=0 && idx<=15 ); |
| 74604 | 74621 | |
| 74605 | 74622 | if( idx==BTREE_DATA_VERSION ){ |
| 74606 | - *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; | |
| 74623 | + *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion; | |
| 74607 | 74624 | }else{ |
| 74608 | 74625 | *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); |
| 74609 | 74626 | } |
| 74610 | 74627 | |
| 74611 | 74628 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| @@ -80829,24 +80846,19 @@ | ||
| 80829 | 80846 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ |
| 80830 | 80847 | if( pCx==0 ){ |
| 80831 | 80848 | return; |
| 80832 | 80849 | } |
| 80833 | 80850 | assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); |
| 80851 | + assert( pCx->pBtx==0 || pCx->isEphemeral ); | |
| 80834 | 80852 | switch( pCx->eCurType ){ |
| 80835 | 80853 | case CURTYPE_SORTER: { |
| 80836 | 80854 | sqlite3VdbeSorterClose(p->db, pCx); |
| 80837 | 80855 | break; |
| 80838 | 80856 | } |
| 80839 | 80857 | case CURTYPE_BTREE: { |
| 80840 | - if( pCx->isEphemeral ){ | |
| 80841 | - if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx); | |
| 80842 | - /* The pCx->pCursor will be close automatically, if it exists, by | |
| 80843 | - ** the call above. */ | |
| 80844 | - }else{ | |
| 80845 | - assert( pCx->uc.pCursor!=0 ); | |
| 80846 | - sqlite3BtreeCloseCursor(pCx->uc.pCursor); | |
| 80847 | - } | |
| 80858 | + assert( pCx->uc.pCursor!=0 ); | |
| 80859 | + sqlite3BtreeCloseCursor(pCx->uc.pCursor); | |
| 80848 | 80860 | break; |
| 80849 | 80861 | } |
| 80850 | 80862 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 80851 | 80863 | case CURTYPE_VTAB: { |
| 80852 | 80864 | sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; |
| @@ -81926,10 +81938,11 @@ | ||
| 81926 | 81938 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ |
| 81927 | 81939 | VdbeCursor *p = *pp; |
| 81928 | 81940 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81929 | 81941 | if( p->deferredMoveto ){ |
| 81930 | 81942 | u32 iMap; |
| 81943 | + assert( !p->isEphemeral ); | |
| 81931 | 81944 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81932 | 81945 | *pp = p->pAltCursor; |
| 81933 | 81946 | *piCol = iMap - 1; |
| 81934 | 81947 | return SQLITE_OK; |
| 81935 | 81948 | } |
| @@ -86134,15 +86147,10 @@ | ||
| 86134 | 86147 | ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + |
| 86135 | 86148 | (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); |
| 86136 | 86149 | |
| 86137 | 86150 | assert( iCur>=0 && iCur<p->nCursor ); |
| 86138 | 86151 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 86139 | - /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag | |
| 86140 | - ** is clear. Otherwise, if this is an ephemeral cursor created by | |
| 86141 | - ** OP_OpenDup, the cursor will not be closed and will still be part | |
| 86142 | - ** of a BtShared.pCursor list. */ | |
| 86143 | - if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0; | |
| 86144 | 86152 | sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); |
| 86145 | 86153 | p->apCsr[iCur] = 0; |
| 86146 | 86154 | } |
| 86147 | 86155 | if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ |
| 86148 | 86156 | p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; |
| @@ -89824,21 +89832,24 @@ | ||
| 89824 | 89832 | VdbeCursor *pOrig; /* The original cursor to be duplicated */ |
| 89825 | 89833 | VdbeCursor *pCx; /* The new cursor */ |
| 89826 | 89834 | |
| 89827 | 89835 | pOrig = p->apCsr[pOp->p2]; |
| 89828 | 89836 | assert( pOrig ); |
| 89829 | - assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ | |
| 89837 | + assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ | |
| 89830 | 89838 | |
| 89831 | 89839 | pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); |
| 89832 | 89840 | if( pCx==0 ) goto no_mem; |
| 89833 | 89841 | pCx->nullRow = 1; |
| 89834 | 89842 | pCx->isEphemeral = 1; |
| 89835 | 89843 | pCx->pKeyInfo = pOrig->pKeyInfo; |
| 89836 | 89844 | pCx->isTable = pOrig->isTable; |
| 89837 | 89845 | pCx->pgnoRoot = pOrig->pgnoRoot; |
| 89838 | 89846 | pCx->isOrdered = pOrig->isOrdered; |
| 89839 | - rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, | |
| 89847 | + pCx->pBtx = pOrig->pBtx; | |
| 89848 | + pCx->hasBeenDuped = 1; | |
| 89849 | + pOrig->hasBeenDuped = 1; | |
| 89850 | + rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, | |
| 89840 | 89851 | pCx->pKeyInfo, pCx->uc.pCursor); |
| 89841 | 89852 | /* The sqlite3BtreeCursor() routine can only fail for the first cursor |
| 89842 | 89853 | ** opened for a database. Since there is already an open cursor when this |
| 89843 | 89854 | ** opcode is run, the sqlite3BtreeCursor() cannot fail */ |
| 89844 | 89855 | assert( rc==SQLITE_OK ); |
| @@ -89900,13 +89911,14 @@ | ||
| 89900 | 89911 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 89901 | 89912 | aMem[pOp->p3].n = 0; |
| 89902 | 89913 | aMem[pOp->p3].z = ""; |
| 89903 | 89914 | } |
| 89904 | 89915 | pCx = p->apCsr[pOp->p1]; |
| 89905 | - if( pCx && ALWAYS(pCx->pBtx) ){ | |
| 89906 | - /* If the ephermeral table is already open, erase all existing content | |
| 89907 | - ** so that the table is empty again, rather than creating a new table. */ | |
| 89916 | + if( pCx && !pCx->hasBeenDuped ){ | |
| 89917 | + /* If the ephermeral table is already open and has no duplicates from | |
| 89918 | + ** OP_OpenDup, then erase all existing content so that the table is | |
| 89919 | + ** empty again, rather than creating a new table. */ | |
| 89908 | 89920 | assert( pCx->isEphemeral ); |
| 89909 | 89921 | pCx->seqCount = 0; |
| 89910 | 89922 | pCx->cacheStatus = CACHE_STALE; |
| 89911 | 89923 | rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); |
| 89912 | 89924 | }else{ |
| @@ -89916,37 +89928,40 @@ | ||
| 89916 | 89928 | rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, |
| 89917 | 89929 | BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, |
| 89918 | 89930 | vfsFlags); |
| 89919 | 89931 | if( rc==SQLITE_OK ){ |
| 89920 | 89932 | rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); |
| 89921 | - } | |
| 89922 | - if( rc==SQLITE_OK ){ | |
| 89923 | - /* If a transient index is required, create it by calling | |
| 89924 | - ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before | |
| 89925 | - ** opening it. If a transient table is required, just use the | |
| 89926 | - ** automatically created table with root-page 1 (an BLOB_INTKEY table). | |
| 89927 | - */ | |
| 89928 | - if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ | |
| 89929 | - assert( pOp->p4type==P4_KEYINFO ); | |
| 89930 | - rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, | |
| 89931 | - BTREE_BLOBKEY | pOp->p5); | |
| 89932 | - if( rc==SQLITE_OK ){ | |
| 89933 | - assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); | |
| 89934 | - assert( pKeyInfo->db==db ); | |
| 89935 | - assert( pKeyInfo->enc==ENC(db) ); | |
| 89936 | - rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, | |
| 89937 | - pKeyInfo, pCx->uc.pCursor); | |
| 89938 | - } | |
| 89939 | - pCx->isTable = 0; | |
| 89940 | - }else{ | |
| 89941 | - pCx->pgnoRoot = SCHEMA_ROOT; | |
| 89942 | - rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, | |
| 89943 | - 0, pCx->uc.pCursor); | |
| 89944 | - pCx->isTable = 1; | |
| 89945 | - } | |
| 89946 | - } | |
| 89947 | - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); | |
| 89933 | + if( rc==SQLITE_OK ){ | |
| 89934 | + /* If a transient index is required, create it by calling | |
| 89935 | + ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before | |
| 89936 | + ** opening it. If a transient table is required, just use the | |
| 89937 | + ** automatically created table with root-page 1 (an BLOB_INTKEY table). | |
| 89938 | + */ | |
| 89939 | + if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ | |
| 89940 | + assert( pOp->p4type==P4_KEYINFO ); | |
| 89941 | + rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, | |
| 89942 | + BTREE_BLOBKEY | pOp->p5); | |
| 89943 | + if( rc==SQLITE_OK ){ | |
| 89944 | + assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); | |
| 89945 | + assert( pKeyInfo->db==db ); | |
| 89946 | + assert( pKeyInfo->enc==ENC(db) ); | |
| 89947 | + rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, | |
| 89948 | + pKeyInfo, pCx->uc.pCursor); | |
| 89949 | + } | |
| 89950 | + pCx->isTable = 0; | |
| 89951 | + }else{ | |
| 89952 | + pCx->pgnoRoot = SCHEMA_ROOT; | |
| 89953 | + rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, | |
| 89954 | + 0, pCx->uc.pCursor); | |
| 89955 | + pCx->isTable = 1; | |
| 89956 | + } | |
| 89957 | + } | |
| 89958 | + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); | |
| 89959 | + if( rc ){ | |
| 89960 | + sqlite3BtreeClose(pCx->pBtx); | |
| 89961 | + } | |
| 89962 | + } | |
| 89948 | 89963 | } |
| 89949 | 89964 | if( rc ) goto abort_due_to_error; |
| 89950 | 89965 | pCx->nullRow = 1; |
| 89951 | 89966 | break; |
| 89952 | 89967 | } |
| @@ -90376,17 +90391,17 @@ | ||
| 90376 | 90391 | ** row by, perhaps by invoking sqlite3BtreeStep() on the cursor |
| 90377 | 90392 | ** between 0 and This.P1 times. |
| 90378 | 90393 | ** |
| 90379 | 90394 | ** There are three possible outcomes from this opcode:<ol> |
| 90380 | 90395 | ** |
| 90381 | -** <li> If after This.P1 steps, the cursor is still point to a place that | |
| 90382 | -** is earlier in the btree than the target row, | |
| 90383 | -** then fall through into the subsquence OP_SeekGE opcode. | |
| 90396 | +** <li> If after This.P1 steps, the cursor is still pointing to a place that | |
| 90397 | +** is earlier in the btree than the target row, then fall through | |
| 90398 | +** into the subsquence OP_SeekGE opcode. | |
| 90384 | 90399 | ** |
| 90385 | 90400 | ** <li> If the cursor is successfully moved to the target row by 0 or more |
| 90386 | 90401 | ** sqlite3BtreeNext() calls, then jump to This.P2, which will land just |
| 90387 | -** past the OP_IdxGT opcode that follows the OP_SeekGE. | |
| 90402 | +** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE. | |
| 90388 | 90403 | ** |
| 90389 | 90404 | ** <li> If the cursor ends up past the target row (indicating the the target |
| 90390 | 90405 | ** row does not exist in the btree) then jump to SeekOP.P2. |
| 90391 | 90406 | ** </ol> |
| 90392 | 90407 | */ |
| @@ -90399,11 +90414,12 @@ | ||
| 90399 | 90414 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90400 | 90415 | |
| 90401 | 90416 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90402 | 90417 | ** follows the OP_SeekGE. */ |
| 90403 | 90418 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90404 | - assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); | |
| 90419 | + assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); | |
| 90420 | + testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); | |
| 90405 | 90421 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90406 | 90422 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90407 | 90423 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90408 | 90424 | |
| 90409 | 90425 | assert( pOp->p1>0 ); |
| @@ -91934,10 +91950,12 @@ | ||
| 91934 | 91950 | pTabCur->nullRow = 0; |
| 91935 | 91951 | pTabCur->movetoTarget = rowid; |
| 91936 | 91952 | pTabCur->deferredMoveto = 1; |
| 91937 | 91953 | assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); |
| 91938 | 91954 | pTabCur->aAltMap = pOp->p4.ai; |
| 91955 | + assert( !pC->isEphemeral ); | |
| 91956 | + assert( !pTabCur->isEphemeral ); | |
| 91939 | 91957 | pTabCur->pAltCursor = pC; |
| 91940 | 91958 | }else{ |
| 91941 | 91959 | pOut = out2Prerelease(p, pOp); |
| 91942 | 91960 | pOut->u.i = rowid; |
| 91943 | 91961 | } |
| @@ -99165,10 +99183,11 @@ | ||
| 99165 | 99183 | */ |
| 99166 | 99184 | if( cnt==0 && zTab==0 ){ |
| 99167 | 99185 | assert( pExpr->op==TK_ID ); |
| 99168 | 99186 | if( ExprHasProperty(pExpr,EP_DblQuoted) |
| 99169 | 99187 | && areDoubleQuotedStringsEnabled(db, pTopNC) |
| 99188 | + && (db->init.bDropColumn==0 || sqlite3StrICmp(zCol, db->init.azInit[0])!=0) | |
| 99170 | 99189 | ){ |
| 99171 | 99190 | /* If a double-quoted identifier does not match any known column name, |
| 99172 | 99191 | ** then treat it as a string. |
| 99173 | 99192 | ** |
| 99174 | 99193 | ** This hack was added in the early days of SQLite in a misguided attempt |
| @@ -99179,10 +99198,15 @@ | ||
| 99179 | 99198 | ** programmers. To all those frustrated programmers, my apologies. |
| 99180 | 99199 | ** |
| 99181 | 99200 | ** Someday, I hope to get rid of this hack. Unfortunately there is |
| 99182 | 99201 | ** a huge amount of legacy SQL that uses it. So for now, we just |
| 99183 | 99202 | ** issue a warning. |
| 99203 | + ** | |
| 99204 | + ** 2021-03-15: ticket 1c24a659e6d7f3a1 | |
| 99205 | + ** Do not do the ID-to-STRING conversion when doing the schema | |
| 99206 | + ** sanity check following a DROP COLUMN if the identifer name matches | |
| 99207 | + ** the name of the column being dropped. | |
| 99184 | 99208 | */ |
| 99185 | 99209 | sqlite3_log(SQLITE_WARNING, |
| 99186 | 99210 | "double-quoted string literal: \"%w\"", zCol); |
| 99187 | 99211 | #ifdef SQLITE_ENABLE_NORMALIZE |
| 99188 | 99212 | sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); |
| @@ -106778,31 +106802,32 @@ | ||
| 106778 | 106802 | */ |
| 106779 | 106803 | static void renameTestSchema( |
| 106780 | 106804 | Parse *pParse, /* Parse context */ |
| 106781 | 106805 | const char *zDb, /* Name of db to verify schema of */ |
| 106782 | 106806 | int bTemp, /* True if this is the temp db */ |
| 106783 | - const char *zWhen /* "when" part of error message */ | |
| 106807 | + const char *zWhen, /* "when" part of error message */ | |
| 106808 | + const char *zDropColumn /* Name of column being dropped */ | |
| 106784 | 106809 | ){ |
| 106785 | 106810 | pParse->colNamesSet = 1; |
| 106786 | 106811 | sqlite3NestedParse(pParse, |
| 106787 | 106812 | "SELECT 1 " |
| 106788 | 106813 | "FROM \"%w\"." DFLT_SCHEMA_TABLE " " |
| 106789 | 106814 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106790 | 106815 | " AND sql NOT LIKE 'create virtual%%'" |
| 106791 | - " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ", | |
| 106816 | + " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %Q)=NULL ", | |
| 106792 | 106817 | zDb, |
| 106793 | - zDb, bTemp, zWhen | |
| 106818 | + zDb, bTemp, zWhen, zDropColumn | |
| 106794 | 106819 | ); |
| 106795 | 106820 | |
| 106796 | 106821 | if( bTemp==0 ){ |
| 106797 | 106822 | sqlite3NestedParse(pParse, |
| 106798 | 106823 | "SELECT 1 " |
| 106799 | 106824 | "FROM temp." DFLT_SCHEMA_TABLE " " |
| 106800 | 106825 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106801 | 106826 | " AND sql NOT LIKE 'create virtual%%'" |
| 106802 | - " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ", | |
| 106803 | - zDb, zWhen | |
| 106827 | + " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %Q)=NULL ", | |
| 106828 | + zDb, zWhen, zDropColumn | |
| 106804 | 106829 | ); |
| 106805 | 106830 | } |
| 106806 | 106831 | } |
| 106807 | 106832 | |
| 106808 | 106833 | /* |
| @@ -106961,11 +106986,11 @@ | ||
| 106961 | 106986 | sqlite3NestedParse(pParse, |
| 106962 | 106987 | "UPDATE sqlite_temp_schema SET " |
| 106963 | 106988 | "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " |
| 106964 | 106989 | "tbl_name = " |
| 106965 | 106990 | "CASE WHEN tbl_name=%Q COLLATE nocase AND " |
| 106966 | - " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') " | |
| 106991 | + " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename',0) " | |
| 106967 | 106992 | "THEN %Q ELSE tbl_name END " |
| 106968 | 106993 | "WHERE type IN ('view', 'trigger')" |
| 106969 | 106994 | , zDb, zTabName, zName, zTabName, zDb, zName); |
| 106970 | 106995 | } |
| 106971 | 106996 | |
| @@ -106981,11 +107006,11 @@ | ||
| 106981 | 107006 | sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); |
| 106982 | 107007 | } |
| 106983 | 107008 | #endif |
| 106984 | 107009 | |
| 106985 | 107010 | renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); |
| 106986 | - renameTestSchema(pParse, zDb, iDb==1, "after rename"); | |
| 107011 | + renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); | |
| 106987 | 107012 | |
| 106988 | 107013 | exit_rename_table: |
| 106989 | 107014 | sqlite3SrcListDelete(db, pSrc); |
| 106990 | 107015 | sqlite3DbFree(db, zName); |
| 106991 | 107016 | db->mDbFlags = savedDbFlags; |
| @@ -107349,11 +107374,11 @@ | ||
| 107349 | 107374 | zDb, pTab->zName, iCol, zNew, bQuote |
| 107350 | 107375 | ); |
| 107351 | 107376 | |
| 107352 | 107377 | /* Drop and reload the database schema. */ |
| 107353 | 107378 | renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); |
| 107354 | - renameTestSchema(pParse, zDb, iSchema==1, "after rename"); | |
| 107379 | + renameTestSchema(pParse, zDb, iSchema==1, "after rename", 0); | |
| 107355 | 107380 | |
| 107356 | 107381 | exit_rename_column: |
| 107357 | 107382 | sqlite3SrcListDelete(db, pSrc); |
| 107358 | 107383 | sqlite3DbFree(db, zOld); |
| 107359 | 107384 | sqlite3DbFree(db, zNew); |
| @@ -107773,16 +107798,21 @@ | ||
| 107773 | 107798 | static int renameParseSql( |
| 107774 | 107799 | Parse *p, /* Memory to use for Parse object */ |
| 107775 | 107800 | const char *zDb, /* Name of schema SQL belongs to */ |
| 107776 | 107801 | sqlite3 *db, /* Database handle */ |
| 107777 | 107802 | const char *zSql, /* SQL to parse */ |
| 107778 | - int bTemp /* True if SQL is from temp schema */ | |
| 107803 | + int bTemp, /* True if SQL is from temp schema */ | |
| 107804 | + const char *zDropColumn /* Name of column being dropped */ | |
| 107779 | 107805 | ){ |
| 107780 | 107806 | int rc; |
| 107781 | 107807 | char *zErr = 0; |
| 107782 | 107808 | |
| 107783 | 107809 | db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); |
| 107810 | + if( zDropColumn ){ | |
| 107811 | + db->init.bDropColumn = 1; | |
| 107812 | + db->init.azInit = (char**)&zDropColumn; | |
| 107813 | + } | |
| 107784 | 107814 | |
| 107785 | 107815 | /* Parse the SQL statement passed as the first argument. If no error |
| 107786 | 107816 | ** occurs and the parse does not result in a new table, index or |
| 107787 | 107817 | ** trigger object, the database must be corrupt. */ |
| 107788 | 107818 | memset(p, 0, sizeof(Parse)); |
| @@ -107811,10 +107841,11 @@ | ||
| 107811 | 107841 | } |
| 107812 | 107842 | } |
| 107813 | 107843 | #endif |
| 107814 | 107844 | |
| 107815 | 107845 | db->init.iDb = 0; |
| 107846 | + db->init.bDropColumn = 0; | |
| 107816 | 107847 | return rc; |
| 107817 | 107848 | } |
| 107818 | 107849 | |
| 107819 | 107850 | /* |
| 107820 | 107851 | ** This function edits SQL statement zSql, replacing each token identified |
| @@ -108112,11 +108143,11 @@ | ||
| 108112 | 108143 | sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
| 108113 | 108144 | |
| 108114 | 108145 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108115 | 108146 | db->xAuth = 0; |
| 108116 | 108147 | #endif |
| 108117 | - rc = renameParseSql(&sParse, zDb, db, zSql, bTemp); | |
| 108148 | + rc = renameParseSql(&sParse, zDb, db, zSql, bTemp, 0); | |
| 108118 | 108149 | |
| 108119 | 108150 | /* Find tokens that need to be replaced. */ |
| 108120 | 108151 | memset(&sWalker, 0, sizeof(Walker)); |
| 108121 | 108152 | sWalker.pParse = &sParse; |
| 108122 | 108153 | sWalker.xExprCallback = renameColumnExprCb; |
| @@ -108154,16 +108185,16 @@ | ||
| 108154 | 108185 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108155 | 108186 | } |
| 108156 | 108187 | for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ |
| 108157 | 108188 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108158 | 108189 | } |
| 108159 | - } | |
| 108160 | 108190 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| 108161 | - for(i=0; i<sParse.pNewTable->nCol; i++){ | |
| 108162 | - sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); | |
| 108191 | + for(i=0; i<sParse.pNewTable->nCol; i++){ | |
| 108192 | + sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); | |
| 108193 | + } | |
| 108194 | +#endif | |
| 108163 | 108195 | } |
| 108164 | -#endif | |
| 108165 | 108196 | |
| 108166 | 108197 | for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| 108167 | 108198 | for(i=0; i<pFKey->nCol; i++){ |
| 108168 | 108199 | if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ |
| 108169 | 108200 | renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); |
| @@ -108316,11 +108347,11 @@ | ||
| 108316 | 108347 | sWalker.pParse = &sParse; |
| 108317 | 108348 | sWalker.xExprCallback = renameTableExprCb; |
| 108318 | 108349 | sWalker.xSelectCallback = renameTableSelectCb; |
| 108319 | 108350 | sWalker.u.pRename = &sCtx; |
| 108320 | 108351 | |
| 108321 | - rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); | |
| 108352 | + rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, 0); | |
| 108322 | 108353 | |
| 108323 | 108354 | if( rc==SQLITE_OK ){ |
| 108324 | 108355 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108325 | 108356 | if( sParse.pNewTable ){ |
| 108326 | 108357 | Table *pTab = sParse.pNewTable; |
| @@ -108432,10 +108463,11 @@ | ||
| 108432 | 108463 | ** 1: SQL statement. |
| 108433 | 108464 | ** 2: Object type ("view", "table", "trigger" or "index"). |
| 108434 | 108465 | ** 3: Object name. |
| 108435 | 108466 | ** 4: True if object is from temp schema. |
| 108436 | 108467 | ** 5: "when" part of error message. |
| 108468 | +** 6: Name of column being dropped, or NULL. | |
| 108437 | 108469 | ** |
| 108438 | 108470 | ** Unless it finds an error, this function normally returns NULL. However, it |
| 108439 | 108471 | ** returns integer value 1 if: |
| 108440 | 108472 | ** |
| 108441 | 108473 | ** * the SQL argument creates a trigger, and |
| @@ -108450,10 +108482,11 @@ | ||
| 108450 | 108482 | char const *zDb = (const char*)sqlite3_value_text(argv[0]); |
| 108451 | 108483 | char const *zInput = (const char*)sqlite3_value_text(argv[1]); |
| 108452 | 108484 | int bTemp = sqlite3_value_int(argv[4]); |
| 108453 | 108485 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108454 | 108486 | char const *zWhen = (const char*)sqlite3_value_text(argv[5]); |
| 108487 | + char const *zDropColumn = (const char*)sqlite3_value_text(argv[6]); | |
| 108455 | 108488 | |
| 108456 | 108489 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108457 | 108490 | sqlite3_xauth xAuth = db->xAuth; |
| 108458 | 108491 | db->xAuth = 0; |
| 108459 | 108492 | #endif |
| @@ -108460,11 +108493,11 @@ | ||
| 108460 | 108493 | |
| 108461 | 108494 | UNUSED_PARAMETER(NotUsed); |
| 108462 | 108495 | if( zDb && zInput ){ |
| 108463 | 108496 | int rc; |
| 108464 | 108497 | Parse sParse; |
| 108465 | - rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); | |
| 108498 | + rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, zDropColumn); | |
| 108466 | 108499 | if( rc==SQLITE_OK ){ |
| 108467 | 108500 | if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ |
| 108468 | 108501 | NameContext sNC; |
| 108469 | 108502 | memset(&sNC, 0, sizeof(sNC)); |
| 108470 | 108503 | sNC.pParse = &sParse; |
| @@ -108528,11 +108561,11 @@ | ||
| 108528 | 108561 | sqlite3_xauth xAuth = db->xAuth; |
| 108529 | 108562 | db->xAuth = 0; |
| 108530 | 108563 | #endif |
| 108531 | 108564 | |
| 108532 | 108565 | UNUSED_PARAMETER(NotUsed); |
| 108533 | - rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); | |
| 108566 | + rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1, 0); | |
| 108534 | 108567 | if( rc!=SQLITE_OK ) goto drop_column_done; |
| 108535 | 108568 | pTab = sParse.pNewTable; |
| 108536 | 108569 | if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ |
| 108537 | 108570 | /* This can happen if the sqlite_schema table is corrupt */ |
| 108538 | 108571 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -108621,21 +108654,21 @@ | ||
| 108621 | 108654 | |
| 108622 | 108655 | /* Edit the sqlite_schema table */ |
| 108623 | 108656 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 108624 | 108657 | assert( iDb>=0 ); |
| 108625 | 108658 | zDb = db->aDb[iDb].zDbSName; |
| 108626 | - renameTestSchema(pParse, zDb, iDb==1, ""); | |
| 108659 | + renameTestSchema(pParse, zDb, iDb==1, "", 0); | |
| 108627 | 108660 | sqlite3NestedParse(pParse, |
| 108628 | 108661 | "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " |
| 108629 | 108662 | "sql = sqlite_drop_column(%d, sql, %d) " |
| 108630 | 108663 | "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" |
| 108631 | 108664 | , zDb, iDb, iCol, pTab->zName |
| 108632 | 108665 | ); |
| 108633 | 108666 | |
| 108634 | 108667 | /* Drop and reload the database schema. */ |
| 108635 | 108668 | renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); |
| 108636 | - renameTestSchema(pParse, zDb, iDb==1, "after drop column"); | |
| 108669 | + renameTestSchema(pParse, zDb, iDb==1, "after drop column", zCol); | |
| 108637 | 108670 | |
| 108638 | 108671 | /* Edit rows of table on disk */ |
| 108639 | 108672 | if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ |
| 108640 | 108673 | int i; |
| 108641 | 108674 | int addr; |
| @@ -108691,11 +108724,11 @@ | ||
| 108691 | 108724 | */ |
| 108692 | 108725 | SQLITE_PRIVATE void sqlite3AlterFunctions(void){ |
| 108693 | 108726 | static FuncDef aAlterTableFuncs[] = { |
| 108694 | 108727 | INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), |
| 108695 | 108728 | INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), |
| 108696 | - INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest), | |
| 108729 | + INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), | |
| 108697 | 108730 | INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), |
| 108698 | 108731 | }; |
| 108699 | 108732 | sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); |
| 108700 | 108733 | } |
| 108701 | 108734 | #endif /* SQLITE_ALTER_TABLE */ |
| @@ -115663,11 +115696,15 @@ | ||
| 115663 | 115696 | } |
| 115664 | 115697 | |
| 115665 | 115698 | /* Clean up before exiting */ |
| 115666 | 115699 | exit_create_index: |
| 115667 | 115700 | if( pIndex ) sqlite3FreeIndex(db, pIndex); |
| 115668 | - if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */ | |
| 115701 | + if( pTab ){ | |
| 115702 | + /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list. | |
| 115703 | + ** The list was already ordered when this routine was entered, so at this | |
| 115704 | + ** point at most a single index (the newly added index) will be out of | |
| 115705 | + ** order. So we have to reorder at most one index. */ | |
| 115669 | 115706 | Index **ppFrom = &pTab->pIndex; |
| 115670 | 115707 | Index *pThis; |
| 115671 | 115708 | for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ |
| 115672 | 115709 | Index *pNext; |
| 115673 | 115710 | if( pThis->onError!=OE_Replace ) continue; |
| @@ -115677,10 +115714,20 @@ | ||
| 115677 | 115714 | pNext->pNext = pThis; |
| 115678 | 115715 | ppFrom = &pNext->pNext; |
| 115679 | 115716 | } |
| 115680 | 115717 | break; |
| 115681 | 115718 | } |
| 115719 | +#ifdef SQLITE_DEBUG | |
| 115720 | + /* Verify that all REPLACE indexes really are now at the end | |
| 115721 | + ** of the index list. In other words, no other index type ever | |
| 115722 | + ** comes after a REPLACE index on the list. */ | |
| 115723 | + for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){ | |
| 115724 | + assert( pThis->onError!=OE_Replace | |
| 115725 | + || pThis->pNext==0 | |
| 115726 | + || pThis->pNext->onError==OE_Replace ); | |
| 115727 | + } | |
| 115728 | +#endif | |
| 115682 | 115729 | } |
| 115683 | 115730 | sqlite3ExprDelete(db, pPIWhere); |
| 115684 | 115731 | sqlite3ExprListDelete(db, pList); |
| 115685 | 115732 | sqlite3SrcListDelete(db, pTblName); |
| 115686 | 115733 | sqlite3DbFree(db, zName); |
| @@ -123093,11 +123140,13 @@ | ||
| 123093 | 123140 | pNx->pUpsertSrc = pTabList; |
| 123094 | 123141 | pNx->regData = regData; |
| 123095 | 123142 | pNx->iDataCur = iDataCur; |
| 123096 | 123143 | pNx->iIdxCur = iIdxCur; |
| 123097 | 123144 | if( pNx->pUpsertTarget ){ |
| 123098 | - sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx); | |
| 123145 | + if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){ | |
| 123146 | + goto insert_cleanup; | |
| 123147 | + } | |
| 123099 | 123148 | } |
| 123100 | 123149 | pNx = pNx->pNextUpsert; |
| 123101 | 123150 | }while( pNx!=0 ); |
| 123102 | 123151 | } |
| 123103 | 123152 | #endif |
| @@ -124515,11 +124564,11 @@ | ||
| 124515 | 124564 | int regData /* Data containing new record */ |
| 124516 | 124565 | ){ |
| 124517 | 124566 | Vdbe *v = pParse->pVdbe; |
| 124518 | 124567 | int r = sqlite3GetTempReg(pParse); |
| 124519 | 124568 | assert( !HasRowid(pTab) ); |
| 124520 | - assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) ); | |
| 124569 | + assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB ); | |
| 124521 | 124570 | sqlite3VdbeAddOp2(v, OP_Integer, 0, r); |
| 124522 | 124571 | sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); |
| 124523 | 124572 | sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); |
| 124524 | 124573 | sqlite3ReleaseTempReg(pParse, r); |
| 124525 | 124574 | } |
| @@ -133063,11 +133112,11 @@ | ||
| 133063 | 133112 | sqlite3HashInit(&ht); |
| 133064 | 133113 | if( pEList ){ |
| 133065 | 133114 | nCol = pEList->nExpr; |
| 133066 | 133115 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| 133067 | 133116 | testcase( aCol==0 ); |
| 133068 | - if( nCol>32767 ) nCol = 32767; | |
| 133117 | + if( NEVER(nCol>32767) ) nCol = 32767; | |
| 133069 | 133118 | }else{ |
| 133070 | 133119 | nCol = 0; |
| 133071 | 133120 | aCol = 0; |
| 133072 | 133121 | } |
| 133073 | 133122 | assert( nCol==(i16)nCol ); |
| @@ -135116,10 +135165,11 @@ | ||
| 135116 | 135165 | |
| 135117 | 135166 | /* Restriction (23) */ |
| 135118 | 135167 | if( (p->selFlags & SF_Recursive) ) return 0; |
| 135119 | 135168 | |
| 135120 | 135169 | if( pSrc->nSrc>1 ){ |
| 135170 | + if( pParse->nSelect>500 ) return 0; | |
| 135121 | 135171 | aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); |
| 135122 | 135172 | } |
| 135123 | 135173 | } |
| 135124 | 135174 | |
| 135125 | 135175 | /***** If we reach this point, flattening is permitted. *****/ |
| @@ -135192,10 +135242,11 @@ | ||
| 135192 | 135242 | p->op = TK_ALL; |
| 135193 | 135243 | pSubitem->pTab = pItemTab; |
| 135194 | 135244 | if( pNew==0 ){ |
| 135195 | 135245 | p->pPrior = pPrior; |
| 135196 | 135246 | }else{ |
| 135247 | + pNew->selId = ++pParse->nSelect; | |
| 135197 | 135248 | if( aCsrMap && db->mallocFailed==0 ){ |
| 135198 | 135249 | renumberCursors(pParse, pNew, iFrom, aCsrMap); |
| 135199 | 135250 | } |
| 135200 | 135251 | pNew->pPrior = pPrior; |
| 135201 | 135252 | if( pPrior ) pPrior->pNext = pNew; |
| @@ -136135,20 +136186,28 @@ | ||
| 136135 | 136186 | |
| 136136 | 136187 | pCte->zCteErr = "circular reference: %s"; |
| 136137 | 136188 | pSavedWith = pParse->pWith; |
| 136138 | 136189 | pParse->pWith = pWith; |
| 136139 | 136190 | if( pSel->selFlags & SF_Recursive ){ |
| 136191 | + int rc; | |
| 136140 | 136192 | assert( pRecTerm!=0 ); |
| 136141 | 136193 | assert( (pRecTerm->selFlags & SF_Recursive)==0 ); |
| 136142 | 136194 | assert( pRecTerm->pNext!=0 ); |
| 136143 | 136195 | assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); |
| 136144 | 136196 | assert( pRecTerm->pWith==0 ); |
| 136145 | 136197 | pRecTerm->pWith = pSel->pWith; |
| 136146 | - sqlite3WalkSelect(pWalker, pRecTerm); | |
| 136198 | + rc = sqlite3WalkSelect(pWalker, pRecTerm); | |
| 136147 | 136199 | pRecTerm->pWith = 0; |
| 136200 | + if( rc ){ | |
| 136201 | + pParse->pWith = pSavedWith; | |
| 136202 | + return 2; | |
| 136203 | + } | |
| 136148 | 136204 | }else{ |
| 136149 | - sqlite3WalkSelect(pWalker, pSel); | |
| 136205 | + if( sqlite3WalkSelect(pWalker, pSel) ){ | |
| 136206 | + pParse->pWith = pSavedWith; | |
| 136207 | + return 2; | |
| 136208 | + } | |
| 136150 | 136209 | } |
| 136151 | 136210 | pParse->pWith = pWith; |
| 136152 | 136211 | |
| 136153 | 136212 | for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); |
| 136154 | 136213 | pEList = pLeft->pEList; |
| @@ -137453,11 +137512,13 @@ | ||
| 137453 | 137512 | sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); |
| 137454 | 137513 | } |
| 137455 | 137514 | sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); |
| 137456 | 137515 | pSub->nSelectRow = pPrior->pSelect->nSelectRow; |
| 137457 | 137516 | }else{ |
| 137458 | - /* Generate a subroutine that will materialize the view. */ | |
| 137517 | + /* Materalize the view. If the view is not correlated, generate a | |
| 137518 | + ** subroutine to do the materialization so that subsequent uses of | |
| 137519 | + ** the same view can reuse the materialization. */ | |
| 137459 | 137520 | int topAddr; |
| 137460 | 137521 | int onceAddr = 0; |
| 137461 | 137522 | int retAddr; |
| 137462 | 137523 | |
| 137463 | 137524 | testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ |
| @@ -137480,11 +137541,11 @@ | ||
| 137480 | 137541 | if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); |
| 137481 | 137542 | retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); |
| 137482 | 137543 | VdbeComment((v, "end %s", pItem->pTab->zName)); |
| 137483 | 137544 | sqlite3VdbeChangeP1(v, topAddr, retAddr); |
| 137484 | 137545 | sqlite3ClearTempRegCache(pParse); |
| 137485 | - if( pItem->fg.isCte ){ | |
| 137546 | + if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ | |
| 137486 | 137547 | CteUse *pCteUse = pItem->u2.pCteUse; |
| 137487 | 137548 | pCteUse->addrM9e = pItem->addrFillSub; |
| 137488 | 137549 | pCteUse->regRtn = pItem->regReturn; |
| 137489 | 137550 | pCteUse->iCur = pItem->iCursor; |
| 137490 | 137551 | pCteUse->nRowEst = pSub->nSelectRow; |
| @@ -162285,11 +162346,11 @@ | ||
| 162285 | 162346 | /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 |
| 162286 | 162347 | #endif |
| 162287 | 162348 | #ifdef SQLITE_EBCDIC |
| 162288 | 162349 | /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ |
| 162289 | 162350 | /* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, |
| 162290 | -/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, | |
| 162351 | +/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, | |
| 162291 | 162352 | /* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162292 | 162353 | /* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162293 | 162354 | /* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, |
| 162294 | 162355 | /* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, |
| 162295 | 162356 | /* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, |
| @@ -229190,11 +229251,11 @@ | ||
| 229190 | 229251 | int nArg, /* Number of args */ |
| 229191 | 229252 | sqlite3_value **apUnused /* Function arguments */ |
| 229192 | 229253 | ){ |
| 229193 | 229254 | assert( nArg==0 ); |
| 229194 | 229255 | UNUSED_PARAM2(nArg, apUnused); |
| 229195 | - sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT); | |
| 229256 | + sqlite3_result_text(pCtx, "fts5: 2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c", -1, SQLITE_TRANSIENT); | |
| 229196 | 229257 | } |
| 229197 | 229258 | |
| 229198 | 229259 | /* |
| 229199 | 229260 | ** Return true if zName is the extension on one of the shadow tables used |
| 229200 | 229261 | ** by this module. |
| @@ -234116,12 +234177,12 @@ | ||
| 234116 | 234177 | } |
| 234117 | 234178 | #endif /* SQLITE_CORE */ |
| 234118 | 234179 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 234119 | 234180 | |
| 234120 | 234181 | /************** End of stmt.c ************************************************/ |
| 234121 | -#if __LINE__!=234121 | |
| 234182 | +#if __LINE__!=234182 | |
| 234122 | 234183 | #undef SQLITE_SOURCE_ID |
| 234123 | -#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2" | |
| 234184 | +#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4alt2" | |
| 234124 | 234185 | #endif |
| 234125 | 234186 | /* Return the source-id for this library */ |
| 234126 | 234187 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 234127 | 234188 | /************************** End of sqlite3.c ******************************/ |
| 234128 | 234189 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.35.0. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -1184,13 +1184,13 @@ | |
| 1184 | ** |
| 1185 | ** See also: [sqlite3_libversion()], |
| 1186 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1187 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1188 | */ |
| 1189 | #define SQLITE_VERSION "3.35.0" |
| 1190 | #define SQLITE_VERSION_NUMBER 3035000 |
| 1191 | #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b" |
| 1192 | |
| 1193 | /* |
| 1194 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1195 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1196 | ** |
| @@ -17005,11 +17005,14 @@ | |
| 17005 | u8 iDb; /* Which db file is being initialized */ |
| 17006 | u8 busy; /* TRUE if currently initializing */ |
| 17007 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 17008 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 17009 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| 17010 | char **azInit; /* "type", "name", and "tbl_name" columns */ |
| 17011 | } init; |
| 17012 | int nVdbeActive; /* Number of VDBEs currently running */ |
| 17013 | int nVdbeRead; /* Number of active VDBEs that read or write */ |
| 17014 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 17015 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| @@ -21066,10 +21069,11 @@ | |
| 21066 | u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ |
| 21067 | #endif |
| 21068 | Bool isEphemeral:1; /* True for an ephemeral table */ |
| 21069 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 21070 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 21071 | u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 21072 | Btree *pBtx; /* Separate file holding temporary table */ |
| 21073 | i64 seqCount; /* Sequence counter */ |
| 21074 | u32 *aAltMap; /* Mapping from table to index column numbers */ |
| 21075 | |
| @@ -22866,10 +22870,11 @@ | |
| 22866 | int i, n; |
| 22867 | const unsigned char *z; |
| 22868 | int eType; |
| 22869 | memset(p, 0, sizeof(*p)); |
| 22870 | if( argc==0 ){ |
| 22871 | return setDateTimeToCurrent(context, p); |
| 22872 | } |
| 22873 | if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT |
| 22874 | || eType==SQLITE_INTEGER ){ |
| 22875 | setRawDateNumber(p, sqlite3_value_double(argv[0])); |
| @@ -40214,11 +40219,12 @@ | |
| 40214 | */ |
| 40215 | static int unixBackupDir(const char *z, int *pJ){ |
| 40216 | int j = *pJ; |
| 40217 | int i; |
| 40218 | if( j<=0 ) return 0; |
| 40219 | for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} |
| 40220 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 40221 | *pJ = i-1; |
| 40222 | return 1; |
| 40223 | } |
| 40224 | |
| @@ -64271,11 +64277,11 @@ | |
| 64271 | u8 sharable; /* True if we can share pBt with another db */ |
| 64272 | u8 locked; /* True if db currently has pBt locked */ |
| 64273 | u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ |
| 64274 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 64275 | int nBackup; /* Number of backup operations reading this btree */ |
| 64276 | u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ |
| 64277 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 64278 | Btree *pPrev; /* Back pointer of the same list */ |
| 64279 | #ifdef SQLITE_DEBUG |
| 64280 | u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */ |
| 64281 | #endif |
| @@ -67684,23 +67690,27 @@ | |
| 67684 | /* |
| 67685 | ** Close an open database and invalidate all cursors. |
| 67686 | */ |
| 67687 | SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ |
| 67688 | BtShared *pBt = p->pBt; |
| 67689 | BtCursor *pCur; |
| 67690 | |
| 67691 | /* Close all cursors opened via this handle. */ |
| 67692 | assert( sqlite3_mutex_held(p->db->mutex) ); |
| 67693 | sqlite3BtreeEnter(p); |
| 67694 | pCur = pBt->pCursor; |
| 67695 | while( pCur ){ |
| 67696 | BtCursor *pTmp = pCur; |
| 67697 | pCur = pCur->pNext; |
| 67698 | if( pTmp->pBtree==p ){ |
| 67699 | sqlite3BtreeCloseCursor(pTmp); |
| 67700 | } |
| 67701 | } |
| 67702 | |
| 67703 | /* Rollback any active transaction and free the handle structure. |
| 67704 | ** The call to sqlite3BtreeRollback() drops any table-locks held by |
| 67705 | ** this handle. |
| 67706 | */ |
| @@ -69078,11 +69088,11 @@ | |
| 69078 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 69079 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 69080 | sqlite3BtreeLeave(p); |
| 69081 | return rc; |
| 69082 | } |
| 69083 | p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ |
| 69084 | pBt->inTransaction = TRANS_READ; |
| 69085 | btreeClearHasContent(pBt); |
| 69086 | } |
| 69087 | |
| 69088 | btreeEndTransaction(p); |
| @@ -69488,11 +69498,18 @@ | |
| 69488 | } |
| 69489 | btreeReleaseAllCursorPages(pCur); |
| 69490 | unlockBtreeIfUnused(pBt); |
| 69491 | sqlite3_free(pCur->aOverflow); |
| 69492 | sqlite3_free(pCur->pKey); |
| 69493 | sqlite3BtreeLeave(pBtree); |
| 69494 | pCur->pBtree = 0; |
| 69495 | } |
| 69496 | return SQLITE_OK; |
| 69497 | } |
| 69498 | |
| @@ -74601,11 +74618,11 @@ | |
| 74601 | assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) ); |
| 74602 | assert( pBt->pPage1 ); |
| 74603 | assert( idx>=0 && idx<=15 ); |
| 74604 | |
| 74605 | if( idx==BTREE_DATA_VERSION ){ |
| 74606 | *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; |
| 74607 | }else{ |
| 74608 | *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); |
| 74609 | } |
| 74610 | |
| 74611 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| @@ -80829,24 +80846,19 @@ | |
| 80829 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ |
| 80830 | if( pCx==0 ){ |
| 80831 | return; |
| 80832 | } |
| 80833 | assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); |
| 80834 | switch( pCx->eCurType ){ |
| 80835 | case CURTYPE_SORTER: { |
| 80836 | sqlite3VdbeSorterClose(p->db, pCx); |
| 80837 | break; |
| 80838 | } |
| 80839 | case CURTYPE_BTREE: { |
| 80840 | if( pCx->isEphemeral ){ |
| 80841 | if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx); |
| 80842 | /* The pCx->pCursor will be close automatically, if it exists, by |
| 80843 | ** the call above. */ |
| 80844 | }else{ |
| 80845 | assert( pCx->uc.pCursor!=0 ); |
| 80846 | sqlite3BtreeCloseCursor(pCx->uc.pCursor); |
| 80847 | } |
| 80848 | break; |
| 80849 | } |
| 80850 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 80851 | case CURTYPE_VTAB: { |
| 80852 | sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; |
| @@ -81926,10 +81938,11 @@ | |
| 81926 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ |
| 81927 | VdbeCursor *p = *pp; |
| 81928 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81929 | if( p->deferredMoveto ){ |
| 81930 | u32 iMap; |
| 81931 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81932 | *pp = p->pAltCursor; |
| 81933 | *piCol = iMap - 1; |
| 81934 | return SQLITE_OK; |
| 81935 | } |
| @@ -86134,15 +86147,10 @@ | |
| 86134 | ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + |
| 86135 | (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); |
| 86136 | |
| 86137 | assert( iCur>=0 && iCur<p->nCursor ); |
| 86138 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 86139 | /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag |
| 86140 | ** is clear. Otherwise, if this is an ephemeral cursor created by |
| 86141 | ** OP_OpenDup, the cursor will not be closed and will still be part |
| 86142 | ** of a BtShared.pCursor list. */ |
| 86143 | if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0; |
| 86144 | sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); |
| 86145 | p->apCsr[iCur] = 0; |
| 86146 | } |
| 86147 | if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ |
| 86148 | p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; |
| @@ -89824,21 +89832,24 @@ | |
| 89824 | VdbeCursor *pOrig; /* The original cursor to be duplicated */ |
| 89825 | VdbeCursor *pCx; /* The new cursor */ |
| 89826 | |
| 89827 | pOrig = p->apCsr[pOp->p2]; |
| 89828 | assert( pOrig ); |
| 89829 | assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */ |
| 89830 | |
| 89831 | pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); |
| 89832 | if( pCx==0 ) goto no_mem; |
| 89833 | pCx->nullRow = 1; |
| 89834 | pCx->isEphemeral = 1; |
| 89835 | pCx->pKeyInfo = pOrig->pKeyInfo; |
| 89836 | pCx->isTable = pOrig->isTable; |
| 89837 | pCx->pgnoRoot = pOrig->pgnoRoot; |
| 89838 | pCx->isOrdered = pOrig->isOrdered; |
| 89839 | rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, |
| 89840 | pCx->pKeyInfo, pCx->uc.pCursor); |
| 89841 | /* The sqlite3BtreeCursor() routine can only fail for the first cursor |
| 89842 | ** opened for a database. Since there is already an open cursor when this |
| 89843 | ** opcode is run, the sqlite3BtreeCursor() cannot fail */ |
| 89844 | assert( rc==SQLITE_OK ); |
| @@ -89900,13 +89911,14 @@ | |
| 89900 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 89901 | aMem[pOp->p3].n = 0; |
| 89902 | aMem[pOp->p3].z = ""; |
| 89903 | } |
| 89904 | pCx = p->apCsr[pOp->p1]; |
| 89905 | if( pCx && ALWAYS(pCx->pBtx) ){ |
| 89906 | /* If the ephermeral table is already open, erase all existing content |
| 89907 | ** so that the table is empty again, rather than creating a new table. */ |
| 89908 | assert( pCx->isEphemeral ); |
| 89909 | pCx->seqCount = 0; |
| 89910 | pCx->cacheStatus = CACHE_STALE; |
| 89911 | rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); |
| 89912 | }else{ |
| @@ -89916,37 +89928,40 @@ | |
| 89916 | rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, |
| 89917 | BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, |
| 89918 | vfsFlags); |
| 89919 | if( rc==SQLITE_OK ){ |
| 89920 | rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); |
| 89921 | } |
| 89922 | if( rc==SQLITE_OK ){ |
| 89923 | /* If a transient index is required, create it by calling |
| 89924 | ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before |
| 89925 | ** opening it. If a transient table is required, just use the |
| 89926 | ** automatically created table with root-page 1 (an BLOB_INTKEY table). |
| 89927 | */ |
| 89928 | if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ |
| 89929 | assert( pOp->p4type==P4_KEYINFO ); |
| 89930 | rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, |
| 89931 | BTREE_BLOBKEY | pOp->p5); |
| 89932 | if( rc==SQLITE_OK ){ |
| 89933 | assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); |
| 89934 | assert( pKeyInfo->db==db ); |
| 89935 | assert( pKeyInfo->enc==ENC(db) ); |
| 89936 | rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, |
| 89937 | pKeyInfo, pCx->uc.pCursor); |
| 89938 | } |
| 89939 | pCx->isTable = 0; |
| 89940 | }else{ |
| 89941 | pCx->pgnoRoot = SCHEMA_ROOT; |
| 89942 | rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, |
| 89943 | 0, pCx->uc.pCursor); |
| 89944 | pCx->isTable = 1; |
| 89945 | } |
| 89946 | } |
| 89947 | pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); |
| 89948 | } |
| 89949 | if( rc ) goto abort_due_to_error; |
| 89950 | pCx->nullRow = 1; |
| 89951 | break; |
| 89952 | } |
| @@ -90376,17 +90391,17 @@ | |
| 90376 | ** row by, perhaps by invoking sqlite3BtreeStep() on the cursor |
| 90377 | ** between 0 and This.P1 times. |
| 90378 | ** |
| 90379 | ** There are three possible outcomes from this opcode:<ol> |
| 90380 | ** |
| 90381 | ** <li> If after This.P1 steps, the cursor is still point to a place that |
| 90382 | ** is earlier in the btree than the target row, |
| 90383 | ** then fall through into the subsquence OP_SeekGE opcode. |
| 90384 | ** |
| 90385 | ** <li> If the cursor is successfully moved to the target row by 0 or more |
| 90386 | ** sqlite3BtreeNext() calls, then jump to This.P2, which will land just |
| 90387 | ** past the OP_IdxGT opcode that follows the OP_SeekGE. |
| 90388 | ** |
| 90389 | ** <li> If the cursor ends up past the target row (indicating the the target |
| 90390 | ** row does not exist in the btree) then jump to SeekOP.P2. |
| 90391 | ** </ol> |
| 90392 | */ |
| @@ -90399,11 +90414,12 @@ | |
| 90399 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90400 | |
| 90401 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90402 | ** follows the OP_SeekGE. */ |
| 90403 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90404 | assert( aOp[pOp->p2-1].opcode==OP_IdxGT ); |
| 90405 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90406 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90407 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90408 | |
| 90409 | assert( pOp->p1>0 ); |
| @@ -91934,10 +91950,12 @@ | |
| 91934 | pTabCur->nullRow = 0; |
| 91935 | pTabCur->movetoTarget = rowid; |
| 91936 | pTabCur->deferredMoveto = 1; |
| 91937 | assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); |
| 91938 | pTabCur->aAltMap = pOp->p4.ai; |
| 91939 | pTabCur->pAltCursor = pC; |
| 91940 | }else{ |
| 91941 | pOut = out2Prerelease(p, pOp); |
| 91942 | pOut->u.i = rowid; |
| 91943 | } |
| @@ -99165,10 +99183,11 @@ | |
| 99165 | */ |
| 99166 | if( cnt==0 && zTab==0 ){ |
| 99167 | assert( pExpr->op==TK_ID ); |
| 99168 | if( ExprHasProperty(pExpr,EP_DblQuoted) |
| 99169 | && areDoubleQuotedStringsEnabled(db, pTopNC) |
| 99170 | ){ |
| 99171 | /* If a double-quoted identifier does not match any known column name, |
| 99172 | ** then treat it as a string. |
| 99173 | ** |
| 99174 | ** This hack was added in the early days of SQLite in a misguided attempt |
| @@ -99179,10 +99198,15 @@ | |
| 99179 | ** programmers. To all those frustrated programmers, my apologies. |
| 99180 | ** |
| 99181 | ** Someday, I hope to get rid of this hack. Unfortunately there is |
| 99182 | ** a huge amount of legacy SQL that uses it. So for now, we just |
| 99183 | ** issue a warning. |
| 99184 | */ |
| 99185 | sqlite3_log(SQLITE_WARNING, |
| 99186 | "double-quoted string literal: \"%w\"", zCol); |
| 99187 | #ifdef SQLITE_ENABLE_NORMALIZE |
| 99188 | sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); |
| @@ -106778,31 +106802,32 @@ | |
| 106778 | */ |
| 106779 | static void renameTestSchema( |
| 106780 | Parse *pParse, /* Parse context */ |
| 106781 | const char *zDb, /* Name of db to verify schema of */ |
| 106782 | int bTemp, /* True if this is the temp db */ |
| 106783 | const char *zWhen /* "when" part of error message */ |
| 106784 | ){ |
| 106785 | pParse->colNamesSet = 1; |
| 106786 | sqlite3NestedParse(pParse, |
| 106787 | "SELECT 1 " |
| 106788 | "FROM \"%w\"." DFLT_SCHEMA_TABLE " " |
| 106789 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106790 | " AND sql NOT LIKE 'create virtual%%'" |
| 106791 | " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ", |
| 106792 | zDb, |
| 106793 | zDb, bTemp, zWhen |
| 106794 | ); |
| 106795 | |
| 106796 | if( bTemp==0 ){ |
| 106797 | sqlite3NestedParse(pParse, |
| 106798 | "SELECT 1 " |
| 106799 | "FROM temp." DFLT_SCHEMA_TABLE " " |
| 106800 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106801 | " AND sql NOT LIKE 'create virtual%%'" |
| 106802 | " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ", |
| 106803 | zDb, zWhen |
| 106804 | ); |
| 106805 | } |
| 106806 | } |
| 106807 | |
| 106808 | /* |
| @@ -106961,11 +106986,11 @@ | |
| 106961 | sqlite3NestedParse(pParse, |
| 106962 | "UPDATE sqlite_temp_schema SET " |
| 106963 | "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " |
| 106964 | "tbl_name = " |
| 106965 | "CASE WHEN tbl_name=%Q COLLATE nocase AND " |
| 106966 | " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') " |
| 106967 | "THEN %Q ELSE tbl_name END " |
| 106968 | "WHERE type IN ('view', 'trigger')" |
| 106969 | , zDb, zTabName, zName, zTabName, zDb, zName); |
| 106970 | } |
| 106971 | |
| @@ -106981,11 +107006,11 @@ | |
| 106981 | sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); |
| 106982 | } |
| 106983 | #endif |
| 106984 | |
| 106985 | renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); |
| 106986 | renameTestSchema(pParse, zDb, iDb==1, "after rename"); |
| 106987 | |
| 106988 | exit_rename_table: |
| 106989 | sqlite3SrcListDelete(db, pSrc); |
| 106990 | sqlite3DbFree(db, zName); |
| 106991 | db->mDbFlags = savedDbFlags; |
| @@ -107349,11 +107374,11 @@ | |
| 107349 | zDb, pTab->zName, iCol, zNew, bQuote |
| 107350 | ); |
| 107351 | |
| 107352 | /* Drop and reload the database schema. */ |
| 107353 | renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); |
| 107354 | renameTestSchema(pParse, zDb, iSchema==1, "after rename"); |
| 107355 | |
| 107356 | exit_rename_column: |
| 107357 | sqlite3SrcListDelete(db, pSrc); |
| 107358 | sqlite3DbFree(db, zOld); |
| 107359 | sqlite3DbFree(db, zNew); |
| @@ -107773,16 +107798,21 @@ | |
| 107773 | static int renameParseSql( |
| 107774 | Parse *p, /* Memory to use for Parse object */ |
| 107775 | const char *zDb, /* Name of schema SQL belongs to */ |
| 107776 | sqlite3 *db, /* Database handle */ |
| 107777 | const char *zSql, /* SQL to parse */ |
| 107778 | int bTemp /* True if SQL is from temp schema */ |
| 107779 | ){ |
| 107780 | int rc; |
| 107781 | char *zErr = 0; |
| 107782 | |
| 107783 | db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); |
| 107784 | |
| 107785 | /* Parse the SQL statement passed as the first argument. If no error |
| 107786 | ** occurs and the parse does not result in a new table, index or |
| 107787 | ** trigger object, the database must be corrupt. */ |
| 107788 | memset(p, 0, sizeof(Parse)); |
| @@ -107811,10 +107841,11 @@ | |
| 107811 | } |
| 107812 | } |
| 107813 | #endif |
| 107814 | |
| 107815 | db->init.iDb = 0; |
| 107816 | return rc; |
| 107817 | } |
| 107818 | |
| 107819 | /* |
| 107820 | ** This function edits SQL statement zSql, replacing each token identified |
| @@ -108112,11 +108143,11 @@ | |
| 108112 | sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
| 108113 | |
| 108114 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108115 | db->xAuth = 0; |
| 108116 | #endif |
| 108117 | rc = renameParseSql(&sParse, zDb, db, zSql, bTemp); |
| 108118 | |
| 108119 | /* Find tokens that need to be replaced. */ |
| 108120 | memset(&sWalker, 0, sizeof(Walker)); |
| 108121 | sWalker.pParse = &sParse; |
| 108122 | sWalker.xExprCallback = renameColumnExprCb; |
| @@ -108154,16 +108185,16 @@ | |
| 108154 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108155 | } |
| 108156 | for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ |
| 108157 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108158 | } |
| 108159 | } |
| 108160 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| 108161 | for(i=0; i<sParse.pNewTable->nCol; i++){ |
| 108162 | sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); |
| 108163 | } |
| 108164 | #endif |
| 108165 | |
| 108166 | for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| 108167 | for(i=0; i<pFKey->nCol; i++){ |
| 108168 | if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ |
| 108169 | renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); |
| @@ -108316,11 +108347,11 @@ | |
| 108316 | sWalker.pParse = &sParse; |
| 108317 | sWalker.xExprCallback = renameTableExprCb; |
| 108318 | sWalker.xSelectCallback = renameTableSelectCb; |
| 108319 | sWalker.u.pRename = &sCtx; |
| 108320 | |
| 108321 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 108322 | |
| 108323 | if( rc==SQLITE_OK ){ |
| 108324 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108325 | if( sParse.pNewTable ){ |
| 108326 | Table *pTab = sParse.pNewTable; |
| @@ -108432,10 +108463,11 @@ | |
| 108432 | ** 1: SQL statement. |
| 108433 | ** 2: Object type ("view", "table", "trigger" or "index"). |
| 108434 | ** 3: Object name. |
| 108435 | ** 4: True if object is from temp schema. |
| 108436 | ** 5: "when" part of error message. |
| 108437 | ** |
| 108438 | ** Unless it finds an error, this function normally returns NULL. However, it |
| 108439 | ** returns integer value 1 if: |
| 108440 | ** |
| 108441 | ** * the SQL argument creates a trigger, and |
| @@ -108450,10 +108482,11 @@ | |
| 108450 | char const *zDb = (const char*)sqlite3_value_text(argv[0]); |
| 108451 | char const *zInput = (const char*)sqlite3_value_text(argv[1]); |
| 108452 | int bTemp = sqlite3_value_int(argv[4]); |
| 108453 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108454 | char const *zWhen = (const char*)sqlite3_value_text(argv[5]); |
| 108455 | |
| 108456 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108457 | sqlite3_xauth xAuth = db->xAuth; |
| 108458 | db->xAuth = 0; |
| 108459 | #endif |
| @@ -108460,11 +108493,11 @@ | |
| 108460 | |
| 108461 | UNUSED_PARAMETER(NotUsed); |
| 108462 | if( zDb && zInput ){ |
| 108463 | int rc; |
| 108464 | Parse sParse; |
| 108465 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 108466 | if( rc==SQLITE_OK ){ |
| 108467 | if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ |
| 108468 | NameContext sNC; |
| 108469 | memset(&sNC, 0, sizeof(sNC)); |
| 108470 | sNC.pParse = &sParse; |
| @@ -108528,11 +108561,11 @@ | |
| 108528 | sqlite3_xauth xAuth = db->xAuth; |
| 108529 | db->xAuth = 0; |
| 108530 | #endif |
| 108531 | |
| 108532 | UNUSED_PARAMETER(NotUsed); |
| 108533 | rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); |
| 108534 | if( rc!=SQLITE_OK ) goto drop_column_done; |
| 108535 | pTab = sParse.pNewTable; |
| 108536 | if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ |
| 108537 | /* This can happen if the sqlite_schema table is corrupt */ |
| 108538 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -108621,21 +108654,21 @@ | |
| 108621 | |
| 108622 | /* Edit the sqlite_schema table */ |
| 108623 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 108624 | assert( iDb>=0 ); |
| 108625 | zDb = db->aDb[iDb].zDbSName; |
| 108626 | renameTestSchema(pParse, zDb, iDb==1, ""); |
| 108627 | sqlite3NestedParse(pParse, |
| 108628 | "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " |
| 108629 | "sql = sqlite_drop_column(%d, sql, %d) " |
| 108630 | "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" |
| 108631 | , zDb, iDb, iCol, pTab->zName |
| 108632 | ); |
| 108633 | |
| 108634 | /* Drop and reload the database schema. */ |
| 108635 | renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); |
| 108636 | renameTestSchema(pParse, zDb, iDb==1, "after drop column"); |
| 108637 | |
| 108638 | /* Edit rows of table on disk */ |
| 108639 | if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ |
| 108640 | int i; |
| 108641 | int addr; |
| @@ -108691,11 +108724,11 @@ | |
| 108691 | */ |
| 108692 | SQLITE_PRIVATE void sqlite3AlterFunctions(void){ |
| 108693 | static FuncDef aAlterTableFuncs[] = { |
| 108694 | INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), |
| 108695 | INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), |
| 108696 | INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest), |
| 108697 | INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), |
| 108698 | }; |
| 108699 | sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); |
| 108700 | } |
| 108701 | #endif /* SQLITE_ALTER_TABLE */ |
| @@ -115663,11 +115696,15 @@ | |
| 115663 | } |
| 115664 | |
| 115665 | /* Clean up before exiting */ |
| 115666 | exit_create_index: |
| 115667 | if( pIndex ) sqlite3FreeIndex(db, pIndex); |
| 115668 | if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */ |
| 115669 | Index **ppFrom = &pTab->pIndex; |
| 115670 | Index *pThis; |
| 115671 | for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ |
| 115672 | Index *pNext; |
| 115673 | if( pThis->onError!=OE_Replace ) continue; |
| @@ -115677,10 +115714,20 @@ | |
| 115677 | pNext->pNext = pThis; |
| 115678 | ppFrom = &pNext->pNext; |
| 115679 | } |
| 115680 | break; |
| 115681 | } |
| 115682 | } |
| 115683 | sqlite3ExprDelete(db, pPIWhere); |
| 115684 | sqlite3ExprListDelete(db, pList); |
| 115685 | sqlite3SrcListDelete(db, pTblName); |
| 115686 | sqlite3DbFree(db, zName); |
| @@ -123093,11 +123140,13 @@ | |
| 123093 | pNx->pUpsertSrc = pTabList; |
| 123094 | pNx->regData = regData; |
| 123095 | pNx->iDataCur = iDataCur; |
| 123096 | pNx->iIdxCur = iIdxCur; |
| 123097 | if( pNx->pUpsertTarget ){ |
| 123098 | sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx); |
| 123099 | } |
| 123100 | pNx = pNx->pNextUpsert; |
| 123101 | }while( pNx!=0 ); |
| 123102 | } |
| 123103 | #endif |
| @@ -124515,11 +124564,11 @@ | |
| 124515 | int regData /* Data containing new record */ |
| 124516 | ){ |
| 124517 | Vdbe *v = pParse->pVdbe; |
| 124518 | int r = sqlite3GetTempReg(pParse); |
| 124519 | assert( !HasRowid(pTab) ); |
| 124520 | assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) ); |
| 124521 | sqlite3VdbeAddOp2(v, OP_Integer, 0, r); |
| 124522 | sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); |
| 124523 | sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); |
| 124524 | sqlite3ReleaseTempReg(pParse, r); |
| 124525 | } |
| @@ -133063,11 +133112,11 @@ | |
| 133063 | sqlite3HashInit(&ht); |
| 133064 | if( pEList ){ |
| 133065 | nCol = pEList->nExpr; |
| 133066 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| 133067 | testcase( aCol==0 ); |
| 133068 | if( nCol>32767 ) nCol = 32767; |
| 133069 | }else{ |
| 133070 | nCol = 0; |
| 133071 | aCol = 0; |
| 133072 | } |
| 133073 | assert( nCol==(i16)nCol ); |
| @@ -135116,10 +135165,11 @@ | |
| 135116 | |
| 135117 | /* Restriction (23) */ |
| 135118 | if( (p->selFlags & SF_Recursive) ) return 0; |
| 135119 | |
| 135120 | if( pSrc->nSrc>1 ){ |
| 135121 | aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); |
| 135122 | } |
| 135123 | } |
| 135124 | |
| 135125 | /***** If we reach this point, flattening is permitted. *****/ |
| @@ -135192,10 +135242,11 @@ | |
| 135192 | p->op = TK_ALL; |
| 135193 | pSubitem->pTab = pItemTab; |
| 135194 | if( pNew==0 ){ |
| 135195 | p->pPrior = pPrior; |
| 135196 | }else{ |
| 135197 | if( aCsrMap && db->mallocFailed==0 ){ |
| 135198 | renumberCursors(pParse, pNew, iFrom, aCsrMap); |
| 135199 | } |
| 135200 | pNew->pPrior = pPrior; |
| 135201 | if( pPrior ) pPrior->pNext = pNew; |
| @@ -136135,20 +136186,28 @@ | |
| 136135 | |
| 136136 | pCte->zCteErr = "circular reference: %s"; |
| 136137 | pSavedWith = pParse->pWith; |
| 136138 | pParse->pWith = pWith; |
| 136139 | if( pSel->selFlags & SF_Recursive ){ |
| 136140 | assert( pRecTerm!=0 ); |
| 136141 | assert( (pRecTerm->selFlags & SF_Recursive)==0 ); |
| 136142 | assert( pRecTerm->pNext!=0 ); |
| 136143 | assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); |
| 136144 | assert( pRecTerm->pWith==0 ); |
| 136145 | pRecTerm->pWith = pSel->pWith; |
| 136146 | sqlite3WalkSelect(pWalker, pRecTerm); |
| 136147 | pRecTerm->pWith = 0; |
| 136148 | }else{ |
| 136149 | sqlite3WalkSelect(pWalker, pSel); |
| 136150 | } |
| 136151 | pParse->pWith = pWith; |
| 136152 | |
| 136153 | for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); |
| 136154 | pEList = pLeft->pEList; |
| @@ -137453,11 +137512,13 @@ | |
| 137453 | sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); |
| 137454 | } |
| 137455 | sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); |
| 137456 | pSub->nSelectRow = pPrior->pSelect->nSelectRow; |
| 137457 | }else{ |
| 137458 | /* Generate a subroutine that will materialize the view. */ |
| 137459 | int topAddr; |
| 137460 | int onceAddr = 0; |
| 137461 | int retAddr; |
| 137462 | |
| 137463 | testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ |
| @@ -137480,11 +137541,11 @@ | |
| 137480 | if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); |
| 137481 | retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); |
| 137482 | VdbeComment((v, "end %s", pItem->pTab->zName)); |
| 137483 | sqlite3VdbeChangeP1(v, topAddr, retAddr); |
| 137484 | sqlite3ClearTempRegCache(pParse); |
| 137485 | if( pItem->fg.isCte ){ |
| 137486 | CteUse *pCteUse = pItem->u2.pCteUse; |
| 137487 | pCteUse->addrM9e = pItem->addrFillSub; |
| 137488 | pCteUse->regRtn = pItem->regReturn; |
| 137489 | pCteUse->iCur = pItem->iCursor; |
| 137490 | pCteUse->nRowEst = pSub->nSelectRow; |
| @@ -162285,11 +162346,11 @@ | |
| 162285 | /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 |
| 162286 | #endif |
| 162287 | #ifdef SQLITE_EBCDIC |
| 162288 | /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ |
| 162289 | /* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, |
| 162290 | /* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162291 | /* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162292 | /* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162293 | /* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, |
| 162294 | /* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, |
| 162295 | /* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, |
| @@ -229190,11 +229251,11 @@ | |
| 229190 | int nArg, /* Number of args */ |
| 229191 | sqlite3_value **apUnused /* Function arguments */ |
| 229192 | ){ |
| 229193 | assert( nArg==0 ); |
| 229194 | UNUSED_PARAM2(nArg, apUnused); |
| 229195 | sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT); |
| 229196 | } |
| 229197 | |
| 229198 | /* |
| 229199 | ** Return true if zName is the extension on one of the shadow tables used |
| 229200 | ** by this module. |
| @@ -234116,12 +234177,12 @@ | |
| 234116 | } |
| 234117 | #endif /* SQLITE_CORE */ |
| 234118 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 234119 | |
| 234120 | /************** End of stmt.c ************************************************/ |
| 234121 | #if __LINE__!=234121 |
| 234122 | #undef SQLITE_SOURCE_ID |
| 234123 | #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2" |
| 234124 | #endif |
| 234125 | /* Return the source-id for this library */ |
| 234126 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 234127 | /************************** End of sqlite3.c ******************************/ |
| 234128 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.35.3. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -1184,13 +1184,13 @@ | |
| 1184 | ** |
| 1185 | ** See also: [sqlite3_libversion()], |
| 1186 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1187 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1188 | */ |
| 1189 | #define SQLITE_VERSION "3.35.3" |
| 1190 | #define SQLITE_VERSION_NUMBER 3035003 |
| 1191 | #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c" |
| 1192 | |
| 1193 | /* |
| 1194 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1195 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1196 | ** |
| @@ -17005,11 +17005,14 @@ | |
| 17005 | u8 iDb; /* Which db file is being initialized */ |
| 17006 | u8 busy; /* TRUE if currently initializing */ |
| 17007 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 17008 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 17009 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| 17010 | unsigned bDropColumn : 1; /* Doing schema check after DROP COLUMN */ |
| 17011 | char **azInit; /* "type", "name", and "tbl_name" columns */ |
| 17012 | /* or if bDropColumn, then azInit[0] is the */ |
| 17013 | /* name of the column being dropped */ |
| 17014 | } init; |
| 17015 | int nVdbeActive; /* Number of VDBEs currently running */ |
| 17016 | int nVdbeRead; /* Number of active VDBEs that read or write */ |
| 17017 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 17018 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| @@ -21066,10 +21069,11 @@ | |
| 21069 | u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ |
| 21070 | #endif |
| 21071 | Bool isEphemeral:1; /* True for an ephemeral table */ |
| 21072 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 21073 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 21074 | Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ |
| 21075 | u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 21076 | Btree *pBtx; /* Separate file holding temporary table */ |
| 21077 | i64 seqCount; /* Sequence counter */ |
| 21078 | u32 *aAltMap; /* Mapping from table to index column numbers */ |
| 21079 | |
| @@ -22866,10 +22870,11 @@ | |
| 22870 | int i, n; |
| 22871 | const unsigned char *z; |
| 22872 | int eType; |
| 22873 | memset(p, 0, sizeof(*p)); |
| 22874 | if( argc==0 ){ |
| 22875 | if( !sqlite3NotPureFunc(context) ) return 1; |
| 22876 | return setDateTimeToCurrent(context, p); |
| 22877 | } |
| 22878 | if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT |
| 22879 | || eType==SQLITE_INTEGER ){ |
| 22880 | setRawDateNumber(p, sqlite3_value_double(argv[0])); |
| @@ -40214,11 +40219,12 @@ | |
| 40219 | */ |
| 40220 | static int unixBackupDir(const char *z, int *pJ){ |
| 40221 | int j = *pJ; |
| 40222 | int i; |
| 40223 | if( j<=0 ) return 0; |
| 40224 | for(i=j-1; i>0 && z[i-1]!='/'; i--){} |
| 40225 | if( i==0 ) return 0; |
| 40226 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 40227 | *pJ = i-1; |
| 40228 | return 1; |
| 40229 | } |
| 40230 | |
| @@ -64271,11 +64277,11 @@ | |
| 64277 | u8 sharable; /* True if we can share pBt with another db */ |
| 64278 | u8 locked; /* True if db currently has pBt locked */ |
| 64279 | u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */ |
| 64280 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 64281 | int nBackup; /* Number of backup operations reading this btree */ |
| 64282 | u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */ |
| 64283 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 64284 | Btree *pPrev; /* Back pointer of the same list */ |
| 64285 | #ifdef SQLITE_DEBUG |
| 64286 | u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */ |
| 64287 | #endif |
| @@ -67684,23 +67690,27 @@ | |
| 67690 | /* |
| 67691 | ** Close an open database and invalidate all cursors. |
| 67692 | */ |
| 67693 | SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ |
| 67694 | BtShared *pBt = p->pBt; |
| 67695 | |
| 67696 | /* Close all cursors opened via this handle. */ |
| 67697 | assert( sqlite3_mutex_held(p->db->mutex) ); |
| 67698 | sqlite3BtreeEnter(p); |
| 67699 | |
| 67700 | /* Verify that no other cursors have this Btree open */ |
| 67701 | #ifdef SQLITE_DEBUG |
| 67702 | { |
| 67703 | BtCursor *pCur = pBt->pCursor; |
| 67704 | while( pCur ){ |
| 67705 | BtCursor *pTmp = pCur; |
| 67706 | pCur = pCur->pNext; |
| 67707 | assert( pTmp->pBtree!=p ); |
| 67708 | |
| 67709 | } |
| 67710 | } |
| 67711 | #endif |
| 67712 | |
| 67713 | /* Rollback any active transaction and free the handle structure. |
| 67714 | ** The call to sqlite3BtreeRollback() drops any table-locks held by |
| 67715 | ** this handle. |
| 67716 | */ |
| @@ -69078,11 +69088,11 @@ | |
| 69088 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 69089 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 69090 | sqlite3BtreeLeave(p); |
| 69091 | return rc; |
| 69092 | } |
| 69093 | p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */ |
| 69094 | pBt->inTransaction = TRANS_READ; |
| 69095 | btreeClearHasContent(pBt); |
| 69096 | } |
| 69097 | |
| 69098 | btreeEndTransaction(p); |
| @@ -69488,11 +69498,18 @@ | |
| 69498 | } |
| 69499 | btreeReleaseAllCursorPages(pCur); |
| 69500 | unlockBtreeIfUnused(pBt); |
| 69501 | sqlite3_free(pCur->aOverflow); |
| 69502 | sqlite3_free(pCur->pKey); |
| 69503 | if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ |
| 69504 | /* Since the BtShared is not sharable, there is no need to |
| 69505 | ** worry about the missing sqlite3BtreeLeave() call here. */ |
| 69506 | assert( pBtree->sharable==0 ); |
| 69507 | sqlite3BtreeClose(pBtree); |
| 69508 | }else{ |
| 69509 | sqlite3BtreeLeave(pBtree); |
| 69510 | } |
| 69511 | pCur->pBtree = 0; |
| 69512 | } |
| 69513 | return SQLITE_OK; |
| 69514 | } |
| 69515 | |
| @@ -74601,11 +74618,11 @@ | |
| 74618 | assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) ); |
| 74619 | assert( pBt->pPage1 ); |
| 74620 | assert( idx>=0 && idx<=15 ); |
| 74621 | |
| 74622 | if( idx==BTREE_DATA_VERSION ){ |
| 74623 | *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion; |
| 74624 | }else{ |
| 74625 | *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); |
| 74626 | } |
| 74627 | |
| 74628 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| @@ -80829,24 +80846,19 @@ | |
| 80846 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ |
| 80847 | if( pCx==0 ){ |
| 80848 | return; |
| 80849 | } |
| 80850 | assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE ); |
| 80851 | assert( pCx->pBtx==0 || pCx->isEphemeral ); |
| 80852 | switch( pCx->eCurType ){ |
| 80853 | case CURTYPE_SORTER: { |
| 80854 | sqlite3VdbeSorterClose(p->db, pCx); |
| 80855 | break; |
| 80856 | } |
| 80857 | case CURTYPE_BTREE: { |
| 80858 | assert( pCx->uc.pCursor!=0 ); |
| 80859 | sqlite3BtreeCloseCursor(pCx->uc.pCursor); |
| 80860 | break; |
| 80861 | } |
| 80862 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 80863 | case CURTYPE_VTAB: { |
| 80864 | sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; |
| @@ -81926,10 +81938,11 @@ | |
| 81938 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ |
| 81939 | VdbeCursor *p = *pp; |
| 81940 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81941 | if( p->deferredMoveto ){ |
| 81942 | u32 iMap; |
| 81943 | assert( !p->isEphemeral ); |
| 81944 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81945 | *pp = p->pAltCursor; |
| 81946 | *piCol = iMap - 1; |
| 81947 | return SQLITE_OK; |
| 81948 | } |
| @@ -86134,15 +86147,10 @@ | |
| 86147 | ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + |
| 86148 | (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); |
| 86149 | |
| 86150 | assert( iCur>=0 && iCur<p->nCursor ); |
| 86151 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 86152 | sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); |
| 86153 | p->apCsr[iCur] = 0; |
| 86154 | } |
| 86155 | if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ |
| 86156 | p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; |
| @@ -89824,21 +89832,24 @@ | |
| 89832 | VdbeCursor *pOrig; /* The original cursor to be duplicated */ |
| 89833 | VdbeCursor *pCx; /* The new cursor */ |
| 89834 | |
| 89835 | pOrig = p->apCsr[pOp->p2]; |
| 89836 | assert( pOrig ); |
| 89837 | assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ |
| 89838 | |
| 89839 | pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); |
| 89840 | if( pCx==0 ) goto no_mem; |
| 89841 | pCx->nullRow = 1; |
| 89842 | pCx->isEphemeral = 1; |
| 89843 | pCx->pKeyInfo = pOrig->pKeyInfo; |
| 89844 | pCx->isTable = pOrig->isTable; |
| 89845 | pCx->pgnoRoot = pOrig->pgnoRoot; |
| 89846 | pCx->isOrdered = pOrig->isOrdered; |
| 89847 | pCx->pBtx = pOrig->pBtx; |
| 89848 | pCx->hasBeenDuped = 1; |
| 89849 | pOrig->hasBeenDuped = 1; |
| 89850 | rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, |
| 89851 | pCx->pKeyInfo, pCx->uc.pCursor); |
| 89852 | /* The sqlite3BtreeCursor() routine can only fail for the first cursor |
| 89853 | ** opened for a database. Since there is already an open cursor when this |
| 89854 | ** opcode is run, the sqlite3BtreeCursor() cannot fail */ |
| 89855 | assert( rc==SQLITE_OK ); |
| @@ -89900,13 +89911,14 @@ | |
| 89911 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 89912 | aMem[pOp->p3].n = 0; |
| 89913 | aMem[pOp->p3].z = ""; |
| 89914 | } |
| 89915 | pCx = p->apCsr[pOp->p1]; |
| 89916 | if( pCx && !pCx->hasBeenDuped ){ |
| 89917 | /* If the ephermeral table is already open and has no duplicates from |
| 89918 | ** OP_OpenDup, then erase all existing content so that the table is |
| 89919 | ** empty again, rather than creating a new table. */ |
| 89920 | assert( pCx->isEphemeral ); |
| 89921 | pCx->seqCount = 0; |
| 89922 | pCx->cacheStatus = CACHE_STALE; |
| 89923 | rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); |
| 89924 | }else{ |
| @@ -89916,37 +89928,40 @@ | |
| 89928 | rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, |
| 89929 | BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, |
| 89930 | vfsFlags); |
| 89931 | if( rc==SQLITE_OK ){ |
| 89932 | rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); |
| 89933 | if( rc==SQLITE_OK ){ |
| 89934 | /* If a transient index is required, create it by calling |
| 89935 | ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before |
| 89936 | ** opening it. If a transient table is required, just use the |
| 89937 | ** automatically created table with root-page 1 (an BLOB_INTKEY table). |
| 89938 | */ |
| 89939 | if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ |
| 89940 | assert( pOp->p4type==P4_KEYINFO ); |
| 89941 | rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, |
| 89942 | BTREE_BLOBKEY | pOp->p5); |
| 89943 | if( rc==SQLITE_OK ){ |
| 89944 | assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); |
| 89945 | assert( pKeyInfo->db==db ); |
| 89946 | assert( pKeyInfo->enc==ENC(db) ); |
| 89947 | rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, |
| 89948 | pKeyInfo, pCx->uc.pCursor); |
| 89949 | } |
| 89950 | pCx->isTable = 0; |
| 89951 | }else{ |
| 89952 | pCx->pgnoRoot = SCHEMA_ROOT; |
| 89953 | rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, |
| 89954 | 0, pCx->uc.pCursor); |
| 89955 | pCx->isTable = 1; |
| 89956 | } |
| 89957 | } |
| 89958 | pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); |
| 89959 | if( rc ){ |
| 89960 | sqlite3BtreeClose(pCx->pBtx); |
| 89961 | } |
| 89962 | } |
| 89963 | } |
| 89964 | if( rc ) goto abort_due_to_error; |
| 89965 | pCx->nullRow = 1; |
| 89966 | break; |
| 89967 | } |
| @@ -90376,17 +90391,17 @@ | |
| 90391 | ** row by, perhaps by invoking sqlite3BtreeStep() on the cursor |
| 90392 | ** between 0 and This.P1 times. |
| 90393 | ** |
| 90394 | ** There are three possible outcomes from this opcode:<ol> |
| 90395 | ** |
| 90396 | ** <li> If after This.P1 steps, the cursor is still pointing to a place that |
| 90397 | ** is earlier in the btree than the target row, then fall through |
| 90398 | ** into the subsquence OP_SeekGE opcode. |
| 90399 | ** |
| 90400 | ** <li> If the cursor is successfully moved to the target row by 0 or more |
| 90401 | ** sqlite3BtreeNext() calls, then jump to This.P2, which will land just |
| 90402 | ** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE. |
| 90403 | ** |
| 90404 | ** <li> If the cursor ends up past the target row (indicating the the target |
| 90405 | ** row does not exist in the btree) then jump to SeekOP.P2. |
| 90406 | ** </ol> |
| 90407 | */ |
| @@ -90399,11 +90414,12 @@ | |
| 90414 | assert( pOp[1].opcode==OP_SeekGE ); |
| 90415 | |
| 90416 | /* pOp->p2 points to the first instruction past the OP_IdxGT that |
| 90417 | ** follows the OP_SeekGE. */ |
| 90418 | assert( pOp->p2>=(int)(pOp-aOp)+2 ); |
| 90419 | assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE ); |
| 90420 | testcase( aOp[pOp->p2-1].opcode==OP_IdxGE ); |
| 90421 | assert( pOp[1].p1==aOp[pOp->p2-1].p1 ); |
| 90422 | assert( pOp[1].p2==aOp[pOp->p2-1].p2 ); |
| 90423 | assert( pOp[1].p3==aOp[pOp->p2-1].p3 ); |
| 90424 | |
| 90425 | assert( pOp->p1>0 ); |
| @@ -91934,10 +91950,12 @@ | |
| 91950 | pTabCur->nullRow = 0; |
| 91951 | pTabCur->movetoTarget = rowid; |
| 91952 | pTabCur->deferredMoveto = 1; |
| 91953 | assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); |
| 91954 | pTabCur->aAltMap = pOp->p4.ai; |
| 91955 | assert( !pC->isEphemeral ); |
| 91956 | assert( !pTabCur->isEphemeral ); |
| 91957 | pTabCur->pAltCursor = pC; |
| 91958 | }else{ |
| 91959 | pOut = out2Prerelease(p, pOp); |
| 91960 | pOut->u.i = rowid; |
| 91961 | } |
| @@ -99165,10 +99183,11 @@ | |
| 99183 | */ |
| 99184 | if( cnt==0 && zTab==0 ){ |
| 99185 | assert( pExpr->op==TK_ID ); |
| 99186 | if( ExprHasProperty(pExpr,EP_DblQuoted) |
| 99187 | && areDoubleQuotedStringsEnabled(db, pTopNC) |
| 99188 | && (db->init.bDropColumn==0 || sqlite3StrICmp(zCol, db->init.azInit[0])!=0) |
| 99189 | ){ |
| 99190 | /* If a double-quoted identifier does not match any known column name, |
| 99191 | ** then treat it as a string. |
| 99192 | ** |
| 99193 | ** This hack was added in the early days of SQLite in a misguided attempt |
| @@ -99179,10 +99198,15 @@ | |
| 99198 | ** programmers. To all those frustrated programmers, my apologies. |
| 99199 | ** |
| 99200 | ** Someday, I hope to get rid of this hack. Unfortunately there is |
| 99201 | ** a huge amount of legacy SQL that uses it. So for now, we just |
| 99202 | ** issue a warning. |
| 99203 | ** |
| 99204 | ** 2021-03-15: ticket 1c24a659e6d7f3a1 |
| 99205 | ** Do not do the ID-to-STRING conversion when doing the schema |
| 99206 | ** sanity check following a DROP COLUMN if the identifer name matches |
| 99207 | ** the name of the column being dropped. |
| 99208 | */ |
| 99209 | sqlite3_log(SQLITE_WARNING, |
| 99210 | "double-quoted string literal: \"%w\"", zCol); |
| 99211 | #ifdef SQLITE_ENABLE_NORMALIZE |
| 99212 | sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); |
| @@ -106778,31 +106802,32 @@ | |
| 106802 | */ |
| 106803 | static void renameTestSchema( |
| 106804 | Parse *pParse, /* Parse context */ |
| 106805 | const char *zDb, /* Name of db to verify schema of */ |
| 106806 | int bTemp, /* True if this is the temp db */ |
| 106807 | const char *zWhen, /* "when" part of error message */ |
| 106808 | const char *zDropColumn /* Name of column being dropped */ |
| 106809 | ){ |
| 106810 | pParse->colNamesSet = 1; |
| 106811 | sqlite3NestedParse(pParse, |
| 106812 | "SELECT 1 " |
| 106813 | "FROM \"%w\"." DFLT_SCHEMA_TABLE " " |
| 106814 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106815 | " AND sql NOT LIKE 'create virtual%%'" |
| 106816 | " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %Q)=NULL ", |
| 106817 | zDb, |
| 106818 | zDb, bTemp, zWhen, zDropColumn |
| 106819 | ); |
| 106820 | |
| 106821 | if( bTemp==0 ){ |
| 106822 | sqlite3NestedParse(pParse, |
| 106823 | "SELECT 1 " |
| 106824 | "FROM temp." DFLT_SCHEMA_TABLE " " |
| 106825 | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| 106826 | " AND sql NOT LIKE 'create virtual%%'" |
| 106827 | " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %Q)=NULL ", |
| 106828 | zDb, zWhen, zDropColumn |
| 106829 | ); |
| 106830 | } |
| 106831 | } |
| 106832 | |
| 106833 | /* |
| @@ -106961,11 +106986,11 @@ | |
| 106986 | sqlite3NestedParse(pParse, |
| 106987 | "UPDATE sqlite_temp_schema SET " |
| 106988 | "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " |
| 106989 | "tbl_name = " |
| 106990 | "CASE WHEN tbl_name=%Q COLLATE nocase AND " |
| 106991 | " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename',0) " |
| 106992 | "THEN %Q ELSE tbl_name END " |
| 106993 | "WHERE type IN ('view', 'trigger')" |
| 106994 | , zDb, zTabName, zName, zTabName, zDb, zName); |
| 106995 | } |
| 106996 | |
| @@ -106981,11 +107006,11 @@ | |
| 107006 | sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); |
| 107007 | } |
| 107008 | #endif |
| 107009 | |
| 107010 | renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); |
| 107011 | renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); |
| 107012 | |
| 107013 | exit_rename_table: |
| 107014 | sqlite3SrcListDelete(db, pSrc); |
| 107015 | sqlite3DbFree(db, zName); |
| 107016 | db->mDbFlags = savedDbFlags; |
| @@ -107349,11 +107374,11 @@ | |
| 107374 | zDb, pTab->zName, iCol, zNew, bQuote |
| 107375 | ); |
| 107376 | |
| 107377 | /* Drop and reload the database schema. */ |
| 107378 | renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); |
| 107379 | renameTestSchema(pParse, zDb, iSchema==1, "after rename", 0); |
| 107380 | |
| 107381 | exit_rename_column: |
| 107382 | sqlite3SrcListDelete(db, pSrc); |
| 107383 | sqlite3DbFree(db, zOld); |
| 107384 | sqlite3DbFree(db, zNew); |
| @@ -107773,16 +107798,21 @@ | |
| 107798 | static int renameParseSql( |
| 107799 | Parse *p, /* Memory to use for Parse object */ |
| 107800 | const char *zDb, /* Name of schema SQL belongs to */ |
| 107801 | sqlite3 *db, /* Database handle */ |
| 107802 | const char *zSql, /* SQL to parse */ |
| 107803 | int bTemp, /* True if SQL is from temp schema */ |
| 107804 | const char *zDropColumn /* Name of column being dropped */ |
| 107805 | ){ |
| 107806 | int rc; |
| 107807 | char *zErr = 0; |
| 107808 | |
| 107809 | db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); |
| 107810 | if( zDropColumn ){ |
| 107811 | db->init.bDropColumn = 1; |
| 107812 | db->init.azInit = (char**)&zDropColumn; |
| 107813 | } |
| 107814 | |
| 107815 | /* Parse the SQL statement passed as the first argument. If no error |
| 107816 | ** occurs and the parse does not result in a new table, index or |
| 107817 | ** trigger object, the database must be corrupt. */ |
| 107818 | memset(p, 0, sizeof(Parse)); |
| @@ -107811,10 +107841,11 @@ | |
| 107841 | } |
| 107842 | } |
| 107843 | #endif |
| 107844 | |
| 107845 | db->init.iDb = 0; |
| 107846 | db->init.bDropColumn = 0; |
| 107847 | return rc; |
| 107848 | } |
| 107849 | |
| 107850 | /* |
| 107851 | ** This function edits SQL statement zSql, replacing each token identified |
| @@ -108112,11 +108143,11 @@ | |
| 108143 | sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
| 108144 | |
| 108145 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108146 | db->xAuth = 0; |
| 108147 | #endif |
| 108148 | rc = renameParseSql(&sParse, zDb, db, zSql, bTemp, 0); |
| 108149 | |
| 108150 | /* Find tokens that need to be replaced. */ |
| 108151 | memset(&sWalker, 0, sizeof(Walker)); |
| 108152 | sWalker.pParse = &sParse; |
| 108153 | sWalker.xExprCallback = renameColumnExprCb; |
| @@ -108154,16 +108185,16 @@ | |
| 108185 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108186 | } |
| 108187 | for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ |
| 108188 | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| 108189 | } |
| 108190 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| 108191 | for(i=0; i<sParse.pNewTable->nCol; i++){ |
| 108192 | sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt); |
| 108193 | } |
| 108194 | #endif |
| 108195 | } |
| 108196 | |
| 108197 | for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| 108198 | for(i=0; i<pFKey->nCol; i++){ |
| 108199 | if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ |
| 108200 | renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); |
| @@ -108316,11 +108347,11 @@ | |
| 108347 | sWalker.pParse = &sParse; |
| 108348 | sWalker.xExprCallback = renameTableExprCb; |
| 108349 | sWalker.xSelectCallback = renameTableSelectCb; |
| 108350 | sWalker.u.pRename = &sCtx; |
| 108351 | |
| 108352 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, 0); |
| 108353 | |
| 108354 | if( rc==SQLITE_OK ){ |
| 108355 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108356 | if( sParse.pNewTable ){ |
| 108357 | Table *pTab = sParse.pNewTable; |
| @@ -108432,10 +108463,11 @@ | |
| 108463 | ** 1: SQL statement. |
| 108464 | ** 2: Object type ("view", "table", "trigger" or "index"). |
| 108465 | ** 3: Object name. |
| 108466 | ** 4: True if object is from temp schema. |
| 108467 | ** 5: "when" part of error message. |
| 108468 | ** 6: Name of column being dropped, or NULL. |
| 108469 | ** |
| 108470 | ** Unless it finds an error, this function normally returns NULL. However, it |
| 108471 | ** returns integer value 1 if: |
| 108472 | ** |
| 108473 | ** * the SQL argument creates a trigger, and |
| @@ -108450,10 +108482,11 @@ | |
| 108482 | char const *zDb = (const char*)sqlite3_value_text(argv[0]); |
| 108483 | char const *zInput = (const char*)sqlite3_value_text(argv[1]); |
| 108484 | int bTemp = sqlite3_value_int(argv[4]); |
| 108485 | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| 108486 | char const *zWhen = (const char*)sqlite3_value_text(argv[5]); |
| 108487 | char const *zDropColumn = (const char*)sqlite3_value_text(argv[6]); |
| 108488 | |
| 108489 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 108490 | sqlite3_xauth xAuth = db->xAuth; |
| 108491 | db->xAuth = 0; |
| 108492 | #endif |
| @@ -108460,11 +108493,11 @@ | |
| 108493 | |
| 108494 | UNUSED_PARAMETER(NotUsed); |
| 108495 | if( zDb && zInput ){ |
| 108496 | int rc; |
| 108497 | Parse sParse; |
| 108498 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, zDropColumn); |
| 108499 | if( rc==SQLITE_OK ){ |
| 108500 | if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){ |
| 108501 | NameContext sNC; |
| 108502 | memset(&sNC, 0, sizeof(sNC)); |
| 108503 | sNC.pParse = &sParse; |
| @@ -108528,11 +108561,11 @@ | |
| 108561 | sqlite3_xauth xAuth = db->xAuth; |
| 108562 | db->xAuth = 0; |
| 108563 | #endif |
| 108564 | |
| 108565 | UNUSED_PARAMETER(NotUsed); |
| 108566 | rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1, 0); |
| 108567 | if( rc!=SQLITE_OK ) goto drop_column_done; |
| 108568 | pTab = sParse.pNewTable; |
| 108569 | if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ |
| 108570 | /* This can happen if the sqlite_schema table is corrupt */ |
| 108571 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -108621,21 +108654,21 @@ | |
| 108654 | |
| 108655 | /* Edit the sqlite_schema table */ |
| 108656 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 108657 | assert( iDb>=0 ); |
| 108658 | zDb = db->aDb[iDb].zDbSName; |
| 108659 | renameTestSchema(pParse, zDb, iDb==1, "", 0); |
| 108660 | sqlite3NestedParse(pParse, |
| 108661 | "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET " |
| 108662 | "sql = sqlite_drop_column(%d, sql, %d) " |
| 108663 | "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" |
| 108664 | , zDb, iDb, iCol, pTab->zName |
| 108665 | ); |
| 108666 | |
| 108667 | /* Drop and reload the database schema. */ |
| 108668 | renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); |
| 108669 | renameTestSchema(pParse, zDb, iDb==1, "after drop column", zCol); |
| 108670 | |
| 108671 | /* Edit rows of table on disk */ |
| 108672 | if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ |
| 108673 | int i; |
| 108674 | int addr; |
| @@ -108691,11 +108724,11 @@ | |
| 108724 | */ |
| 108725 | SQLITE_PRIVATE void sqlite3AlterFunctions(void){ |
| 108726 | static FuncDef aAlterTableFuncs[] = { |
| 108727 | INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), |
| 108728 | INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), |
| 108729 | INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), |
| 108730 | INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), |
| 108731 | }; |
| 108732 | sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); |
| 108733 | } |
| 108734 | #endif /* SQLITE_ALTER_TABLE */ |
| @@ -115663,11 +115696,15 @@ | |
| 115696 | } |
| 115697 | |
| 115698 | /* Clean up before exiting */ |
| 115699 | exit_create_index: |
| 115700 | if( pIndex ) sqlite3FreeIndex(db, pIndex); |
| 115701 | if( pTab ){ |
| 115702 | /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list. |
| 115703 | ** The list was already ordered when this routine was entered, so at this |
| 115704 | ** point at most a single index (the newly added index) will be out of |
| 115705 | ** order. So we have to reorder at most one index. */ |
| 115706 | Index **ppFrom = &pTab->pIndex; |
| 115707 | Index *pThis; |
| 115708 | for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){ |
| 115709 | Index *pNext; |
| 115710 | if( pThis->onError!=OE_Replace ) continue; |
| @@ -115677,10 +115714,20 @@ | |
| 115714 | pNext->pNext = pThis; |
| 115715 | ppFrom = &pNext->pNext; |
| 115716 | } |
| 115717 | break; |
| 115718 | } |
| 115719 | #ifdef SQLITE_DEBUG |
| 115720 | /* Verify that all REPLACE indexes really are now at the end |
| 115721 | ** of the index list. In other words, no other index type ever |
| 115722 | ** comes after a REPLACE index on the list. */ |
| 115723 | for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){ |
| 115724 | assert( pThis->onError!=OE_Replace |
| 115725 | || pThis->pNext==0 |
| 115726 | || pThis->pNext->onError==OE_Replace ); |
| 115727 | } |
| 115728 | #endif |
| 115729 | } |
| 115730 | sqlite3ExprDelete(db, pPIWhere); |
| 115731 | sqlite3ExprListDelete(db, pList); |
| 115732 | sqlite3SrcListDelete(db, pTblName); |
| 115733 | sqlite3DbFree(db, zName); |
| @@ -123093,11 +123140,13 @@ | |
| 123140 | pNx->pUpsertSrc = pTabList; |
| 123141 | pNx->regData = regData; |
| 123142 | pNx->iDataCur = iDataCur; |
| 123143 | pNx->iIdxCur = iIdxCur; |
| 123144 | if( pNx->pUpsertTarget ){ |
| 123145 | if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){ |
| 123146 | goto insert_cleanup; |
| 123147 | } |
| 123148 | } |
| 123149 | pNx = pNx->pNextUpsert; |
| 123150 | }while( pNx!=0 ); |
| 123151 | } |
| 123152 | #endif |
| @@ -124515,11 +124564,11 @@ | |
| 124564 | int regData /* Data containing new record */ |
| 124565 | ){ |
| 124566 | Vdbe *v = pParse->pVdbe; |
| 124567 | int r = sqlite3GetTempReg(pParse); |
| 124568 | assert( !HasRowid(pTab) ); |
| 124569 | assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB ); |
| 124570 | sqlite3VdbeAddOp2(v, OP_Integer, 0, r); |
| 124571 | sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE); |
| 124572 | sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); |
| 124573 | sqlite3ReleaseTempReg(pParse, r); |
| 124574 | } |
| @@ -133063,11 +133112,11 @@ | |
| 133112 | sqlite3HashInit(&ht); |
| 133113 | if( pEList ){ |
| 133114 | nCol = pEList->nExpr; |
| 133115 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| 133116 | testcase( aCol==0 ); |
| 133117 | if( NEVER(nCol>32767) ) nCol = 32767; |
| 133118 | }else{ |
| 133119 | nCol = 0; |
| 133120 | aCol = 0; |
| 133121 | } |
| 133122 | assert( nCol==(i16)nCol ); |
| @@ -135116,10 +135165,11 @@ | |
| 135165 | |
| 135166 | /* Restriction (23) */ |
| 135167 | if( (p->selFlags & SF_Recursive) ) return 0; |
| 135168 | |
| 135169 | if( pSrc->nSrc>1 ){ |
| 135170 | if( pParse->nSelect>500 ) return 0; |
| 135171 | aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int)); |
| 135172 | } |
| 135173 | } |
| 135174 | |
| 135175 | /***** If we reach this point, flattening is permitted. *****/ |
| @@ -135192,10 +135242,11 @@ | |
| 135242 | p->op = TK_ALL; |
| 135243 | pSubitem->pTab = pItemTab; |
| 135244 | if( pNew==0 ){ |
| 135245 | p->pPrior = pPrior; |
| 135246 | }else{ |
| 135247 | pNew->selId = ++pParse->nSelect; |
| 135248 | if( aCsrMap && db->mallocFailed==0 ){ |
| 135249 | renumberCursors(pParse, pNew, iFrom, aCsrMap); |
| 135250 | } |
| 135251 | pNew->pPrior = pPrior; |
| 135252 | if( pPrior ) pPrior->pNext = pNew; |
| @@ -136135,20 +136186,28 @@ | |
| 136186 | |
| 136187 | pCte->zCteErr = "circular reference: %s"; |
| 136188 | pSavedWith = pParse->pWith; |
| 136189 | pParse->pWith = pWith; |
| 136190 | if( pSel->selFlags & SF_Recursive ){ |
| 136191 | int rc; |
| 136192 | assert( pRecTerm!=0 ); |
| 136193 | assert( (pRecTerm->selFlags & SF_Recursive)==0 ); |
| 136194 | assert( pRecTerm->pNext!=0 ); |
| 136195 | assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 ); |
| 136196 | assert( pRecTerm->pWith==0 ); |
| 136197 | pRecTerm->pWith = pSel->pWith; |
| 136198 | rc = sqlite3WalkSelect(pWalker, pRecTerm); |
| 136199 | pRecTerm->pWith = 0; |
| 136200 | if( rc ){ |
| 136201 | pParse->pWith = pSavedWith; |
| 136202 | return 2; |
| 136203 | } |
| 136204 | }else{ |
| 136205 | if( sqlite3WalkSelect(pWalker, pSel) ){ |
| 136206 | pParse->pWith = pSavedWith; |
| 136207 | return 2; |
| 136208 | } |
| 136209 | } |
| 136210 | pParse->pWith = pWith; |
| 136211 | |
| 136212 | for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); |
| 136213 | pEList = pLeft->pEList; |
| @@ -137453,11 +137512,13 @@ | |
| 137512 | sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub); |
| 137513 | } |
| 137514 | sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); |
| 137515 | pSub->nSelectRow = pPrior->pSelect->nSelectRow; |
| 137516 | }else{ |
| 137517 | /* Materalize the view. If the view is not correlated, generate a |
| 137518 | ** subroutine to do the materialization so that subsequent uses of |
| 137519 | ** the same view can reuse the materialization. */ |
| 137520 | int topAddr; |
| 137521 | int onceAddr = 0; |
| 137522 | int retAddr; |
| 137523 | |
| 137524 | testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */ |
| @@ -137480,11 +137541,11 @@ | |
| 137541 | if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); |
| 137542 | retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn); |
| 137543 | VdbeComment((v, "end %s", pItem->pTab->zName)); |
| 137544 | sqlite3VdbeChangeP1(v, topAddr, retAddr); |
| 137545 | sqlite3ClearTempRegCache(pParse); |
| 137546 | if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ |
| 137547 | CteUse *pCteUse = pItem->u2.pCteUse; |
| 137548 | pCteUse->addrM9e = pItem->addrFillSub; |
| 137549 | pCteUse->regRtn = pItem->regReturn; |
| 137550 | pCteUse->iCur = pItem->iCursor; |
| 137551 | pCteUse->nRowEst = pSub->nSelectRow; |
| @@ -162285,11 +162346,11 @@ | |
| 162346 | /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 |
| 162347 | #endif |
| 162348 | #ifdef SQLITE_EBCDIC |
| 162349 | /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ |
| 162350 | /* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28, |
| 162351 | /* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162352 | /* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162353 | /* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, |
| 162354 | /* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10, |
| 162355 | /* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28, |
| 162356 | /* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6, |
| @@ -229190,11 +229251,11 @@ | |
| 229251 | int nArg, /* Number of args */ |
| 229252 | sqlite3_value **apUnused /* Function arguments */ |
| 229253 | ){ |
| 229254 | assert( nArg==0 ); |
| 229255 | UNUSED_PARAM2(nArg, apUnused); |
| 229256 | sqlite3_result_text(pCtx, "fts5: 2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c", -1, SQLITE_TRANSIENT); |
| 229257 | } |
| 229258 | |
| 229259 | /* |
| 229260 | ** Return true if zName is the extension on one of the shadow tables used |
| 229261 | ** by this module. |
| @@ -234116,12 +234177,12 @@ | |
| 234177 | } |
| 234178 | #endif /* SQLITE_CORE */ |
| 234179 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 234180 | |
| 234181 | /************** End of stmt.c ************************************************/ |
| 234182 | #if __LINE__!=234182 |
| 234183 | #undef SQLITE_SOURCE_ID |
| 234184 | #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4alt2" |
| 234185 | #endif |
| 234186 | /* Return the source-id for this library */ |
| 234187 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 234188 | /************************** End of sqlite3.c ******************************/ |
| 234189 |
+3
-3
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -121,13 +121,13 @@ | ||
| 121 | 121 | ** |
| 122 | 122 | ** See also: [sqlite3_libversion()], |
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | -#define SQLITE_VERSION "3.35.0" | |
| 127 | -#define SQLITE_VERSION_NUMBER 3035000 | |
| 128 | -#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b" | |
| 126 | +#define SQLITE_VERSION "3.35.3" | |
| 127 | +#define SQLITE_VERSION_NUMBER 3035003 | |
| 128 | +#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| 134 | 134 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -121,13 +121,13 @@ | |
| 121 | ** |
| 122 | ** See also: [sqlite3_libversion()], |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.35.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3035000 |
| 128 | #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -121,13 +121,13 @@ | |
| 121 | ** |
| 122 | ** See also: [sqlite3_libversion()], |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.35.3" |
| 127 | #define SQLITE_VERSION_NUMBER 3035003 |
| 128 | #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
M
src/th.c
+47
-3
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -222,11 +222,20 @@ | ||
| 222 | 222 | Buffer *pBuffer, |
| 223 | 223 | const char *zAdd, |
| 224 | 224 | int nAdd |
| 225 | 225 | ){ |
| 226 | 226 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 227 | +#if defined(TH_MEMDEBUG) | |
| 228 | + char *zNew = (char *)Th_Malloc(interp, nNew); | |
| 229 | + th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); | |
| 230 | + Th_Free(interp, pBuffer->zBuf); | |
| 231 | + pBuffer->zBuf = zNew; | |
| 232 | +#else | |
| 233 | + int nOld = pBuffer->nBufAlloc; | |
| 227 | 234 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 235 | + memset(pBuffer->zBuf+nOld, 0, nNew-nOld); | |
| 236 | +#endif | |
| 228 | 237 | pBuffer->nBufAlloc = nNew; |
| 229 | 238 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 230 | 239 | pBuffer->nBuf += nAdd; |
| 231 | 240 | } |
| 232 | 241 | static void thBufferWriteFast( |
| @@ -1535,10 +1544,37 @@ | ||
| 1535 | 1544 | }else{ |
| 1536 | 1545 | return (char *)Th_Malloc(pInterp, 1); |
| 1537 | 1546 | } |
| 1538 | 1547 | } |
| 1539 | 1548 | |
| 1549 | +#if defined(TH_MEMDEBUG) | |
| 1550 | +/* | |
| 1551 | +** Wrappers around the supplied malloc() and free() | |
| 1552 | +*/ | |
| 1553 | +void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){ | |
| 1554 | + void *p; | |
| 1555 | + Th_Vtab *pVtab = pInterp->pVtab; | |
| 1556 | + if( pVtab ){ | |
| 1557 | + p = pVtab->xMalloc(nByte); | |
| 1558 | + if( p ) memset(p, 0, nByte); | |
| 1559 | + }else{ | |
| 1560 | + p = Th_SysMalloc(pInterp, nByte); | |
| 1561 | + } | |
| 1562 | + return p; | |
| 1563 | +} | |
| 1564 | +void Th_DbgFree(Th_Interp *pInterp, void *z){ | |
| 1565 | + if( z ){ | |
| 1566 | + Th_Vtab *pVtab = pInterp->pVtab; | |
| 1567 | + if( pVtab ){ | |
| 1568 | + pVtab->xFree(z); | |
| 1569 | + }else{ | |
| 1570 | + Th_SysFree(pInterp, z); | |
| 1571 | + } | |
| 1572 | + } | |
| 1573 | +} | |
| 1574 | +#endif | |
| 1575 | + | |
| 1540 | 1576 | /* |
| 1541 | 1577 | ** Install a new th1 command. |
| 1542 | 1578 | ** |
| 1543 | 1579 | ** If a command of the same name already exists, it is deleted automatically. |
| 1544 | 1580 | */ |
| @@ -1821,16 +1857,24 @@ | ||
| 1821 | 1857 | } |
| 1822 | 1858 | |
| 1823 | 1859 | /* |
| 1824 | 1860 | ** Create a new interpreter. |
| 1825 | 1861 | */ |
| 1826 | -Th_Interp * Th_CreateInterp(void){ | |
| 1862 | +Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ | |
| 1863 | + int nByte = sizeof(Th_Interp) + sizeof(Th_Frame); | |
| 1827 | 1864 | Th_Interp *p; |
| 1828 | 1865 | |
| 1829 | 1866 | /* Allocate and initialise the interpreter and the global frame */ |
| 1830 | - p = Th_Malloc(0, sizeof(Th_Interp) + sizeof(Th_Frame)); | |
| 1831 | - memset(p, 0, sizeof(Th_Interp)); | |
| 1867 | +#if defined(TH_MEMDEBUG) | |
| 1868 | + if( pVtab ){ | |
| 1869 | + p = pVtab->xMalloc(nByte); | |
| 1870 | + memset(p, 0, nByte); | |
| 1871 | + p->pVtab = pVtab; | |
| 1872 | + }else | |
| 1873 | +#endif | |
| 1874 | + p = Th_SysMalloc(0, nByte); | |
| 1875 | + | |
| 1832 | 1876 | p->paCmd = Th_HashNew(p); |
| 1833 | 1877 | thPushFrame(p, (Th_Frame *)&p[1]); |
| 1834 | 1878 | thInitialize(p); |
| 1835 | 1879 | |
| 1836 | 1880 | return p; |
| 1837 | 1881 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -222,11 +222,20 @@ | |
| 222 | Buffer *pBuffer, |
| 223 | const char *zAdd, |
| 224 | int nAdd |
| 225 | ){ |
| 226 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 227 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 228 | pBuffer->nBufAlloc = nNew; |
| 229 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 230 | pBuffer->nBuf += nAdd; |
| 231 | } |
| 232 | static void thBufferWriteFast( |
| @@ -1535,10 +1544,37 @@ | |
| 1535 | }else{ |
| 1536 | return (char *)Th_Malloc(pInterp, 1); |
| 1537 | } |
| 1538 | } |
| 1539 | |
| 1540 | /* |
| 1541 | ** Install a new th1 command. |
| 1542 | ** |
| 1543 | ** If a command of the same name already exists, it is deleted automatically. |
| 1544 | */ |
| @@ -1821,16 +1857,24 @@ | |
| 1821 | } |
| 1822 | |
| 1823 | /* |
| 1824 | ** Create a new interpreter. |
| 1825 | */ |
| 1826 | Th_Interp * Th_CreateInterp(void){ |
| 1827 | Th_Interp *p; |
| 1828 | |
| 1829 | /* Allocate and initialise the interpreter and the global frame */ |
| 1830 | p = Th_Malloc(0, sizeof(Th_Interp) + sizeof(Th_Frame)); |
| 1831 | memset(p, 0, sizeof(Th_Interp)); |
| 1832 | p->paCmd = Th_HashNew(p); |
| 1833 | thPushFrame(p, (Th_Frame *)&p[1]); |
| 1834 | thInitialize(p); |
| 1835 | |
| 1836 | return p; |
| 1837 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -222,11 +222,20 @@ | |
| 222 | Buffer *pBuffer, |
| 223 | const char *zAdd, |
| 224 | int nAdd |
| 225 | ){ |
| 226 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 227 | #if defined(TH_MEMDEBUG) |
| 228 | char *zNew = (char *)Th_Malloc(interp, nNew); |
| 229 | th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); |
| 230 | Th_Free(interp, pBuffer->zBuf); |
| 231 | pBuffer->zBuf = zNew; |
| 232 | #else |
| 233 | int nOld = pBuffer->nBufAlloc; |
| 234 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 235 | memset(pBuffer->zBuf+nOld, 0, nNew-nOld); |
| 236 | #endif |
| 237 | pBuffer->nBufAlloc = nNew; |
| 238 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 239 | pBuffer->nBuf += nAdd; |
| 240 | } |
| 241 | static void thBufferWriteFast( |
| @@ -1535,10 +1544,37 @@ | |
| 1544 | }else{ |
| 1545 | return (char *)Th_Malloc(pInterp, 1); |
| 1546 | } |
| 1547 | } |
| 1548 | |
| 1549 | #if defined(TH_MEMDEBUG) |
| 1550 | /* |
| 1551 | ** Wrappers around the supplied malloc() and free() |
| 1552 | */ |
| 1553 | void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){ |
| 1554 | void *p; |
| 1555 | Th_Vtab *pVtab = pInterp->pVtab; |
| 1556 | if( pVtab ){ |
| 1557 | p = pVtab->xMalloc(nByte); |
| 1558 | if( p ) memset(p, 0, nByte); |
| 1559 | }else{ |
| 1560 | p = Th_SysMalloc(pInterp, nByte); |
| 1561 | } |
| 1562 | return p; |
| 1563 | } |
| 1564 | void Th_DbgFree(Th_Interp *pInterp, void *z){ |
| 1565 | if( z ){ |
| 1566 | Th_Vtab *pVtab = pInterp->pVtab; |
| 1567 | if( pVtab ){ |
| 1568 | pVtab->xFree(z); |
| 1569 | }else{ |
| 1570 | Th_SysFree(pInterp, z); |
| 1571 | } |
| 1572 | } |
| 1573 | } |
| 1574 | #endif |
| 1575 | |
| 1576 | /* |
| 1577 | ** Install a new th1 command. |
| 1578 | ** |
| 1579 | ** If a command of the same name already exists, it is deleted automatically. |
| 1580 | */ |
| @@ -1821,16 +1857,24 @@ | |
| 1857 | } |
| 1858 | |
| 1859 | /* |
| 1860 | ** Create a new interpreter. |
| 1861 | */ |
| 1862 | Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ |
| 1863 | int nByte = sizeof(Th_Interp) + sizeof(Th_Frame); |
| 1864 | Th_Interp *p; |
| 1865 | |
| 1866 | /* Allocate and initialise the interpreter and the global frame */ |
| 1867 | #if defined(TH_MEMDEBUG) |
| 1868 | if( pVtab ){ |
| 1869 | p = pVtab->xMalloc(nByte); |
| 1870 | memset(p, 0, nByte); |
| 1871 | p->pVtab = pVtab; |
| 1872 | }else |
| 1873 | #endif |
| 1874 | p = Th_SysMalloc(0, nByte); |
| 1875 | |
| 1876 | p->paCmd = Th_HashNew(p); |
| 1877 | thPushFrame(p, (Th_Frame *)&p[1]); |
| 1878 | thInitialize(p); |
| 1879 | |
| 1880 | return p; |
| 1881 |
M
src/th.h
+21
-6
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -21,11 +21,11 @@ | ||
| 21 | 21 | typedef struct Th_Interp Th_Interp; |
| 22 | 22 | |
| 23 | 23 | /* |
| 24 | 24 | ** Create and delete interpreters. |
| 25 | 25 | */ |
| 26 | -Th_Interp * Th_CreateInterp(void); | |
| 26 | +Th_Interp * Th_CreateInterp(Th_Vtab *); | |
| 27 | 27 | void Th_DeleteInterp(Th_Interp *); |
| 28 | 28 | |
| 29 | 29 | /* |
| 30 | 30 | ** Evaluate an TH program in the stack frame identified by parameter |
| 31 | 31 | ** iFrame, according to the following rules: |
| @@ -121,16 +121,31 @@ | ||
| 121 | 121 | |
| 122 | 122 | /* |
| 123 | 123 | ** Access the memory management functions associated with the specified |
| 124 | 124 | ** interpreter. |
| 125 | 125 | */ |
| 126 | +#if defined(TH_MEMDEBUG) | |
| 127 | +void *Th_DbgMalloc(Th_Interp *, int); | |
| 128 | +void Th_DbgFree(Th_Interp *, void *); | |
| 129 | +#endif | |
| 130 | + | |
| 126 | 131 | void *fossil_malloc_zero(size_t); |
| 127 | -void *fossil_realloc(void*,size_t); | |
| 128 | -void fossil_free(void*); | |
| 129 | -#define Th_Malloc(I,N) fossil_malloc_zero(N) | |
| 130 | -#define Th_Realloc(I,P,N) fossil_realloc(P,N) | |
| 131 | -#define Th_Free(I,P) fossil_free(P) | |
| 132 | +void *fossil_realloc(void *, size_t); | |
| 133 | +void fossil_free(void *); | |
| 134 | + | |
| 135 | +#define Th_SysMalloc(I,N) fossil_malloc_zero((N)) | |
| 136 | +#define Th_SysRealloc(I,P,N) fossil_realloc((P),(N)) | |
| 137 | +#define Th_SysFree(I,P) fossil_free((P)) | |
| 138 | + | |
| 139 | +#if defined(TH_MEMDEBUG) | |
| 140 | +# define Th_Malloc(I,N) Th_DbgMalloc((I),(N)) | |
| 141 | +# define Th_Free(I,P) Th_DbgFree((I),(P)) | |
| 142 | +#else | |
| 143 | +# define Th_Malloc(I,N) Th_SysMalloc((I),(N)) | |
| 144 | +# define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N)) | |
| 145 | +# define Th_Free(I,P) Th_SysFree((I),(P)) | |
| 146 | +#endif | |
| 132 | 147 | |
| 133 | 148 | /* |
| 134 | 149 | ** Functions for handling TH lists. |
| 135 | 150 | */ |
| 136 | 151 | int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); |
| 137 | 152 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -21,11 +21,11 @@ | |
| 21 | typedef struct Th_Interp Th_Interp; |
| 22 | |
| 23 | /* |
| 24 | ** Create and delete interpreters. |
| 25 | */ |
| 26 | Th_Interp * Th_CreateInterp(void); |
| 27 | void Th_DeleteInterp(Th_Interp *); |
| 28 | |
| 29 | /* |
| 30 | ** Evaluate an TH program in the stack frame identified by parameter |
| 31 | ** iFrame, according to the following rules: |
| @@ -121,16 +121,31 @@ | |
| 121 | |
| 122 | /* |
| 123 | ** Access the memory management functions associated with the specified |
| 124 | ** interpreter. |
| 125 | */ |
| 126 | void *fossil_malloc_zero(size_t); |
| 127 | void *fossil_realloc(void*,size_t); |
| 128 | void fossil_free(void*); |
| 129 | #define Th_Malloc(I,N) fossil_malloc_zero(N) |
| 130 | #define Th_Realloc(I,P,N) fossil_realloc(P,N) |
| 131 | #define Th_Free(I,P) fossil_free(P) |
| 132 | |
| 133 | /* |
| 134 | ** Functions for handling TH lists. |
| 135 | */ |
| 136 | int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); |
| 137 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -21,11 +21,11 @@ | |
| 21 | typedef struct Th_Interp Th_Interp; |
| 22 | |
| 23 | /* |
| 24 | ** Create and delete interpreters. |
| 25 | */ |
| 26 | Th_Interp * Th_CreateInterp(Th_Vtab *); |
| 27 | void Th_DeleteInterp(Th_Interp *); |
| 28 | |
| 29 | /* |
| 30 | ** Evaluate an TH program in the stack frame identified by parameter |
| 31 | ** iFrame, according to the following rules: |
| @@ -121,16 +121,31 @@ | |
| 121 | |
| 122 | /* |
| 123 | ** Access the memory management functions associated with the specified |
| 124 | ** interpreter. |
| 125 | */ |
| 126 | #if defined(TH_MEMDEBUG) |
| 127 | void *Th_DbgMalloc(Th_Interp *, int); |
| 128 | void Th_DbgFree(Th_Interp *, void *); |
| 129 | #endif |
| 130 | |
| 131 | void *fossil_malloc_zero(size_t); |
| 132 | void *fossil_realloc(void *, size_t); |
| 133 | void fossil_free(void *); |
| 134 | |
| 135 | #define Th_SysMalloc(I,N) fossil_malloc_zero((N)) |
| 136 | #define Th_SysRealloc(I,P,N) fossil_realloc((P),(N)) |
| 137 | #define Th_SysFree(I,P) fossil_free((P)) |
| 138 | |
| 139 | #if defined(TH_MEMDEBUG) |
| 140 | # define Th_Malloc(I,N) Th_DbgMalloc((I),(N)) |
| 141 | # define Th_Free(I,P) Th_DbgFree((I),(P)) |
| 142 | #else |
| 143 | # define Th_Malloc(I,N) Th_SysMalloc((I),(N)) |
| 144 | # define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N)) |
| 145 | # define Th_Free(I,P) Th_SysFree((I),(P)) |
| 146 | #endif |
| 147 | |
| 148 | /* |
| 149 | ** Functions for handling TH lists. |
| 150 | */ |
| 151 | int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); |
| 152 |
+50
-4
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -68,10 +68,46 @@ | ||
| 68 | 68 | ** configuration ("user") database are currently open. |
| 69 | 69 | */ |
| 70 | 70 | #define Th_IsRepositoryOpen() (g.repositoryOpen) |
| 71 | 71 | #define Th_IsConfigOpen() (g.zConfigDbName!=0) |
| 72 | 72 | |
| 73 | +/* | |
| 74 | +** When memory debugging is enabled, use our custom memory allocator. | |
| 75 | +*/ | |
| 76 | +#if defined(TH_MEMDEBUG) | |
| 77 | +/* | |
| 78 | +** Global variable counting the number of outstanding calls to malloc() | |
| 79 | +** made by the th1 implementation. This is used to catch memory leaks | |
| 80 | +** in the interpreter. Obviously, it also means th1 is not threadsafe. | |
| 81 | +*/ | |
| 82 | +static int nOutstandingMalloc = 0; | |
| 83 | + | |
| 84 | +/* | |
| 85 | +** Implementations of malloc() and free() to pass to the interpreter. | |
| 86 | +*/ | |
| 87 | +static void *xMalloc(unsigned int n){ | |
| 88 | + void *p = fossil_malloc(n); | |
| 89 | + if( p ){ | |
| 90 | + nOutstandingMalloc++; | |
| 91 | + } | |
| 92 | + return p; | |
| 93 | +} | |
| 94 | +static void xFree(void *p){ | |
| 95 | + if( p ){ | |
| 96 | + nOutstandingMalloc--; | |
| 97 | + } | |
| 98 | + free(p); | |
| 99 | +} | |
| 100 | +static Th_Vtab vtab = { xMalloc, xFree }; | |
| 101 | + | |
| 102 | +/* | |
| 103 | +** Returns the number of outstanding TH1 memory allocations. | |
| 104 | +*/ | |
| 105 | +int Th_GetOutstandingMalloc(){ | |
| 106 | + return nOutstandingMalloc; | |
| 107 | +} | |
| 108 | +#endif | |
| 73 | 109 | |
| 74 | 110 | /* |
| 75 | 111 | ** Generate a TH1 trace message if debugging is enabled. |
| 76 | 112 | */ |
| 77 | 113 | void Th_Trace(const char *zFormat, ...){ |
| @@ -350,11 +386,11 @@ | ||
| 350 | 386 | if(0==pOut && pThOut!=0){ |
| 351 | 387 | pOut = pThOut; |
| 352 | 388 | } |
| 353 | 389 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 354 | 390 | encode = 0; |
| 355 | - } | |
| 391 | + } | |
| 356 | 392 | if( enableOutput && n ){ |
| 357 | 393 | if( n<0 ) n = strlen(z); |
| 358 | 394 | if( encode ){ |
| 359 | 395 | z = htmlize(z, n); |
| 360 | 396 | n = strlen(z); |
| @@ -627,10 +663,11 @@ | ||
| 627 | 663 | blob_zero(&title); blob_zero(&body); |
| 628 | 664 | markdown_to_html(&src, &title, &body); |
| 629 | 665 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 630 | 666 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 631 | 667 | Th_SetResult(interp, zValue, nValue); |
| 668 | + Th_Free(interp, zValue); | |
| 632 | 669 | return TH_OK; |
| 633 | 670 | } |
| 634 | 671 | |
| 635 | 672 | /* |
| 636 | 673 | ** TH1 command: decorate STRING |
| @@ -810,15 +847,15 @@ | ||
| 810 | 847 | }else if( azCap[i][0]=='*' ){ |
| 811 | 848 | rc = 1; |
| 812 | 849 | }else{ |
| 813 | 850 | rc = login_has_capability(azCap[i], anCap[i], 0); |
| 814 | 851 | } |
| 815 | - break; | |
| 852 | + break; | |
| 816 | 853 | } |
| 817 | 854 | Th_Free(interp, azCap); |
| 818 | 855 | Th_SetResultInt(interp, rc); |
| 819 | - return TH_OK; | |
| 856 | + return TH_OK; | |
| 820 | 857 | } |
| 821 | 858 | |
| 822 | 859 | |
| 823 | 860 | /* |
| 824 | 861 | ** TH1 command: searchable STRING... |
| @@ -2356,11 +2393,20 @@ | ||
| 2356 | 2393 | } |
| 2357 | 2394 | if( forceReset || forceTcl || g.interp==0 ){ |
| 2358 | 2395 | int created = 0; |
| 2359 | 2396 | int i; |
| 2360 | 2397 | if( g.interp==0 ){ |
| 2361 | - g.interp = Th_CreateInterp(); | |
| 2398 | + Th_Vtab *pVtab = 0; | |
| 2399 | +#if defined(TH_MEMDEBUG) | |
| 2400 | + if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ | |
| 2401 | + pVtab = &vtab; | |
| 2402 | + if( g.thTrace ){ | |
| 2403 | + Th_Trace("th1-init MEMDEBUG ENABLED<br />\n"); | |
| 2404 | + } | |
| 2405 | + } | |
| 2406 | +#endif | |
| 2407 | + g.interp = Th_CreateInterp(pVtab); | |
| 2362 | 2408 | created = 1; |
| 2363 | 2409 | } |
| 2364 | 2410 | if( forceReset || created ){ |
| 2365 | 2411 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 2366 | 2412 | } |
| 2367 | 2413 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -68,10 +68,46 @@ | |
| 68 | ** configuration ("user") database are currently open. |
| 69 | */ |
| 70 | #define Th_IsRepositoryOpen() (g.repositoryOpen) |
| 71 | #define Th_IsConfigOpen() (g.zConfigDbName!=0) |
| 72 | |
| 73 | |
| 74 | /* |
| 75 | ** Generate a TH1 trace message if debugging is enabled. |
| 76 | */ |
| 77 | void Th_Trace(const char *zFormat, ...){ |
| @@ -350,11 +386,11 @@ | |
| 350 | if(0==pOut && pThOut!=0){ |
| 351 | pOut = pThOut; |
| 352 | } |
| 353 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 354 | encode = 0; |
| 355 | } |
| 356 | if( enableOutput && n ){ |
| 357 | if( n<0 ) n = strlen(z); |
| 358 | if( encode ){ |
| 359 | z = htmlize(z, n); |
| 360 | n = strlen(z); |
| @@ -627,10 +663,11 @@ | |
| 627 | blob_zero(&title); blob_zero(&body); |
| 628 | markdown_to_html(&src, &title, &body); |
| 629 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 630 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 631 | Th_SetResult(interp, zValue, nValue); |
| 632 | return TH_OK; |
| 633 | } |
| 634 | |
| 635 | /* |
| 636 | ** TH1 command: decorate STRING |
| @@ -810,15 +847,15 @@ | |
| 810 | }else if( azCap[i][0]=='*' ){ |
| 811 | rc = 1; |
| 812 | }else{ |
| 813 | rc = login_has_capability(azCap[i], anCap[i], 0); |
| 814 | } |
| 815 | break; |
| 816 | } |
| 817 | Th_Free(interp, azCap); |
| 818 | Th_SetResultInt(interp, rc); |
| 819 | return TH_OK; |
| 820 | } |
| 821 | |
| 822 | |
| 823 | /* |
| 824 | ** TH1 command: searchable STRING... |
| @@ -2356,11 +2393,20 @@ | |
| 2356 | } |
| 2357 | if( forceReset || forceTcl || g.interp==0 ){ |
| 2358 | int created = 0; |
| 2359 | int i; |
| 2360 | if( g.interp==0 ){ |
| 2361 | g.interp = Th_CreateInterp(); |
| 2362 | created = 1; |
| 2363 | } |
| 2364 | if( forceReset || created ){ |
| 2365 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 2366 | } |
| 2367 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -68,10 +68,46 @@ | |
| 68 | ** configuration ("user") database are currently open. |
| 69 | */ |
| 70 | #define Th_IsRepositoryOpen() (g.repositoryOpen) |
| 71 | #define Th_IsConfigOpen() (g.zConfigDbName!=0) |
| 72 | |
| 73 | /* |
| 74 | ** When memory debugging is enabled, use our custom memory allocator. |
| 75 | */ |
| 76 | #if defined(TH_MEMDEBUG) |
| 77 | /* |
| 78 | ** Global variable counting the number of outstanding calls to malloc() |
| 79 | ** made by the th1 implementation. This is used to catch memory leaks |
| 80 | ** in the interpreter. Obviously, it also means th1 is not threadsafe. |
| 81 | */ |
| 82 | static int nOutstandingMalloc = 0; |
| 83 | |
| 84 | /* |
| 85 | ** Implementations of malloc() and free() to pass to the interpreter. |
| 86 | */ |
| 87 | static void *xMalloc(unsigned int n){ |
| 88 | void *p = fossil_malloc(n); |
| 89 | if( p ){ |
| 90 | nOutstandingMalloc++; |
| 91 | } |
| 92 | return p; |
| 93 | } |
| 94 | static void xFree(void *p){ |
| 95 | if( p ){ |
| 96 | nOutstandingMalloc--; |
| 97 | } |
| 98 | free(p); |
| 99 | } |
| 100 | static Th_Vtab vtab = { xMalloc, xFree }; |
| 101 | |
| 102 | /* |
| 103 | ** Returns the number of outstanding TH1 memory allocations. |
| 104 | */ |
| 105 | int Th_GetOutstandingMalloc(){ |
| 106 | return nOutstandingMalloc; |
| 107 | } |
| 108 | #endif |
| 109 | |
| 110 | /* |
| 111 | ** Generate a TH1 trace message if debugging is enabled. |
| 112 | */ |
| 113 | void Th_Trace(const char *zFormat, ...){ |
| @@ -350,11 +386,11 @@ | |
| 386 | if(0==pOut && pThOut!=0){ |
| 387 | pOut = pThOut; |
| 388 | } |
| 389 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 390 | encode = 0; |
| 391 | } |
| 392 | if( enableOutput && n ){ |
| 393 | if( n<0 ) n = strlen(z); |
| 394 | if( encode ){ |
| 395 | z = htmlize(z, n); |
| 396 | n = strlen(z); |
| @@ -627,10 +663,11 @@ | |
| 663 | blob_zero(&title); blob_zero(&body); |
| 664 | markdown_to_html(&src, &title, &body); |
| 665 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 666 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 667 | Th_SetResult(interp, zValue, nValue); |
| 668 | Th_Free(interp, zValue); |
| 669 | return TH_OK; |
| 670 | } |
| 671 | |
| 672 | /* |
| 673 | ** TH1 command: decorate STRING |
| @@ -810,15 +847,15 @@ | |
| 847 | }else if( azCap[i][0]=='*' ){ |
| 848 | rc = 1; |
| 849 | }else{ |
| 850 | rc = login_has_capability(azCap[i], anCap[i], 0); |
| 851 | } |
| 852 | break; |
| 853 | } |
| 854 | Th_Free(interp, azCap); |
| 855 | Th_SetResultInt(interp, rc); |
| 856 | return TH_OK; |
| 857 | } |
| 858 | |
| 859 | |
| 860 | /* |
| 861 | ** TH1 command: searchable STRING... |
| @@ -2356,11 +2393,20 @@ | |
| 2393 | } |
| 2394 | if( forceReset || forceTcl || g.interp==0 ){ |
| 2395 | int created = 0; |
| 2396 | int i; |
| 2397 | if( g.interp==0 ){ |
| 2398 | Th_Vtab *pVtab = 0; |
| 2399 | #if defined(TH_MEMDEBUG) |
| 2400 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 2401 | pVtab = &vtab; |
| 2402 | if( g.thTrace ){ |
| 2403 | Th_Trace("th1-init MEMDEBUG ENABLED<br />\n"); |
| 2404 | } |
| 2405 | } |
| 2406 | #endif |
| 2407 | g.interp = Th_CreateInterp(pVtab); |
| 2408 | created = 1; |
| 2409 | } |
| 2410 | if( forceReset || created ){ |
| 2411 | th_register_language(g.interp); /* Basic scripting commands. */ |
| 2412 | } |
| 2413 |
+1
-1
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -38,11 +38,11 @@ | ||
| 38 | 38 | if( z[i]>0 && z[i]<' ' ) z[i] = ' '; |
| 39 | 39 | } |
| 40 | 40 | blob_append(pBlob, z, -1); |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | -#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) | |
| 43 | +#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) | |
| 44 | 44 | #ifdef _WIN32 |
| 45 | 45 | #include <conio.h> |
| 46 | 46 | #endif |
| 47 | 47 | |
| 48 | 48 | /* |
| 49 | 49 | |
| 50 | 50 | DELETED src/webmail.c |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -38,11 +38,11 @@ | |
| 38 | if( z[i]>0 && z[i]<' ' ) z[i] = ' '; |
| 39 | } |
| 40 | blob_append(pBlob, z, -1); |
| 41 | } |
| 42 | |
| 43 | #if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS) |
| 44 | #ifdef _WIN32 |
| 45 | #include <conio.h> |
| 46 | #endif |
| 47 | |
| 48 | /* |
| 49 | |
| 50 | ELETED src/webmail.c |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -38,11 +38,11 @@ | |
| 38 | if( z[i]>0 && z[i]<' ' ) z[i] = ' '; |
| 39 | } |
| 40 | blob_append(pBlob, z, -1); |
| 41 | } |
| 42 | |
| 43 | #if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)) |
| 44 | #ifdef _WIN32 |
| 45 | #include <conio.h> |
| 46 | #endif |
| 47 | |
| 48 | /* |
| 49 | |
| 50 | ELETED src/webmail.c |
D
src/webmail.c
-921
| --- a/src/webmail.c | ||
| +++ b/src/webmail.c | ||
| @@ -1,921 +0,0 @@ | ||
| 1 | -/* | |
| 2 | -** Copyright (c) 2018 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 | -******************************************************************************* | |
| 17 | -** | |
| 18 | -** Implementation of web pages for managing the email storage tables | |
| 19 | -** (if they exist): | |
| 20 | -** | |
| 21 | -** emailbox | |
| 22 | -** emailblob | |
| 23 | -** emailroute | |
| 24 | -*/ | |
| 25 | -#include "config.h" | |
| 26 | -#include "webmail.h" | |
| 27 | -#include <assert.h> | |
| 28 | - | |
| 29 | - | |
| 30 | -#if INTERFACE | |
| 31 | - | |
| 32 | -/* Recognized content encodings */ | |
| 33 | -#define EMAILENC_NONE 0 /* No encoding */ | |
| 34 | -#define EMAILENC_B64 1 /* Base64 encoded */ | |
| 35 | -#define EMAILENC_QUOTED 2 /* Quoted printable */ | |
| 36 | - | |
| 37 | -/* An instance of the following object records the location of important | |
| 38 | -** attributes on a single element in a multipart email message body. | |
| 39 | -*/ | |
| 40 | -struct EmailBody { | |
| 41 | - char zMimetype[32]; /* Mimetype */ | |
| 42 | - u8 encoding; /* Type of encoding */ | |
| 43 | - char *zFilename; /* From content-disposition: */ | |
| 44 | - char *zContent; /* Content. \0 terminator inserted */ | |
| 45 | -}; | |
| 46 | - | |
| 47 | -/* | |
| 48 | -** An instance of the following object describes the struture of | |
| 49 | -** an rfc-2822 email message. | |
| 50 | -*/ | |
| 51 | -struct EmailToc { | |
| 52 | - int nHdr; /* Number of header lines */ | |
| 53 | - int nHdrAlloc; /* Number of header lines allocated */ | |
| 54 | - char **azHdr; /* Pointer to header line. \0 terminator inserted */ | |
| 55 | - int nBody; /* Number of body segments */ | |
| 56 | - int nBodyAlloc; /* Number of body segments allocated */ | |
| 57 | - EmailBody *aBody; /* Location of body information */ | |
| 58 | -}; | |
| 59 | -#endif | |
| 60 | - | |
| 61 | -/* | |
| 62 | -** Free An EmailToc object | |
| 63 | -*/ | |
| 64 | -void emailtoc_free(EmailToc *p){ | |
| 65 | - int i; | |
| 66 | - fossil_free(p->azHdr); | |
| 67 | - for(i=0; i<p->nBody; i++){ | |
| 68 | - fossil_free(p->aBody[i].zFilename); | |
| 69 | - } | |
| 70 | - fossil_free(p->aBody); | |
| 71 | - fossil_free(p); | |
| 72 | -} | |
| 73 | - | |
| 74 | -/* | |
| 75 | -** Allocate a new EmailToc object | |
| 76 | -*/ | |
| 77 | -EmailToc *emailtoc_alloc(void){ | |
| 78 | - EmailToc *p = fossil_malloc( sizeof(*p) ); | |
| 79 | - memset(p, 0, sizeof(*p)); | |
| 80 | - return p; | |
| 81 | -} | |
| 82 | - | |
| 83 | -/* | |
| 84 | -** Add a new body element to an EmailToc. | |
| 85 | -*/ | |
| 86 | -EmailBody *emailtoc_new_body(EmailToc *p){ | |
| 87 | - EmailBody *pNew; | |
| 88 | - p->nBody++; | |
| 89 | - if( p->nBody>p->nBodyAlloc ){ | |
| 90 | - p->nBodyAlloc = (p->nBodyAlloc+1)*2; | |
| 91 | - p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc); | |
| 92 | - } | |
| 93 | - pNew = &p->aBody[p->nBody-1]; | |
| 94 | - memset(pNew, 0, sizeof(*pNew)); | |
| 95 | - return pNew; | |
| 96 | -} | |
| 97 | - | |
| 98 | -/* | |
| 99 | -** Add a new header line to the EmailToc. | |
| 100 | -*/ | |
| 101 | -void emailtoc_new_header_line(EmailToc *p, char *z){ | |
| 102 | - p->nHdr++; | |
| 103 | - if( p->nHdr>p->nHdrAlloc ){ | |
| 104 | - p->nHdrAlloc = (p->nHdrAlloc+1)*2; | |
| 105 | - p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc); | |
| 106 | - } | |
| 107 | - p->azHdr[p->nHdr-1] = z; | |
| 108 | -} | |
| 109 | - | |
| 110 | -/* | |
| 111 | -** Return the length of a line in an email header. Continuation lines | |
| 112 | -** are included. Hence, this routine returns the number of bytes up to | |
| 113 | -** and including the first \n character that is followed by something | |
| 114 | -** other than whitespace. | |
| 115 | -*/ | |
| 116 | -static int email_line_length(const char *z){ | |
| 117 | - int i; | |
| 118 | - for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){} | |
| 119 | - if( z[i]=='\n' ) i++; | |
| 120 | - return i; | |
| 121 | -} | |
| 122 | - | |
| 123 | -/* | |
| 124 | -** Look for a parameter of the form NAME=VALUE in the given email | |
| 125 | -** header line. Return a copy of VALUE in space obtained from | |
| 126 | -** fossil_malloc(). Or return NULL if there is no such parameter. | |
| 127 | -*/ | |
| 128 | -static char *email_hdr_value(const char *z, const char *zName){ | |
| 129 | - int nName = (int)strlen(zName); | |
| 130 | - int i; | |
| 131 | - const char *z2 = strstr(z, zName); | |
| 132 | - if( z2==0 ) return 0; | |
| 133 | - z2 += nName; | |
| 134 | - if( z2[0]!='=' ) return 0; | |
| 135 | - z2++; | |
| 136 | - if( z2[0]=='"' ){ | |
| 137 | - z2++; | |
| 138 | - for(i=0; z2[i] && z2[i]!='"'; i++){} | |
| 139 | - if( z2[i]!='"' ) return 0; | |
| 140 | - }else{ | |
| 141 | - for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){} | |
| 142 | - } | |
| 143 | - return mprintf("%.*s", i, z2); | |
| 144 | -} | |
| 145 | - | |
| 146 | -/* | |
| 147 | -** Return a pointer to the first non-whitespace character in z | |
| 148 | -*/ | |
| 149 | -static const char *firstToken(const char *z){ | |
| 150 | - while( fossil_isspace(*z) ){ | |
| 151 | - z++; | |
| 152 | - } | |
| 153 | - return z; | |
| 154 | -} | |
| 155 | - | |
| 156 | -/* | |
| 157 | -** The n-bytes of content in z is a single multipart mime segment | |
| 158 | -** with its own header and body. Decode this one segment and add it to p; | |
| 159 | -** | |
| 160 | -** Rows of the header of the segment are added to p if bAddHeader is | |
| 161 | -** true. | |
| 162 | -*/ | |
| 163 | -LOCAL void emailtoc_add_multipart_segment( | |
| 164 | - EmailToc *p, /* Append the segments here */ | |
| 165 | - char *z, /* The body component */ | |
| 166 | - int bAddHeader /* True to add header lines to p */ | |
| 167 | -){ | |
| 168 | - int i, j; | |
| 169 | - int n; | |
| 170 | - int multipartBody = 0; | |
| 171 | - EmailBody *pBody = emailtoc_new_body(p); | |
| 172 | - i = 0; | |
| 173 | - while( z[i] ){ | |
| 174 | - n = email_line_length(&z[i]); | |
| 175 | - if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){ | |
| 176 | - /* This is the blank line at the end of the header */ | |
| 177 | - i += n; | |
| 178 | - break; | |
| 179 | - } | |
| 180 | - for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){} | |
| 181 | - z[j] = 0; | |
| 182 | - if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){ | |
| 183 | - const char *z2 = firstToken(z+i+13); | |
| 184 | - if( z2 && strncmp(z2, "multipart/", 10)==0 ){ | |
| 185 | - multipartBody = 1; | |
| 186 | - }else{ | |
| 187 | - int j; | |
| 188 | - for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){} | |
| 189 | - if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype); | |
| 190 | - memcpy(pBody->zMimetype, z2, j); | |
| 191 | - pBody->zMimetype[j] = 0; | |
| 192 | - } | |
| 193 | - } | |
| 194 | - /* 123456789 123456789 123456 */ | |
| 195 | - if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){ | |
| 196 | - const char *z2 = firstToken(z+(i+26)); | |
| 197 | - if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){ | |
| 198 | - pBody->encoding = EMAILENC_B64; | |
| 199 | - /* 123456789 123456 */ | |
| 200 | - }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){ | |
| 201 | - pBody->encoding = EMAILENC_QUOTED; | |
| 202 | - }else{ | |
| 203 | - pBody->encoding = EMAILENC_NONE; | |
| 204 | - } | |
| 205 | - } | |
| 206 | - if( bAddHeader ){ | |
| 207 | - emailtoc_new_header_line(p, z+i); | |
| 208 | - }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){ | |
| 209 | - /* 123456789 123456789 */ | |
| 210 | - fossil_free(pBody->zFilename); | |
| 211 | - pBody->zFilename = email_hdr_value(z+i, "filename"); | |
| 212 | - } | |
| 213 | - i += n; | |
| 214 | - } | |
| 215 | - if( multipartBody ){ | |
| 216 | - p->nBody--; | |
| 217 | - emailtoc_add_multipart(p, z+i); | |
| 218 | - }else{ | |
| 219 | - pBody->zContent = z+i; | |
| 220 | - } | |
| 221 | -} | |
| 222 | - | |
| 223 | -/* | |
| 224 | -** The n-bytes of content in z are a multipart/ body component for | |
| 225 | -** an email message. Decode this into its individual segments. | |
| 226 | -** | |
| 227 | -** The component should start and end with a boundary line. There | |
| 228 | -** may be additional boundary lines in the middle. | |
| 229 | -*/ | |
| 230 | -LOCAL void emailtoc_add_multipart( | |
| 231 | - EmailToc *p, /* Append the segments here */ | |
| 232 | - char *z /* The body component. zero-terminated */ | |
| 233 | -){ | |
| 234 | - int nB; /* Size of the boundary string */ | |
| 235 | - int iStart; /* Start of the coding region past boundary mark */ | |
| 236 | - int i; /* Loop index */ | |
| 237 | - char *zBoundary = 0; /* Boundary marker */ | |
| 238 | - | |
| 239 | - /* Skip forward to the beginning of the boundary mark. The boundary | |
| 240 | - ** mark always begins with "--" */ | |
| 241 | - while( z[0]!='-' || z[1]!='-' ){ | |
| 242 | - while( z[0] && z[0]!='\n' ) z++; | |
| 243 | - if( z[0]==0 ) return; | |
| 244 | - z++; | |
| 245 | - } | |
| 246 | - | |
| 247 | - /* Find the length of the boundary mark. */ | |
| 248 | - zBoundary = z; | |
| 249 | - for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){} | |
| 250 | - if( nB==0 ) return; | |
| 251 | - | |
| 252 | - z += nB; | |
| 253 | - while( fossil_isspace(z[0]) ) z++; | |
| 254 | - zBoundary[nB] = 0; | |
| 255 | - for(i=iStart=0; z[i]; i++){ | |
| 256 | - if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){ | |
| 257 | - z[i+1] = 0; | |
| 258 | - emailtoc_add_multipart_segment(p, z+iStart, 0); | |
| 259 | - iStart = i+nB; | |
| 260 | - if( z[iStart]=='-' && z[iStart+1]=='-' ) return; | |
| 261 | - while( fossil_isspace(z[iStart]) ) iStart++; | |
| 262 | - i = iStart; | |
| 263 | - } | |
| 264 | - } | |
| 265 | -} | |
| 266 | - | |
| 267 | -/* | |
| 268 | -** Compute a table-of-contents (EmailToc) for the email message | |
| 269 | -** provided on the input. | |
| 270 | -** | |
| 271 | -** This routine will cause pEmail to become zero-terminated if it is | |
| 272 | -** not already. It will also insert zero characters into parts of | |
| 273 | -** the message, to delimit the various components. | |
| 274 | -*/ | |
| 275 | -EmailToc *emailtoc_from_email(Blob *pEmail){ | |
| 276 | - char *z; | |
| 277 | - EmailToc *p = emailtoc_alloc(); | |
| 278 | - blob_terminate(pEmail); | |
| 279 | - z = blob_buffer(pEmail); | |
| 280 | - emailtoc_add_multipart_segment(p, z, 1); | |
| 281 | - return p; | |
| 282 | -} | |
| 283 | - | |
| 284 | -/* | |
| 285 | -** Inplace-unfolding of an email header line. | |
| 286 | -** | |
| 287 | -** Actually - this routine works by converting all contiguous sequences | |
| 288 | -** of whitespace into a single space character. | |
| 289 | -*/ | |
| 290 | -static void email_hdr_unfold(char *z){ | |
| 291 | - int i, j; | |
| 292 | - char c; | |
| 293 | - for(i=j=0; (c = z[i])!=0; i++){ | |
| 294 | - if( fossil_isspace(c) ){ | |
| 295 | - c = ' '; | |
| 296 | - if( j && z[j-1]==' ' ) continue; | |
| 297 | - } | |
| 298 | - z[j++] = c; | |
| 299 | - } | |
| 300 | - z[j] = 0; | |
| 301 | -} | |
| 302 | - | |
| 303 | -/* | |
| 304 | -** COMMAND: test-decode-email | |
| 305 | -** | |
| 306 | -** Usage: %fossil test-decode-email FILE | |
| 307 | -** | |
| 308 | -** Read an rfc-2822 formatted email out of FILE, then write a decoding | |
| 309 | -** to stdout. Use for testing and validating the email decoder. | |
| 310 | -*/ | |
| 311 | -void test_email_decode_cmd(void){ | |
| 312 | - Blob email; | |
| 313 | - EmailToc *p; | |
| 314 | - int i; | |
| 315 | - verify_all_options(); | |
| 316 | - if( g.argc!=3 ) usage("FILE"); | |
| 317 | - blob_read_from_file(&email, g.argv[2], ExtFILE); | |
| 318 | - p = emailtoc_from_email(&email); | |
| 319 | - fossil_print("%d header line and %d content segments\n", | |
| 320 | - p->nHdr, p->nBody); | |
| 321 | - for(i=0; i<p->nHdr; i++){ | |
| 322 | - email_hdr_unfold(p->azHdr[i]); | |
| 323 | - fossil_print("%3d: %s\n", i, p->azHdr[i]); | |
| 324 | - } | |
| 325 | - for(i=0; i<p->nBody; i++){ | |
| 326 | - fossil_print("\nBODY %d mime \"%s\" encoding %d", | |
| 327 | - i, p->aBody[i].zMimetype, p->aBody[i].encoding); | |
| 328 | - if( p->aBody[i].zFilename ){ | |
| 329 | - fossil_print(" filename \"%s\"", p->aBody[i].zFilename); | |
| 330 | - } | |
| 331 | - fossil_print("\n"); | |
| 332 | - if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue; | |
| 333 | - switch( p->aBody[i].encoding ){ | |
| 334 | - case EMAILENC_B64: { | |
| 335 | - int n = 0; | |
| 336 | - decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); | |
| 337 | - fossil_print("%s", p->aBody[i].zContent); | |
| 338 | - if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); | |
| 339 | - break; | |
| 340 | - } | |
| 341 | - case EMAILENC_QUOTED: { | |
| 342 | - int n = 0; | |
| 343 | - decodeQuotedPrintable(p->aBody[i].zContent, &n); | |
| 344 | - fossil_print("%s", p->aBody[i].zContent); | |
| 345 | - if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); | |
| 346 | - break; | |
| 347 | - } | |
| 348 | - default: { | |
| 349 | - fossil_print("%s\n", p->aBody[i].zContent); | |
| 350 | - break; | |
| 351 | - } | |
| 352 | - } | |
| 353 | - } | |
| 354 | - emailtoc_free(p); | |
| 355 | - blob_reset(&email); | |
| 356 | -} | |
| 357 | - | |
| 358 | -/* | |
| 359 | -** Add the select/option box to the timeline submenu that shows | |
| 360 | -** the various email message formats. | |
| 361 | -*/ | |
| 362 | -static void webmail_f_submenu(void){ | |
| 363 | - static const char *const az[] = { | |
| 364 | - "0", "Normal", | |
| 365 | - "1", "Decoded", | |
| 366 | - "2", "Raw", | |
| 367 | - }; | |
| 368 | - style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0); | |
| 369 | -} | |
| 370 | - | |
| 371 | -/* | |
| 372 | -** If the first N characters of z[] are the name of a header field | |
| 373 | -** that should be shown in "Normal" mode, then return 1. | |
| 374 | -*/ | |
| 375 | -static int webmail_normal_header(const char *z, int N){ | |
| 376 | - static const char *const az[] = { | |
| 377 | - "To", "Cc", "Bcc", "Date", "From", "Subject", | |
| 378 | - }; | |
| 379 | - int i; | |
| 380 | - for(i=0; i<sizeof(az)/sizeof(az[0]); i++){ | |
| 381 | - if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1; | |
| 382 | - } | |
| 383 | - return 0; | |
| 384 | -} | |
| 385 | - | |
| 386 | -/* | |
| 387 | -** Paint a page showing a single email message | |
| 388 | -*/ | |
| 389 | -static void webmail_show_one_message( | |
| 390 | - HQuery *pUrl, /* Calling context */ | |
| 391 | - int emailid, /* emailbox.ebid to display */ | |
| 392 | - const char *zUser /* User who owns it, or NULL if does not matter */ | |
| 393 | -){ | |
| 394 | - Blob sql; | |
| 395 | - Stmt q; | |
| 396 | - int eState = -1; | |
| 397 | - int eTranscript = 0; | |
| 398 | - char zENum[30]; | |
| 399 | - style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0)); | |
| 400 | - webmail_f_submenu(); | |
| 401 | - blob_init(&sql, 0, 0); | |
| 402 | - db_begin_transaction(); | |
| 403 | - blob_append_sql(&sql, | |
| 404 | - "SELECT decompress(etxt), estate, emailblob.ets" | |
| 405 | - " FROM emailblob, emailbox" | |
| 406 | - " WHERE emailid=emsgid AND ebid=%d", | |
| 407 | - emailid | |
| 408 | - ); | |
| 409 | - if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); | |
| 410 | - db_prepare_blob(&q, &sql); | |
| 411 | - blob_reset(&sql); | |
| 412 | - style_set_current_feature("webmail"); | |
| 413 | - style_header("Message %d",emailid); | |
| 414 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 415 | - Blob msg = db_column_text_as_blob(&q, 0); | |
| 416 | - int eFormat = atoi(PD("f","0")); | |
| 417 | - eState = db_column_int(&q, 1); | |
| 418 | - eTranscript = db_column_int(&q, 2); | |
| 419 | - if( eFormat==2 ){ | |
| 420 | - @ <pre>%h(db_column_text(&q, 0))</pre> | |
| 421 | - }else{ | |
| 422 | - EmailToc *p = emailtoc_from_email(&msg); | |
| 423 | - int i, j; | |
| 424 | - @ <p> | |
| 425 | - for(i=0; i<p->nHdr; i++){ | |
| 426 | - char *z = p->azHdr[i]; | |
| 427 | - email_hdr_unfold(z); | |
| 428 | - for(j=0; z[j] && z[j]!=':'; j++){} | |
| 429 | - if( eFormat==0 && !webmail_normal_header(z, j) ) continue; | |
| 430 | - if( z[j]!=':' ){ | |
| 431 | - @ %h(z)<br> | |
| 432 | - }else{ | |
| 433 | - z[j] = 0; | |
| 434 | - @ <b>%h(z):</b> %h(z+j+1)<br> | |
| 435 | - } | |
| 436 | - } | |
| 437 | - for(i=0; i<p->nBody; i++){ | |
| 438 | - @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ | |
| 439 | - if( p->aBody[i].zFilename ){ | |
| 440 | - @ "%h(p->aBody[i].zFilename)" | |
| 441 | - } | |
| 442 | - @ </b> | |
| 443 | - if( eFormat==0 ){ | |
| 444 | - if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue; | |
| 445 | - if( p->aBody[i].zFilename ) continue; | |
| 446 | - }else{ | |
| 447 | - if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; | |
| 448 | - } | |
| 449 | - switch( p->aBody[i].encoding ){ | |
| 450 | - case EMAILENC_B64: { | |
| 451 | - int n = 0; | |
| 452 | - decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); | |
| 453 | - break; | |
| 454 | - } | |
| 455 | - case EMAILENC_QUOTED: { | |
| 456 | - int n = 0; | |
| 457 | - decodeQuotedPrintable(p->aBody[i].zContent, &n); | |
| 458 | - break; | |
| 459 | - } | |
| 460 | - } | |
| 461 | - @ <pre>%h(p->aBody[i].zContent)</pre> | |
| 462 | - } | |
| 463 | - } | |
| 464 | - } | |
| 465 | - db_finalize(&q); | |
| 466 | - | |
| 467 | - /* Optionally show the SMTP transcript */ | |
| 468 | - if( eTranscript>0 | |
| 469 | - && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript) | |
| 470 | - ){ | |
| 471 | - if( P("ts")==0 ){ | |
| 472 | - sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid); | |
| 473 | - style_submenu_element("SMTP Transcript","%s", | |
| 474 | - url_render(pUrl, "ts", "1", "id", zENum)); | |
| 475 | - }else{ | |
| 476 | - db_prepare(&q, | |
| 477 | - "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript | |
| 478 | - ); | |
| 479 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 480 | - const char *zTranscript = db_column_text(&q, 0); | |
| 481 | - @ <hr> | |
| 482 | - @ <pre>%h(zTranscript)</pre> | |
| 483 | - } | |
| 484 | - db_finalize(&q); | |
| 485 | - } | |
| 486 | - } | |
| 487 | - | |
| 488 | - if( eState==0 ){ | |
| 489 | - /* If is message is currently Unread, change it to Read */ | |
| 490 | - blob_append_sql(&sql, | |
| 491 | - "UPDATE emailbox SET estate=1 " | |
| 492 | - " WHERE estate=0 AND ebid=%d", | |
| 493 | - emailid | |
| 494 | - ); | |
| 495 | - if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); | |
| 496 | - db_multi_exec("%s", blob_sql_text(&sql)); | |
| 497 | - blob_reset(&sql); | |
| 498 | - eState = 1; | |
| 499 | - } | |
| 500 | - | |
| 501 | - url_add_parameter(pUrl, "id", 0); | |
| 502 | - sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid); | |
| 503 | - if( eState==2 ){ | |
| 504 | - style_submenu_element("Undelete","%s", | |
| 505 | - url_render(pUrl,"read","1",zENum,"1")); | |
| 506 | - } | |
| 507 | - if( eState==1 ){ | |
| 508 | - style_submenu_element("Delete", "%s", | |
| 509 | - url_render(pUrl,"trash","1",zENum,"1")); | |
| 510 | - style_submenu_element("Mark As Unread", "%s", | |
| 511 | - url_render(pUrl,"unread","1",zENum,"1")); | |
| 512 | - } | |
| 513 | - if( eState==3 ){ | |
| 514 | - style_submenu_element("Delete", "%s", | |
| 515 | - url_render(pUrl,"trash","1",zENum,"1")); | |
| 516 | - } | |
| 517 | - | |
| 518 | - db_end_transaction(0); | |
| 519 | - style_finish_page(); | |
| 520 | - return; | |
| 521 | -} | |
| 522 | - | |
| 523 | -/* | |
| 524 | -** Scan the query parameters looking for parameters with name of the | |
| 525 | -** form "eN" where N is an integer. For all such integers, change | |
| 526 | -** the state of every emailbox entry with ebid==N to eStateNew provided | |
| 527 | -** that either zUser is NULL or matches. | |
| 528 | -** | |
| 529 | -** Or if eNewState==99, then delete the entries. | |
| 530 | -*/ | |
| 531 | -static void webmail_change_state(int eNewState, const char *zUser){ | |
| 532 | - Blob sql; | |
| 533 | - int sep = '('; | |
| 534 | - int i; | |
| 535 | - const char *zName; | |
| 536 | - int n; | |
| 537 | - if( !cgi_csrf_safe(0) ) return; | |
| 538 | - blob_init(&sql, 0, 0); | |
| 539 | - if( eNewState==99 ){ | |
| 540 | - blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN "); | |
| 541 | - }else{ | |
| 542 | - blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ", | |
| 543 | - eNewState); | |
| 544 | - } | |
| 545 | - for(i=0; (zName = cgi_parameter_name(i))!=0; i++){ | |
| 546 | - if( zName[0]!='e' ) continue; | |
| 547 | - if( !fossil_isdigit(zName[1]) ) continue; | |
| 548 | - n = atoi(zName+1); | |
| 549 | - blob_append_sql(&sql, "%c%d", sep, n); | |
| 550 | - sep = ','; | |
| 551 | - } | |
| 552 | - if( zUser ){ | |
| 553 | - blob_append_sql(&sql, ") AND euser=%Q", zUser); | |
| 554 | - }else{ | |
| 555 | - blob_append_sql(&sql, ")"); | |
| 556 | - } | |
| 557 | - if( sep==',' ){ | |
| 558 | - db_multi_exec("%s", blob_sql_text(&sql)); | |
| 559 | - } | |
| 560 | - blob_reset(&sql); | |
| 561 | -} | |
| 562 | - | |
| 563 | - | |
| 564 | -/* | |
| 565 | -** Add the select/option box to the timeline submenu that shows | |
| 566 | -** which messages to include in the index. | |
| 567 | -*/ | |
| 568 | -static void webmail_d_submenu(void){ | |
| 569 | - static const char *const az[] = { | |
| 570 | - "0", "InBox", | |
| 571 | - "1", "Unread", | |
| 572 | - "2", "Trash", | |
| 573 | - "3", "Sent", | |
| 574 | - "4", "Everything", | |
| 575 | - }; | |
| 576 | - style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0); | |
| 577 | -} | |
| 578 | - | |
| 579 | -/* | |
| 580 | -** WEBPAGE: webmail | |
| 581 | -** | |
| 582 | -** This page can be used to read content from the EMAILBOX table | |
| 583 | -** that contains email received by the "fossil smtpd" command. | |
| 584 | -** | |
| 585 | -** Query parameters: | |
| 586 | -** | |
| 587 | -** id=N Show a single email entry emailbox.ebid==N | |
| 588 | -** f=N Display format. 0: decoded 1: raw | |
| 589 | -** user=USER Show mailbox for USER (admin only). | |
| 590 | -** user=* Show mailbox for all users (admin only). | |
| 591 | -** d=N 0: inbox+unread 1: unread-only 2: trash 3: all | |
| 592 | -** eN Select email entry emailbox.ebid==N | |
| 593 | -** trash Move selected entries to trash (estate=2) | |
| 594 | -** read Mark selected entries as read (estate=1) | |
| 595 | -** unread Mark selected entries as unread (estate=0) | |
| 596 | -** | |
| 597 | -*/ | |
| 598 | -void webmail_page(void){ | |
| 599 | - int emailid; | |
| 600 | - Stmt q; | |
| 601 | - Blob sql; | |
| 602 | - int showAll = 0; | |
| 603 | - const char *zUser = 0; | |
| 604 | - int d = 0; /* Display mode. 0..3. d= query parameter */ | |
| 605 | - int pg = 0; /* Page number */ | |
| 606 | - int N = 50; /* Results per page */ | |
| 607 | - int got; /* Number of results on this page */ | |
| 608 | - char zPPg[30]; /* Previous page */ | |
| 609 | - char zNPg[30]; /* Next page */ | |
| 610 | - HQuery url; | |
| 611 | - login_check_credentials(); | |
| 612 | - if( !login_is_individual() ){ | |
| 613 | - login_needed(0); | |
| 614 | - return; | |
| 615 | - } | |
| 616 | - style_set_current_feature("webmail"); | |
| 617 | - if( !db_table_exists("repository","emailbox") ){ | |
| 618 | - style_header("Webmail Not Available"); | |
| 619 | - @ <p>This repository is not configured to provide webmail</p> | |
| 620 | - style_finish_page(); | |
| 621 | - return; | |
| 622 | - } | |
| 623 | - add_content_sql_commands(g.db); | |
| 624 | - emailid = atoi(PD("id","0")); | |
| 625 | - url_initialize(&url, "webmail"); | |
| 626 | - if( g.perm.Admin ){ | |
| 627 | - zUser = PD("user",g.zLogin); | |
| 628 | - if( zUser ){ | |
| 629 | - url_add_parameter(&url, "user", zUser); | |
| 630 | - if( fossil_strcmp(zUser,"*")==0 ){ | |
| 631 | - showAll = 1; | |
| 632 | - zUser = 0; | |
| 633 | - } | |
| 634 | - } | |
| 635 | - }else{ | |
| 636 | - zUser = g.zLogin; | |
| 637 | - } | |
| 638 | - if( P("d") ) url_add_parameter(&url, "d", P("d")); | |
| 639 | - if( emailid>0 ){ | |
| 640 | - webmail_show_one_message(&url, emailid, zUser); | |
| 641 | - return; | |
| 642 | - } | |
| 643 | - style_header("Webmail"); | |
| 644 | - webmail_d_submenu(); | |
| 645 | - db_begin_transaction(); | |
| 646 | - if( P("trash")!=0 ) webmail_change_state(2,zUser); | |
| 647 | - if( P("unread")!=0 ) webmail_change_state(0,zUser); | |
| 648 | - if( P("read")!=0 ) webmail_change_state(1,zUser); | |
| 649 | - if( P("purge")!=0 ) webmail_change_state(99,zUser); | |
| 650 | - blob_init(&sql, 0, 0); | |
| 651 | - blob_append_sql(&sql, | |
| 652 | - "CREATE TEMP TABLE tmbox AS " | |
| 653 | - "SELECT ebid," /* 0 */ | |
| 654 | - " efrom," /* 1 */ | |
| 655 | - " datetime(edate,'unixepoch')," /* 2 */ | |
| 656 | - " estate," /* 3 */ | |
| 657 | - " esubject," /* 4 */ | |
| 658 | - " euser" /* 5 */ | |
| 659 | - " FROM emailbox" | |
| 660 | - ); | |
| 661 | - d = atoi(PD("d","0")); | |
| 662 | - switch( d ){ | |
| 663 | - case 0: { /* Show unread and read */ | |
| 664 | - blob_append_sql(&sql, " WHERE estate<=1"); | |
| 665 | - break; | |
| 666 | - } | |
| 667 | - case 1: { /* Unread messages only */ | |
| 668 | - blob_append_sql(&sql, " WHERE estate=0"); | |
| 669 | - break; | |
| 670 | - } | |
| 671 | - case 2: { /* Trashcan only */ | |
| 672 | - blob_append_sql(&sql, " WHERE estate=2"); | |
| 673 | - break; | |
| 674 | - } | |
| 675 | - case 3: { /* Outgoing email only */ | |
| 676 | - blob_append_sql(&sql, " WHERE estate=3"); | |
| 677 | - break; | |
| 678 | - } | |
| 679 | - case 4: { /* Everything */ | |
| 680 | - blob_append_sql(&sql, " WHERE 1"); | |
| 681 | - break; | |
| 682 | - } | |
| 683 | - } | |
| 684 | - if( showAll ){ | |
| 685 | - style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); | |
| 686 | - }else if( zUser!=0 ){ | |
| 687 | - style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); | |
| 688 | - if( fossil_strcmp(zUser, g.zLogin)!=0 ){ | |
| 689 | - style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); | |
| 690 | - } | |
| 691 | - if( zUser ){ | |
| 692 | - blob_append_sql(&sql, " AND euser=%Q", zUser); | |
| 693 | - }else{ | |
| 694 | - blob_append_sql(&sql, " AND euser=%Q", g.zLogin); | |
| 695 | - } | |
| 696 | - }else{ | |
| 697 | - if( g.perm.Admin ){ | |
| 698 | - style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); | |
| 699 | - } | |
| 700 | - blob_append_sql(&sql, " AND euser=%Q", g.zLogin); | |
| 701 | - } | |
| 702 | - pg = atoi(PD("pg","0")); | |
| 703 | - blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N); | |
| 704 | - db_multi_exec("%s", blob_sql_text(&sql)); | |
| 705 | - got = db_int(0, "SELECT count(*) FROM tmbox"); | |
| 706 | - db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N); | |
| 707 | - blob_reset(&sql); | |
| 708 | - @ <form action="%R/webmail" method="POST"> | |
| 709 | - @ <input type="hidden" name="d" value="%d(d)"> | |
| 710 | - @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")"> | |
| 711 | - @ <table border="0" width="100%%"> | |
| 712 | - @ <tr><td align="left"> | |
| 713 | - if( d==2 ){ | |
| 714 | - @ <input type="submit" name="read" value="Undelete"> | |
| 715 | - @ <input type="submit" name="purge" value="Delete Permanently"> | |
| 716 | - }else{ | |
| 717 | - @ <input type="submit" name="trash" value="Delete"> | |
| 718 | - if( d!=1 ){ | |
| 719 | - @ <input type="submit" name="unread" value="Mark as unread"> | |
| 720 | - } | |
| 721 | - @ <input type="submit" name="read" value="Mark as read"> | |
| 722 | - } | |
| 723 | - @ <button onclick="webmailSelectAll(); return false;">Select All</button> | |
| 724 | - @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a> | |
| 725 | - @ </td><td align="right"> | |
| 726 | - if( pg>0 ){ | |
| 727 | - sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1); | |
| 728 | - @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">< Newer</a> | |
| 729 | - } | |
| 730 | - if( got>50 ){ | |
| 731 | - sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1); | |
| 732 | - @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older ></a></td> | |
| 733 | - } | |
| 734 | - @ </table> | |
| 735 | - @ <table> | |
| 736 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 737 | - const char *zId = db_column_text(&q,0); | |
| 738 | - const char *zFrom = db_column_text(&q, 1); | |
| 739 | - const char *zDate = db_column_text(&q, 2); | |
| 740 | - const char *zSubject = db_column_text(&q, 4); | |
| 741 | - if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)"; | |
| 742 | - @ <tr> | |
| 743 | - @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td> | |
| 744 | - @ <td>%h(zFrom)</td> | |
| 745 | - @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \ | |
| 746 | - @ %s(zDate)</td> | |
| 747 | - if( showAll ){ | |
| 748 | - const char *zTo = db_column_text(&q,5); | |
| 749 | - @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td> | |
| 750 | - } | |
| 751 | - @ </tr> | |
| 752 | - } | |
| 753 | - db_finalize(&q); | |
| 754 | - @ </table> | |
| 755 | - @ </form> | |
| 756 | - @ <script> | |
| 757 | - @ function webmailSelectAll(){ | |
| 758 | - @ var x = document.getElementsByClassName("webmailckbox"); | |
| 759 | - @ for(i=0; i<x.length; i++){ | |
| 760 | - @ x[i].checked = true; | |
| 761 | - @ } | |
| 762 | - @ } | |
| 763 | - @ </script> | |
| 764 | - style_finish_page(); | |
| 765 | - db_end_transaction(0); | |
| 766 | -} | |
| 767 | - | |
| 768 | -/* | |
| 769 | -** WEBPAGE: emailblob | |
| 770 | -** | |
| 771 | -** This page, accessible only to administrators, allows easy viewing of | |
| 772 | -** the emailblob table - the table that contains the text of email messages | |
| 773 | -** both inbound and outbound, and transcripts of SMTP sessions. | |
| 774 | -** | |
| 775 | -** id=N Show the text of emailblob with emailid==N | |
| 776 | -** | |
| 777 | -*/ | |
| 778 | -void webmail_emailblob_page(void){ | |
| 779 | - int id = atoi(PD("id","0")); | |
| 780 | - Stmt q; | |
| 781 | - login_check_credentials(); | |
| 782 | - if( !g.perm.Setup ){ | |
| 783 | - login_needed(0); | |
| 784 | - return; | |
| 785 | - } | |
| 786 | - add_content_sql_commands(g.db); | |
| 787 | - style_set_current_feature("webmail"); | |
| 788 | - style_header("emailblob table"); | |
| 789 | - if( id>0 ){ | |
| 790 | - style_submenu_element("Index", "%R/emailblob"); | |
| 791 | - @ <ul> | |
| 792 | - db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id); | |
| 793 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 794 | - int id = db_column_int(&q, 0); | |
| 795 | - @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a> | |
| 796 | - } | |
| 797 | - db_finalize(&q); | |
| 798 | - db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id); | |
| 799 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 800 | - const char *zUser = db_column_text(&q, 0); | |
| 801 | - int e = db_column_int(&q, 1); | |
| 802 | - @ <li> emailbox for %h(zUser) state %d(e) | |
| 803 | - } | |
| 804 | - db_finalize(&q); | |
| 805 | - db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id); | |
| 806 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 807 | - const char *zFrom = db_column_text(&q, 0); | |
| 808 | - const char *zTo = db_column_text(&q, 1); | |
| 809 | - @ <li> emailoutq message body from %h(zFrom) to %h(zTo) | |
| 810 | - } | |
| 811 | - db_finalize(&q); | |
| 812 | - db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id); | |
| 813 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 814 | - const char *zFrom = db_column_text(&q, 0); | |
| 815 | - const char *zTo = db_column_text(&q, 1); | |
| 816 | - @ <li> emailoutq transcript from %h(zFrom) to %h(zTo) | |
| 817 | - } | |
| 818 | - db_finalize(&q); | |
| 819 | - @ </ul> | |
| 820 | - @ <hr> | |
| 821 | - db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", | |
| 822 | - id); | |
| 823 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 824 | - const char *zContent = db_column_text(&q, 0); | |
| 825 | - @ <pre>%h(zContent)</pre> | |
| 826 | - } | |
| 827 | - db_finalize(&q); | |
| 828 | - }else{ | |
| 829 | - style_submenu_element("emailoutq table","%R/emailoutq"); | |
| 830 | - db_prepare(&q, | |
| 831 | - "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz," | |
| 832 | - " length(etxt)" | |
| 833 | - " FROM emailblob ORDER BY etime DESC, emailid DESC"); | |
| 834 | - @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ | |
| 835 | - @ data-column-types='nnntkk'> | |
| 836 | - @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \ | |
| 837 | - @ <th> uncompressed <th> compressed </tr></thead><tbody> | |
| 838 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 839 | - int id = db_column_int(&q, 0); | |
| 840 | - int nref = db_column_int(&q, 1); | |
| 841 | - int ets = db_column_int(&q, 2); | |
| 842 | - const char *zDate = db_column_text(&q, 3); | |
| 843 | - int sz = db_column_int(&q,4); | |
| 844 | - int csz = db_column_int(&q,5); | |
| 845 | - @ <tr> | |
| 846 | - @ <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a> | |
| 847 | - @ <td align="right">%d(nref)</td> | |
| 848 | - if( ets>0 ){ | |
| 849 | - @ <td align="right">%d(ets)</td> | |
| 850 | - }else{ | |
| 851 | - @ <td> </td> | |
| 852 | - } | |
| 853 | - @ <td>%h(zDate)</td> | |
| 854 | - @ <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td> | |
| 855 | - @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td> | |
| 856 | - @ </tr> | |
| 857 | - } | |
| 858 | - @ </tbody></table> | |
| 859 | - db_finalize(&q); | |
| 860 | - style_table_sorter(); | |
| 861 | - } | |
| 862 | - style_finish_page(); | |
| 863 | -} | |
| 864 | - | |
| 865 | -/* | |
| 866 | -** WEBPAGE: emailoutq | |
| 867 | -** | |
| 868 | -** This page, accessible only to administrators, allows easy viewing of | |
| 869 | -** the emailoutq table - the table that contains the email messages | |
| 870 | -** that are queued for transmission via SMTP. | |
| 871 | -*/ | |
| 872 | -void webmail_emailoutq_page(void){ | |
| 873 | - Stmt q; | |
| 874 | - login_check_credentials(); | |
| 875 | - if( !g.perm.Setup ){ | |
| 876 | - login_needed(0); | |
| 877 | - return; | |
| 878 | - } | |
| 879 | - add_content_sql_commands(g.db); | |
| 880 | - style_set_current_feature("webmail"); | |
| 881 | - style_header("emailoutq table"); | |
| 882 | - style_submenu_element("emailblob table","%R/emailblob"); | |
| 883 | - db_prepare(&q, | |
| 884 | - "SELECT edomain, efrom, eto, emsgid, " | |
| 885 | - " datetime(ectime,'unixepoch')," | |
| 886 | - " datetime(nullif(emtime,0),'unixepoch')," | |
| 887 | - " ensend, ets" | |
| 888 | - " FROM emailoutq" | |
| 889 | - ); | |
| 890 | - @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ | |
| 891 | - @ data-column-types='tttnttnn'> | |
| 892 | - @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \ | |
| 893 | - @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody> | |
| 894 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 895 | - const char *zDomain = db_column_text(&q, 0); | |
| 896 | - const char *zFrom = db_column_text(&q, 1); | |
| 897 | - const char *zTo = db_column_text(&q, 2); | |
| 898 | - int emsgid = db_column_int(&q, 3); | |
| 899 | - const char *zCTime = db_column_text(&q, 4); | |
| 900 | - const char *zMTime = db_column_text(&q, 5); | |
| 901 | - int ensend = db_column_int(&q, 6); | |
| 902 | - int ets = db_column_int(&q, 7); | |
| 903 | - @ <tr> | |
| 904 | - @ <td>%h(zDomain) | |
| 905 | - @ <td>%h(zFrom) | |
| 906 | - @ <td>%h(zTo) | |
| 907 | - @ <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a> | |
| 908 | - @ <td>%h(zCTime) | |
| 909 | - @ <td>%h(zMTime) | |
| 910 | - @ <td align="right">%d(ensend) | |
| 911 | - if( ets>0 ){ | |
| 912 | - @ <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td> | |
| 913 | - }else{ | |
| 914 | - @ <td> </td> | |
| 915 | - } | |
| 916 | - } | |
| 917 | - @ </tbody></table> | |
| 918 | - db_finalize(&q); | |
| 919 | - style_table_sorter(); | |
| 920 | - style_finish_page(); | |
| 921 | -} |
| --- a/src/webmail.c | |
| +++ b/src/webmail.c | |
| @@ -1,921 +0,0 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2018 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 | ******************************************************************************* |
| 17 | ** |
| 18 | ** Implementation of web pages for managing the email storage tables |
| 19 | ** (if they exist): |
| 20 | ** |
| 21 | ** emailbox |
| 22 | ** emailblob |
| 23 | ** emailroute |
| 24 | */ |
| 25 | #include "config.h" |
| 26 | #include "webmail.h" |
| 27 | #include <assert.h> |
| 28 | |
| 29 | |
| 30 | #if INTERFACE |
| 31 | |
| 32 | /* Recognized content encodings */ |
| 33 | #define EMAILENC_NONE 0 /* No encoding */ |
| 34 | #define EMAILENC_B64 1 /* Base64 encoded */ |
| 35 | #define EMAILENC_QUOTED 2 /* Quoted printable */ |
| 36 | |
| 37 | /* An instance of the following object records the location of important |
| 38 | ** attributes on a single element in a multipart email message body. |
| 39 | */ |
| 40 | struct EmailBody { |
| 41 | char zMimetype[32]; /* Mimetype */ |
| 42 | u8 encoding; /* Type of encoding */ |
| 43 | char *zFilename; /* From content-disposition: */ |
| 44 | char *zContent; /* Content. \0 terminator inserted */ |
| 45 | }; |
| 46 | |
| 47 | /* |
| 48 | ** An instance of the following object describes the struture of |
| 49 | ** an rfc-2822 email message. |
| 50 | */ |
| 51 | struct EmailToc { |
| 52 | int nHdr; /* Number of header lines */ |
| 53 | int nHdrAlloc; /* Number of header lines allocated */ |
| 54 | char **azHdr; /* Pointer to header line. \0 terminator inserted */ |
| 55 | int nBody; /* Number of body segments */ |
| 56 | int nBodyAlloc; /* Number of body segments allocated */ |
| 57 | EmailBody *aBody; /* Location of body information */ |
| 58 | }; |
| 59 | #endif |
| 60 | |
| 61 | /* |
| 62 | ** Free An EmailToc object |
| 63 | */ |
| 64 | void emailtoc_free(EmailToc *p){ |
| 65 | int i; |
| 66 | fossil_free(p->azHdr); |
| 67 | for(i=0; i<p->nBody; i++){ |
| 68 | fossil_free(p->aBody[i].zFilename); |
| 69 | } |
| 70 | fossil_free(p->aBody); |
| 71 | fossil_free(p); |
| 72 | } |
| 73 | |
| 74 | /* |
| 75 | ** Allocate a new EmailToc object |
| 76 | */ |
| 77 | EmailToc *emailtoc_alloc(void){ |
| 78 | EmailToc *p = fossil_malloc( sizeof(*p) ); |
| 79 | memset(p, 0, sizeof(*p)); |
| 80 | return p; |
| 81 | } |
| 82 | |
| 83 | /* |
| 84 | ** Add a new body element to an EmailToc. |
| 85 | */ |
| 86 | EmailBody *emailtoc_new_body(EmailToc *p){ |
| 87 | EmailBody *pNew; |
| 88 | p->nBody++; |
| 89 | if( p->nBody>p->nBodyAlloc ){ |
| 90 | p->nBodyAlloc = (p->nBodyAlloc+1)*2; |
| 91 | p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc); |
| 92 | } |
| 93 | pNew = &p->aBody[p->nBody-1]; |
| 94 | memset(pNew, 0, sizeof(*pNew)); |
| 95 | return pNew; |
| 96 | } |
| 97 | |
| 98 | /* |
| 99 | ** Add a new header line to the EmailToc. |
| 100 | */ |
| 101 | void emailtoc_new_header_line(EmailToc *p, char *z){ |
| 102 | p->nHdr++; |
| 103 | if( p->nHdr>p->nHdrAlloc ){ |
| 104 | p->nHdrAlloc = (p->nHdrAlloc+1)*2; |
| 105 | p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc); |
| 106 | } |
| 107 | p->azHdr[p->nHdr-1] = z; |
| 108 | } |
| 109 | |
| 110 | /* |
| 111 | ** Return the length of a line in an email header. Continuation lines |
| 112 | ** are included. Hence, this routine returns the number of bytes up to |
| 113 | ** and including the first \n character that is followed by something |
| 114 | ** other than whitespace. |
| 115 | */ |
| 116 | static int email_line_length(const char *z){ |
| 117 | int i; |
| 118 | for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){} |
| 119 | if( z[i]=='\n' ) i++; |
| 120 | return i; |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | ** Look for a parameter of the form NAME=VALUE in the given email |
| 125 | ** header line. Return a copy of VALUE in space obtained from |
| 126 | ** fossil_malloc(). Or return NULL if there is no such parameter. |
| 127 | */ |
| 128 | static char *email_hdr_value(const char *z, const char *zName){ |
| 129 | int nName = (int)strlen(zName); |
| 130 | int i; |
| 131 | const char *z2 = strstr(z, zName); |
| 132 | if( z2==0 ) return 0; |
| 133 | z2 += nName; |
| 134 | if( z2[0]!='=' ) return 0; |
| 135 | z2++; |
| 136 | if( z2[0]=='"' ){ |
| 137 | z2++; |
| 138 | for(i=0; z2[i] && z2[i]!='"'; i++){} |
| 139 | if( z2[i]!='"' ) return 0; |
| 140 | }else{ |
| 141 | for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){} |
| 142 | } |
| 143 | return mprintf("%.*s", i, z2); |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** Return a pointer to the first non-whitespace character in z |
| 148 | */ |
| 149 | static const char *firstToken(const char *z){ |
| 150 | while( fossil_isspace(*z) ){ |
| 151 | z++; |
| 152 | } |
| 153 | return z; |
| 154 | } |
| 155 | |
| 156 | /* |
| 157 | ** The n-bytes of content in z is a single multipart mime segment |
| 158 | ** with its own header and body. Decode this one segment and add it to p; |
| 159 | ** |
| 160 | ** Rows of the header of the segment are added to p if bAddHeader is |
| 161 | ** true. |
| 162 | */ |
| 163 | LOCAL void emailtoc_add_multipart_segment( |
| 164 | EmailToc *p, /* Append the segments here */ |
| 165 | char *z, /* The body component */ |
| 166 | int bAddHeader /* True to add header lines to p */ |
| 167 | ){ |
| 168 | int i, j; |
| 169 | int n; |
| 170 | int multipartBody = 0; |
| 171 | EmailBody *pBody = emailtoc_new_body(p); |
| 172 | i = 0; |
| 173 | while( z[i] ){ |
| 174 | n = email_line_length(&z[i]); |
| 175 | if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){ |
| 176 | /* This is the blank line at the end of the header */ |
| 177 | i += n; |
| 178 | break; |
| 179 | } |
| 180 | for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){} |
| 181 | z[j] = 0; |
| 182 | if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){ |
| 183 | const char *z2 = firstToken(z+i+13); |
| 184 | if( z2 && strncmp(z2, "multipart/", 10)==0 ){ |
| 185 | multipartBody = 1; |
| 186 | }else{ |
| 187 | int j; |
| 188 | for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){} |
| 189 | if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype); |
| 190 | memcpy(pBody->zMimetype, z2, j); |
| 191 | pBody->zMimetype[j] = 0; |
| 192 | } |
| 193 | } |
| 194 | /* 123456789 123456789 123456 */ |
| 195 | if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){ |
| 196 | const char *z2 = firstToken(z+(i+26)); |
| 197 | if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){ |
| 198 | pBody->encoding = EMAILENC_B64; |
| 199 | /* 123456789 123456 */ |
| 200 | }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){ |
| 201 | pBody->encoding = EMAILENC_QUOTED; |
| 202 | }else{ |
| 203 | pBody->encoding = EMAILENC_NONE; |
| 204 | } |
| 205 | } |
| 206 | if( bAddHeader ){ |
| 207 | emailtoc_new_header_line(p, z+i); |
| 208 | }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){ |
| 209 | /* 123456789 123456789 */ |
| 210 | fossil_free(pBody->zFilename); |
| 211 | pBody->zFilename = email_hdr_value(z+i, "filename"); |
| 212 | } |
| 213 | i += n; |
| 214 | } |
| 215 | if( multipartBody ){ |
| 216 | p->nBody--; |
| 217 | emailtoc_add_multipart(p, z+i); |
| 218 | }else{ |
| 219 | pBody->zContent = z+i; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | /* |
| 224 | ** The n-bytes of content in z are a multipart/ body component for |
| 225 | ** an email message. Decode this into its individual segments. |
| 226 | ** |
| 227 | ** The component should start and end with a boundary line. There |
| 228 | ** may be additional boundary lines in the middle. |
| 229 | */ |
| 230 | LOCAL void emailtoc_add_multipart( |
| 231 | EmailToc *p, /* Append the segments here */ |
| 232 | char *z /* The body component. zero-terminated */ |
| 233 | ){ |
| 234 | int nB; /* Size of the boundary string */ |
| 235 | int iStart; /* Start of the coding region past boundary mark */ |
| 236 | int i; /* Loop index */ |
| 237 | char *zBoundary = 0; /* Boundary marker */ |
| 238 | |
| 239 | /* Skip forward to the beginning of the boundary mark. The boundary |
| 240 | ** mark always begins with "--" */ |
| 241 | while( z[0]!='-' || z[1]!='-' ){ |
| 242 | while( z[0] && z[0]!='\n' ) z++; |
| 243 | if( z[0]==0 ) return; |
| 244 | z++; |
| 245 | } |
| 246 | |
| 247 | /* Find the length of the boundary mark. */ |
| 248 | zBoundary = z; |
| 249 | for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){} |
| 250 | if( nB==0 ) return; |
| 251 | |
| 252 | z += nB; |
| 253 | while( fossil_isspace(z[0]) ) z++; |
| 254 | zBoundary[nB] = 0; |
| 255 | for(i=iStart=0; z[i]; i++){ |
| 256 | if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){ |
| 257 | z[i+1] = 0; |
| 258 | emailtoc_add_multipart_segment(p, z+iStart, 0); |
| 259 | iStart = i+nB; |
| 260 | if( z[iStart]=='-' && z[iStart+1]=='-' ) return; |
| 261 | while( fossil_isspace(z[iStart]) ) iStart++; |
| 262 | i = iStart; |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | /* |
| 268 | ** Compute a table-of-contents (EmailToc) for the email message |
| 269 | ** provided on the input. |
| 270 | ** |
| 271 | ** This routine will cause pEmail to become zero-terminated if it is |
| 272 | ** not already. It will also insert zero characters into parts of |
| 273 | ** the message, to delimit the various components. |
| 274 | */ |
| 275 | EmailToc *emailtoc_from_email(Blob *pEmail){ |
| 276 | char *z; |
| 277 | EmailToc *p = emailtoc_alloc(); |
| 278 | blob_terminate(pEmail); |
| 279 | z = blob_buffer(pEmail); |
| 280 | emailtoc_add_multipart_segment(p, z, 1); |
| 281 | return p; |
| 282 | } |
| 283 | |
| 284 | /* |
| 285 | ** Inplace-unfolding of an email header line. |
| 286 | ** |
| 287 | ** Actually - this routine works by converting all contiguous sequences |
| 288 | ** of whitespace into a single space character. |
| 289 | */ |
| 290 | static void email_hdr_unfold(char *z){ |
| 291 | int i, j; |
| 292 | char c; |
| 293 | for(i=j=0; (c = z[i])!=0; i++){ |
| 294 | if( fossil_isspace(c) ){ |
| 295 | c = ' '; |
| 296 | if( j && z[j-1]==' ' ) continue; |
| 297 | } |
| 298 | z[j++] = c; |
| 299 | } |
| 300 | z[j] = 0; |
| 301 | } |
| 302 | |
| 303 | /* |
| 304 | ** COMMAND: test-decode-email |
| 305 | ** |
| 306 | ** Usage: %fossil test-decode-email FILE |
| 307 | ** |
| 308 | ** Read an rfc-2822 formatted email out of FILE, then write a decoding |
| 309 | ** to stdout. Use for testing and validating the email decoder. |
| 310 | */ |
| 311 | void test_email_decode_cmd(void){ |
| 312 | Blob email; |
| 313 | EmailToc *p; |
| 314 | int i; |
| 315 | verify_all_options(); |
| 316 | if( g.argc!=3 ) usage("FILE"); |
| 317 | blob_read_from_file(&email, g.argv[2], ExtFILE); |
| 318 | p = emailtoc_from_email(&email); |
| 319 | fossil_print("%d header line and %d content segments\n", |
| 320 | p->nHdr, p->nBody); |
| 321 | for(i=0; i<p->nHdr; i++){ |
| 322 | email_hdr_unfold(p->azHdr[i]); |
| 323 | fossil_print("%3d: %s\n", i, p->azHdr[i]); |
| 324 | } |
| 325 | for(i=0; i<p->nBody; i++){ |
| 326 | fossil_print("\nBODY %d mime \"%s\" encoding %d", |
| 327 | i, p->aBody[i].zMimetype, p->aBody[i].encoding); |
| 328 | if( p->aBody[i].zFilename ){ |
| 329 | fossil_print(" filename \"%s\"", p->aBody[i].zFilename); |
| 330 | } |
| 331 | fossil_print("\n"); |
| 332 | if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue; |
| 333 | switch( p->aBody[i].encoding ){ |
| 334 | case EMAILENC_B64: { |
| 335 | int n = 0; |
| 336 | decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); |
| 337 | fossil_print("%s", p->aBody[i].zContent); |
| 338 | if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); |
| 339 | break; |
| 340 | } |
| 341 | case EMAILENC_QUOTED: { |
| 342 | int n = 0; |
| 343 | decodeQuotedPrintable(p->aBody[i].zContent, &n); |
| 344 | fossil_print("%s", p->aBody[i].zContent); |
| 345 | if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); |
| 346 | break; |
| 347 | } |
| 348 | default: { |
| 349 | fossil_print("%s\n", p->aBody[i].zContent); |
| 350 | break; |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | emailtoc_free(p); |
| 355 | blob_reset(&email); |
| 356 | } |
| 357 | |
| 358 | /* |
| 359 | ** Add the select/option box to the timeline submenu that shows |
| 360 | ** the various email message formats. |
| 361 | */ |
| 362 | static void webmail_f_submenu(void){ |
| 363 | static const char *const az[] = { |
| 364 | "0", "Normal", |
| 365 | "1", "Decoded", |
| 366 | "2", "Raw", |
| 367 | }; |
| 368 | style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0); |
| 369 | } |
| 370 | |
| 371 | /* |
| 372 | ** If the first N characters of z[] are the name of a header field |
| 373 | ** that should be shown in "Normal" mode, then return 1. |
| 374 | */ |
| 375 | static int webmail_normal_header(const char *z, int N){ |
| 376 | static const char *const az[] = { |
| 377 | "To", "Cc", "Bcc", "Date", "From", "Subject", |
| 378 | }; |
| 379 | int i; |
| 380 | for(i=0; i<sizeof(az)/sizeof(az[0]); i++){ |
| 381 | if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1; |
| 382 | } |
| 383 | return 0; |
| 384 | } |
| 385 | |
| 386 | /* |
| 387 | ** Paint a page showing a single email message |
| 388 | */ |
| 389 | static void webmail_show_one_message( |
| 390 | HQuery *pUrl, /* Calling context */ |
| 391 | int emailid, /* emailbox.ebid to display */ |
| 392 | const char *zUser /* User who owns it, or NULL if does not matter */ |
| 393 | ){ |
| 394 | Blob sql; |
| 395 | Stmt q; |
| 396 | int eState = -1; |
| 397 | int eTranscript = 0; |
| 398 | char zENum[30]; |
| 399 | style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0)); |
| 400 | webmail_f_submenu(); |
| 401 | blob_init(&sql, 0, 0); |
| 402 | db_begin_transaction(); |
| 403 | blob_append_sql(&sql, |
| 404 | "SELECT decompress(etxt), estate, emailblob.ets" |
| 405 | " FROM emailblob, emailbox" |
| 406 | " WHERE emailid=emsgid AND ebid=%d", |
| 407 | emailid |
| 408 | ); |
| 409 | if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); |
| 410 | db_prepare_blob(&q, &sql); |
| 411 | blob_reset(&sql); |
| 412 | style_set_current_feature("webmail"); |
| 413 | style_header("Message %d",emailid); |
| 414 | if( db_step(&q)==SQLITE_ROW ){ |
| 415 | Blob msg = db_column_text_as_blob(&q, 0); |
| 416 | int eFormat = atoi(PD("f","0")); |
| 417 | eState = db_column_int(&q, 1); |
| 418 | eTranscript = db_column_int(&q, 2); |
| 419 | if( eFormat==2 ){ |
| 420 | @ <pre>%h(db_column_text(&q, 0))</pre> |
| 421 | }else{ |
| 422 | EmailToc *p = emailtoc_from_email(&msg); |
| 423 | int i, j; |
| 424 | @ <p> |
| 425 | for(i=0; i<p->nHdr; i++){ |
| 426 | char *z = p->azHdr[i]; |
| 427 | email_hdr_unfold(z); |
| 428 | for(j=0; z[j] && z[j]!=':'; j++){} |
| 429 | if( eFormat==0 && !webmail_normal_header(z, j) ) continue; |
| 430 | if( z[j]!=':' ){ |
| 431 | @ %h(z)<br> |
| 432 | }else{ |
| 433 | z[j] = 0; |
| 434 | @ <b>%h(z):</b> %h(z+j+1)<br> |
| 435 | } |
| 436 | } |
| 437 | for(i=0; i<p->nBody; i++){ |
| 438 | @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ |
| 439 | if( p->aBody[i].zFilename ){ |
| 440 | @ "%h(p->aBody[i].zFilename)" |
| 441 | } |
| 442 | @ </b> |
| 443 | if( eFormat==0 ){ |
| 444 | if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue; |
| 445 | if( p->aBody[i].zFilename ) continue; |
| 446 | }else{ |
| 447 | if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; |
| 448 | } |
| 449 | switch( p->aBody[i].encoding ){ |
| 450 | case EMAILENC_B64: { |
| 451 | int n = 0; |
| 452 | decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); |
| 453 | break; |
| 454 | } |
| 455 | case EMAILENC_QUOTED: { |
| 456 | int n = 0; |
| 457 | decodeQuotedPrintable(p->aBody[i].zContent, &n); |
| 458 | break; |
| 459 | } |
| 460 | } |
| 461 | @ <pre>%h(p->aBody[i].zContent)</pre> |
| 462 | } |
| 463 | } |
| 464 | } |
| 465 | db_finalize(&q); |
| 466 | |
| 467 | /* Optionally show the SMTP transcript */ |
| 468 | if( eTranscript>0 |
| 469 | && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript) |
| 470 | ){ |
| 471 | if( P("ts")==0 ){ |
| 472 | sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid); |
| 473 | style_submenu_element("SMTP Transcript","%s", |
| 474 | url_render(pUrl, "ts", "1", "id", zENum)); |
| 475 | }else{ |
| 476 | db_prepare(&q, |
| 477 | "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript |
| 478 | ); |
| 479 | if( db_step(&q)==SQLITE_ROW ){ |
| 480 | const char *zTranscript = db_column_text(&q, 0); |
| 481 | @ <hr> |
| 482 | @ <pre>%h(zTranscript)</pre> |
| 483 | } |
| 484 | db_finalize(&q); |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | if( eState==0 ){ |
| 489 | /* If is message is currently Unread, change it to Read */ |
| 490 | blob_append_sql(&sql, |
| 491 | "UPDATE emailbox SET estate=1 " |
| 492 | " WHERE estate=0 AND ebid=%d", |
| 493 | emailid |
| 494 | ); |
| 495 | if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); |
| 496 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 497 | blob_reset(&sql); |
| 498 | eState = 1; |
| 499 | } |
| 500 | |
| 501 | url_add_parameter(pUrl, "id", 0); |
| 502 | sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid); |
| 503 | if( eState==2 ){ |
| 504 | style_submenu_element("Undelete","%s", |
| 505 | url_render(pUrl,"read","1",zENum,"1")); |
| 506 | } |
| 507 | if( eState==1 ){ |
| 508 | style_submenu_element("Delete", "%s", |
| 509 | url_render(pUrl,"trash","1",zENum,"1")); |
| 510 | style_submenu_element("Mark As Unread", "%s", |
| 511 | url_render(pUrl,"unread","1",zENum,"1")); |
| 512 | } |
| 513 | if( eState==3 ){ |
| 514 | style_submenu_element("Delete", "%s", |
| 515 | url_render(pUrl,"trash","1",zENum,"1")); |
| 516 | } |
| 517 | |
| 518 | db_end_transaction(0); |
| 519 | style_finish_page(); |
| 520 | return; |
| 521 | } |
| 522 | |
| 523 | /* |
| 524 | ** Scan the query parameters looking for parameters with name of the |
| 525 | ** form "eN" where N is an integer. For all such integers, change |
| 526 | ** the state of every emailbox entry with ebid==N to eStateNew provided |
| 527 | ** that either zUser is NULL or matches. |
| 528 | ** |
| 529 | ** Or if eNewState==99, then delete the entries. |
| 530 | */ |
| 531 | static void webmail_change_state(int eNewState, const char *zUser){ |
| 532 | Blob sql; |
| 533 | int sep = '('; |
| 534 | int i; |
| 535 | const char *zName; |
| 536 | int n; |
| 537 | if( !cgi_csrf_safe(0) ) return; |
| 538 | blob_init(&sql, 0, 0); |
| 539 | if( eNewState==99 ){ |
| 540 | blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN "); |
| 541 | }else{ |
| 542 | blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ", |
| 543 | eNewState); |
| 544 | } |
| 545 | for(i=0; (zName = cgi_parameter_name(i))!=0; i++){ |
| 546 | if( zName[0]!='e' ) continue; |
| 547 | if( !fossil_isdigit(zName[1]) ) continue; |
| 548 | n = atoi(zName+1); |
| 549 | blob_append_sql(&sql, "%c%d", sep, n); |
| 550 | sep = ','; |
| 551 | } |
| 552 | if( zUser ){ |
| 553 | blob_append_sql(&sql, ") AND euser=%Q", zUser); |
| 554 | }else{ |
| 555 | blob_append_sql(&sql, ")"); |
| 556 | } |
| 557 | if( sep==',' ){ |
| 558 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 559 | } |
| 560 | blob_reset(&sql); |
| 561 | } |
| 562 | |
| 563 | |
| 564 | /* |
| 565 | ** Add the select/option box to the timeline submenu that shows |
| 566 | ** which messages to include in the index. |
| 567 | */ |
| 568 | static void webmail_d_submenu(void){ |
| 569 | static const char *const az[] = { |
| 570 | "0", "InBox", |
| 571 | "1", "Unread", |
| 572 | "2", "Trash", |
| 573 | "3", "Sent", |
| 574 | "4", "Everything", |
| 575 | }; |
| 576 | style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0); |
| 577 | } |
| 578 | |
| 579 | /* |
| 580 | ** WEBPAGE: webmail |
| 581 | ** |
| 582 | ** This page can be used to read content from the EMAILBOX table |
| 583 | ** that contains email received by the "fossil smtpd" command. |
| 584 | ** |
| 585 | ** Query parameters: |
| 586 | ** |
| 587 | ** id=N Show a single email entry emailbox.ebid==N |
| 588 | ** f=N Display format. 0: decoded 1: raw |
| 589 | ** user=USER Show mailbox for USER (admin only). |
| 590 | ** user=* Show mailbox for all users (admin only). |
| 591 | ** d=N 0: inbox+unread 1: unread-only 2: trash 3: all |
| 592 | ** eN Select email entry emailbox.ebid==N |
| 593 | ** trash Move selected entries to trash (estate=2) |
| 594 | ** read Mark selected entries as read (estate=1) |
| 595 | ** unread Mark selected entries as unread (estate=0) |
| 596 | ** |
| 597 | */ |
| 598 | void webmail_page(void){ |
| 599 | int emailid; |
| 600 | Stmt q; |
| 601 | Blob sql; |
| 602 | int showAll = 0; |
| 603 | const char *zUser = 0; |
| 604 | int d = 0; /* Display mode. 0..3. d= query parameter */ |
| 605 | int pg = 0; /* Page number */ |
| 606 | int N = 50; /* Results per page */ |
| 607 | int got; /* Number of results on this page */ |
| 608 | char zPPg[30]; /* Previous page */ |
| 609 | char zNPg[30]; /* Next page */ |
| 610 | HQuery url; |
| 611 | login_check_credentials(); |
| 612 | if( !login_is_individual() ){ |
| 613 | login_needed(0); |
| 614 | return; |
| 615 | } |
| 616 | style_set_current_feature("webmail"); |
| 617 | if( !db_table_exists("repository","emailbox") ){ |
| 618 | style_header("Webmail Not Available"); |
| 619 | @ <p>This repository is not configured to provide webmail</p> |
| 620 | style_finish_page(); |
| 621 | return; |
| 622 | } |
| 623 | add_content_sql_commands(g.db); |
| 624 | emailid = atoi(PD("id","0")); |
| 625 | url_initialize(&url, "webmail"); |
| 626 | if( g.perm.Admin ){ |
| 627 | zUser = PD("user",g.zLogin); |
| 628 | if( zUser ){ |
| 629 | url_add_parameter(&url, "user", zUser); |
| 630 | if( fossil_strcmp(zUser,"*")==0 ){ |
| 631 | showAll = 1; |
| 632 | zUser = 0; |
| 633 | } |
| 634 | } |
| 635 | }else{ |
| 636 | zUser = g.zLogin; |
| 637 | } |
| 638 | if( P("d") ) url_add_parameter(&url, "d", P("d")); |
| 639 | if( emailid>0 ){ |
| 640 | webmail_show_one_message(&url, emailid, zUser); |
| 641 | return; |
| 642 | } |
| 643 | style_header("Webmail"); |
| 644 | webmail_d_submenu(); |
| 645 | db_begin_transaction(); |
| 646 | if( P("trash")!=0 ) webmail_change_state(2,zUser); |
| 647 | if( P("unread")!=0 ) webmail_change_state(0,zUser); |
| 648 | if( P("read")!=0 ) webmail_change_state(1,zUser); |
| 649 | if( P("purge")!=0 ) webmail_change_state(99,zUser); |
| 650 | blob_init(&sql, 0, 0); |
| 651 | blob_append_sql(&sql, |
| 652 | "CREATE TEMP TABLE tmbox AS " |
| 653 | "SELECT ebid," /* 0 */ |
| 654 | " efrom," /* 1 */ |
| 655 | " datetime(edate,'unixepoch')," /* 2 */ |
| 656 | " estate," /* 3 */ |
| 657 | " esubject," /* 4 */ |
| 658 | " euser" /* 5 */ |
| 659 | " FROM emailbox" |
| 660 | ); |
| 661 | d = atoi(PD("d","0")); |
| 662 | switch( d ){ |
| 663 | case 0: { /* Show unread and read */ |
| 664 | blob_append_sql(&sql, " WHERE estate<=1"); |
| 665 | break; |
| 666 | } |
| 667 | case 1: { /* Unread messages only */ |
| 668 | blob_append_sql(&sql, " WHERE estate=0"); |
| 669 | break; |
| 670 | } |
| 671 | case 2: { /* Trashcan only */ |
| 672 | blob_append_sql(&sql, " WHERE estate=2"); |
| 673 | break; |
| 674 | } |
| 675 | case 3: { /* Outgoing email only */ |
| 676 | blob_append_sql(&sql, " WHERE estate=3"); |
| 677 | break; |
| 678 | } |
| 679 | case 4: { /* Everything */ |
| 680 | blob_append_sql(&sql, " WHERE 1"); |
| 681 | break; |
| 682 | } |
| 683 | } |
| 684 | if( showAll ){ |
| 685 | style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); |
| 686 | }else if( zUser!=0 ){ |
| 687 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 688 | if( fossil_strcmp(zUser, g.zLogin)!=0 ){ |
| 689 | style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); |
| 690 | } |
| 691 | if( zUser ){ |
| 692 | blob_append_sql(&sql, " AND euser=%Q", zUser); |
| 693 | }else{ |
| 694 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 695 | } |
| 696 | }else{ |
| 697 | if( g.perm.Admin ){ |
| 698 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 699 | } |
| 700 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 701 | } |
| 702 | pg = atoi(PD("pg","0")); |
| 703 | blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N); |
| 704 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 705 | got = db_int(0, "SELECT count(*) FROM tmbox"); |
| 706 | db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N); |
| 707 | blob_reset(&sql); |
| 708 | @ <form action="%R/webmail" method="POST"> |
| 709 | @ <input type="hidden" name="d" value="%d(d)"> |
| 710 | @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")"> |
| 711 | @ <table border="0" width="100%%"> |
| 712 | @ <tr><td align="left"> |
| 713 | if( d==2 ){ |
| 714 | @ <input type="submit" name="read" value="Undelete"> |
| 715 | @ <input type="submit" name="purge" value="Delete Permanently"> |
| 716 | }else{ |
| 717 | @ <input type="submit" name="trash" value="Delete"> |
| 718 | if( d!=1 ){ |
| 719 | @ <input type="submit" name="unread" value="Mark as unread"> |
| 720 | } |
| 721 | @ <input type="submit" name="read" value="Mark as read"> |
| 722 | } |
| 723 | @ <button onclick="webmailSelectAll(); return false;">Select All</button> |
| 724 | @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a> |
| 725 | @ </td><td align="right"> |
| 726 | if( pg>0 ){ |
| 727 | sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1); |
| 728 | @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">< Newer</a> |
| 729 | } |
| 730 | if( got>50 ){ |
| 731 | sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1); |
| 732 | @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older ></a></td> |
| 733 | } |
| 734 | @ </table> |
| 735 | @ <table> |
| 736 | while( db_step(&q)==SQLITE_ROW ){ |
| 737 | const char *zId = db_column_text(&q,0); |
| 738 | const char *zFrom = db_column_text(&q, 1); |
| 739 | const char *zDate = db_column_text(&q, 2); |
| 740 | const char *zSubject = db_column_text(&q, 4); |
| 741 | if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)"; |
| 742 | @ <tr> |
| 743 | @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td> |
| 744 | @ <td>%h(zFrom)</td> |
| 745 | @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \ |
| 746 | @ %s(zDate)</td> |
| 747 | if( showAll ){ |
| 748 | const char *zTo = db_column_text(&q,5); |
| 749 | @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td> |
| 750 | } |
| 751 | @ </tr> |
| 752 | } |
| 753 | db_finalize(&q); |
| 754 | @ </table> |
| 755 | @ </form> |
| 756 | @ <script> |
| 757 | @ function webmailSelectAll(){ |
| 758 | @ var x = document.getElementsByClassName("webmailckbox"); |
| 759 | @ for(i=0; i<x.length; i++){ |
| 760 | @ x[i].checked = true; |
| 761 | @ } |
| 762 | @ } |
| 763 | @ </script> |
| 764 | style_finish_page(); |
| 765 | db_end_transaction(0); |
| 766 | } |
| 767 | |
| 768 | /* |
| 769 | ** WEBPAGE: emailblob |
| 770 | ** |
| 771 | ** This page, accessible only to administrators, allows easy viewing of |
| 772 | ** the emailblob table - the table that contains the text of email messages |
| 773 | ** both inbound and outbound, and transcripts of SMTP sessions. |
| 774 | ** |
| 775 | ** id=N Show the text of emailblob with emailid==N |
| 776 | ** |
| 777 | */ |
| 778 | void webmail_emailblob_page(void){ |
| 779 | int id = atoi(PD("id","0")); |
| 780 | Stmt q; |
| 781 | login_check_credentials(); |
| 782 | if( !g.perm.Setup ){ |
| 783 | login_needed(0); |
| 784 | return; |
| 785 | } |
| 786 | add_content_sql_commands(g.db); |
| 787 | style_set_current_feature("webmail"); |
| 788 | style_header("emailblob table"); |
| 789 | if( id>0 ){ |
| 790 | style_submenu_element("Index", "%R/emailblob"); |
| 791 | @ <ul> |
| 792 | db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id); |
| 793 | while( db_step(&q)==SQLITE_ROW ){ |
| 794 | int id = db_column_int(&q, 0); |
| 795 | @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a> |
| 796 | } |
| 797 | db_finalize(&q); |
| 798 | db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id); |
| 799 | while( db_step(&q)==SQLITE_ROW ){ |
| 800 | const char *zUser = db_column_text(&q, 0); |
| 801 | int e = db_column_int(&q, 1); |
| 802 | @ <li> emailbox for %h(zUser) state %d(e) |
| 803 | } |
| 804 | db_finalize(&q); |
| 805 | db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id); |
| 806 | while( db_step(&q)==SQLITE_ROW ){ |
| 807 | const char *zFrom = db_column_text(&q, 0); |
| 808 | const char *zTo = db_column_text(&q, 1); |
| 809 | @ <li> emailoutq message body from %h(zFrom) to %h(zTo) |
| 810 | } |
| 811 | db_finalize(&q); |
| 812 | db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id); |
| 813 | while( db_step(&q)==SQLITE_ROW ){ |
| 814 | const char *zFrom = db_column_text(&q, 0); |
| 815 | const char *zTo = db_column_text(&q, 1); |
| 816 | @ <li> emailoutq transcript from %h(zFrom) to %h(zTo) |
| 817 | } |
| 818 | db_finalize(&q); |
| 819 | @ </ul> |
| 820 | @ <hr> |
| 821 | db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", |
| 822 | id); |
| 823 | while( db_step(&q)==SQLITE_ROW ){ |
| 824 | const char *zContent = db_column_text(&q, 0); |
| 825 | @ <pre>%h(zContent)</pre> |
| 826 | } |
| 827 | db_finalize(&q); |
| 828 | }else{ |
| 829 | style_submenu_element("emailoutq table","%R/emailoutq"); |
| 830 | db_prepare(&q, |
| 831 | "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz," |
| 832 | " length(etxt)" |
| 833 | " FROM emailblob ORDER BY etime DESC, emailid DESC"); |
| 834 | @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ |
| 835 | @ data-column-types='nnntkk'> |
| 836 | @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \ |
| 837 | @ <th> uncompressed <th> compressed </tr></thead><tbody> |
| 838 | while( db_step(&q)==SQLITE_ROW ){ |
| 839 | int id = db_column_int(&q, 0); |
| 840 | int nref = db_column_int(&q, 1); |
| 841 | int ets = db_column_int(&q, 2); |
| 842 | const char *zDate = db_column_text(&q, 3); |
| 843 | int sz = db_column_int(&q,4); |
| 844 | int csz = db_column_int(&q,5); |
| 845 | @ <tr> |
| 846 | @ <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a> |
| 847 | @ <td align="right">%d(nref)</td> |
| 848 | if( ets>0 ){ |
| 849 | @ <td align="right">%d(ets)</td> |
| 850 | }else{ |
| 851 | @ <td> </td> |
| 852 | } |
| 853 | @ <td>%h(zDate)</td> |
| 854 | @ <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td> |
| 855 | @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td> |
| 856 | @ </tr> |
| 857 | } |
| 858 | @ </tbody></table> |
| 859 | db_finalize(&q); |
| 860 | style_table_sorter(); |
| 861 | } |
| 862 | style_finish_page(); |
| 863 | } |
| 864 | |
| 865 | /* |
| 866 | ** WEBPAGE: emailoutq |
| 867 | ** |
| 868 | ** This page, accessible only to administrators, allows easy viewing of |
| 869 | ** the emailoutq table - the table that contains the email messages |
| 870 | ** that are queued for transmission via SMTP. |
| 871 | */ |
| 872 | void webmail_emailoutq_page(void){ |
| 873 | Stmt q; |
| 874 | login_check_credentials(); |
| 875 | if( !g.perm.Setup ){ |
| 876 | login_needed(0); |
| 877 | return; |
| 878 | } |
| 879 | add_content_sql_commands(g.db); |
| 880 | style_set_current_feature("webmail"); |
| 881 | style_header("emailoutq table"); |
| 882 | style_submenu_element("emailblob table","%R/emailblob"); |
| 883 | db_prepare(&q, |
| 884 | "SELECT edomain, efrom, eto, emsgid, " |
| 885 | " datetime(ectime,'unixepoch')," |
| 886 | " datetime(nullif(emtime,0),'unixepoch')," |
| 887 | " ensend, ets" |
| 888 | " FROM emailoutq" |
| 889 | ); |
| 890 | @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ |
| 891 | @ data-column-types='tttnttnn'> |
| 892 | @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \ |
| 893 | @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody> |
| 894 | while( db_step(&q)==SQLITE_ROW ){ |
| 895 | const char *zDomain = db_column_text(&q, 0); |
| 896 | const char *zFrom = db_column_text(&q, 1); |
| 897 | const char *zTo = db_column_text(&q, 2); |
| 898 | int emsgid = db_column_int(&q, 3); |
| 899 | const char *zCTime = db_column_text(&q, 4); |
| 900 | const char *zMTime = db_column_text(&q, 5); |
| 901 | int ensend = db_column_int(&q, 6); |
| 902 | int ets = db_column_int(&q, 7); |
| 903 | @ <tr> |
| 904 | @ <td>%h(zDomain) |
| 905 | @ <td>%h(zFrom) |
| 906 | @ <td>%h(zTo) |
| 907 | @ <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a> |
| 908 | @ <td>%h(zCTime) |
| 909 | @ <td>%h(zMTime) |
| 910 | @ <td align="right">%d(ensend) |
| 911 | if( ets>0 ){ |
| 912 | @ <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td> |
| 913 | }else{ |
| 914 | @ <td> </td> |
| 915 | } |
| 916 | } |
| 917 | @ </tbody></table> |
| 918 | db_finalize(&q); |
| 919 | style_table_sorter(); |
| 920 | style_finish_page(); |
| 921 | } |
| --- a/src/webmail.c | |
| +++ b/src/webmail.c | |
| @@ -1,921 +0,0 @@ | |
+14
-8
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -367,10 +367,12 @@ | ||
| 367 | 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | 368 | } |
| 369 | 369 | } |
| 370 | 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | 371 | @ available on this server.</li> |
| 372 | + @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a> | |
| 373 | + @ available on this server.</li> | |
| 372 | 374 | if( g.perm.ModWiki ){ |
| 373 | 375 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 374 | 376 | } |
| 375 | 377 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 376 | 378 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | ||
| 1978 | 1980 | ** <html><body>...</body></html>. |
| 1979 | 1981 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1980 | 1982 | ** the text/plain mimetype, its HTML-escaped output |
| 1981 | 1983 | ** will be wrapped in <pre>...</pre>. |
| 1982 | 1984 | ** |
| 1983 | -** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? | |
| 1985 | +** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS? | |
| 1984 | 1986 | ** |
| 1985 | 1987 | ** Create a new or commit changes to an existing wiki page or |
| 1986 | 1988 | ** technote from FILE or from standard input. PAGENAME is the |
| 1987 | -** name of the wiki entry or the timeline comment of the | |
| 1988 | -** technote. | |
| 1989 | +** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of | |
| 1990 | +** the technote. | |
| 1989 | 1991 | ** |
| 1990 | 1992 | ** Options: |
| 1991 | 1993 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1992 | 1994 | ** Defaults to the type used by |
| 1993 | 1995 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | ||
| 1996 | 1998 | ** text/x-markdown and text/plain. fossil, |
| 1997 | 1999 | ** markdown or plain can be specified as |
| 1998 | 2000 | ** synonyms of these values. |
| 1999 | 2001 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2000 | 2002 | ** the technote to be created or |
| 2001 | -** updated. When updating a tech note | |
| 2002 | -** the most recently modified tech note | |
| 2003 | -** with the specified timestamp will be | |
| 2004 | -** updated. | |
| 2003 | +** updated. The timestamp specifies when | |
| 2004 | +** this technote appears in the timeline | |
| 2005 | +** and is its permanent handle although | |
| 2006 | +** it may not be unique. When updating | |
| 2007 | +** a technote the most recently modified | |
| 2008 | +** tech note with the specified timestamp | |
| 2009 | +** will be updated. | |
| 2005 | 2010 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2006 | -** updated by its technote id. | |
| 2011 | +** updated by its technote id, which is | |
| 2012 | +** its UUID. | |
| 2007 | 2013 | ** --technote-tags TAGS The set of tags for a technote. |
| 2008 | 2014 | ** --technote-bgcolor COLOR The color used for the technote |
| 2009 | 2015 | ** on the timeline. |
| 2010 | 2016 | ** |
| 2011 | 2017 | ** > fossil wiki list ?OPTIONS? |
| 2012 | 2018 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -367,10 +367,12 @@ | |
| 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | } |
| 369 | } |
| 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | @ available on this server.</li> |
| 372 | if( g.perm.ModWiki ){ |
| 373 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 374 | } |
| 375 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 376 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | |
| 1978 | ** <html><body>...</body></html>. |
| 1979 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1980 | ** the text/plain mimetype, its HTML-escaped output |
| 1981 | ** will be wrapped in <pre>...</pre>. |
| 1982 | ** |
| 1983 | ** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? |
| 1984 | ** |
| 1985 | ** Create a new or commit changes to an existing wiki page or |
| 1986 | ** technote from FILE or from standard input. PAGENAME is the |
| 1987 | ** name of the wiki entry or the timeline comment of the |
| 1988 | ** technote. |
| 1989 | ** |
| 1990 | ** Options: |
| 1991 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1992 | ** Defaults to the type used by |
| 1993 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | |
| 1996 | ** text/x-markdown and text/plain. fossil, |
| 1997 | ** markdown or plain can be specified as |
| 1998 | ** synonyms of these values. |
| 1999 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2000 | ** the technote to be created or |
| 2001 | ** updated. When updating a tech note |
| 2002 | ** the most recently modified tech note |
| 2003 | ** with the specified timestamp will be |
| 2004 | ** updated. |
| 2005 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2006 | ** updated by its technote id. |
| 2007 | ** --technote-tags TAGS The set of tags for a technote. |
| 2008 | ** --technote-bgcolor COLOR The color used for the technote |
| 2009 | ** on the timeline. |
| 2010 | ** |
| 2011 | ** > fossil wiki list ?OPTIONS? |
| 2012 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -367,10 +367,12 @@ | |
| 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | } |
| 369 | } |
| 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | @ available on this server.</li> |
| 372 | @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a> |
| 373 | @ available on this server.</li> |
| 374 | if( g.perm.ModWiki ){ |
| 375 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 376 | } |
| 377 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 378 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | |
| 1980 | ** <html><body>...</body></html>. |
| 1981 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1982 | ** the text/plain mimetype, its HTML-escaped output |
| 1983 | ** will be wrapped in <pre>...</pre>. |
| 1984 | ** |
| 1985 | ** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS? |
| 1986 | ** |
| 1987 | ** Create a new or commit changes to an existing wiki page or |
| 1988 | ** technote from FILE or from standard input. PAGENAME is the |
| 1989 | ** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of |
| 1990 | ** the technote. |
| 1991 | ** |
| 1992 | ** Options: |
| 1993 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1994 | ** Defaults to the type used by |
| 1995 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | |
| 1998 | ** text/x-markdown and text/plain. fossil, |
| 1999 | ** markdown or plain can be specified as |
| 2000 | ** synonyms of these values. |
| 2001 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2002 | ** the technote to be created or |
| 2003 | ** updated. The timestamp specifies when |
| 2004 | ** this technote appears in the timeline |
| 2005 | ** and is its permanent handle although |
| 2006 | ** it may not be unique. When updating |
| 2007 | ** a technote the most recently modified |
| 2008 | ** tech note with the specified timestamp |
| 2009 | ** will be updated. |
| 2010 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2011 | ** updated by its technote id, which is |
| 2012 | ** its UUID. |
| 2013 | ** --technote-tags TAGS The set of tags for a technote. |
| 2014 | ** --technote-bgcolor COLOR The color used for the technote |
| 2015 | ** on the timeline. |
| 2016 | ** |
| 2017 | ** > fossil wiki list ?OPTIONS? |
| 2018 |
+14
-8
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -367,10 +367,12 @@ | ||
| 367 | 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | 368 | } |
| 369 | 369 | } |
| 370 | 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | 371 | @ available on this server.</li> |
| 372 | + @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a> | |
| 373 | + @ available on this server.</li> | |
| 372 | 374 | if( g.perm.ModWiki ){ |
| 373 | 375 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 374 | 376 | } |
| 375 | 377 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 376 | 378 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | ||
| 1978 | 1980 | ** <html><body>...</body></html>. |
| 1979 | 1981 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1980 | 1982 | ** the text/plain mimetype, its HTML-escaped output |
| 1981 | 1983 | ** will be wrapped in <pre>...</pre>. |
| 1982 | 1984 | ** |
| 1983 | -** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? | |
| 1985 | +** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS? | |
| 1984 | 1986 | ** |
| 1985 | 1987 | ** Create a new or commit changes to an existing wiki page or |
| 1986 | 1988 | ** technote from FILE or from standard input. PAGENAME is the |
| 1987 | -** name of the wiki entry or the timeline comment of the | |
| 1988 | -** technote. | |
| 1989 | +** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of | |
| 1990 | +** the technote. | |
| 1989 | 1991 | ** |
| 1990 | 1992 | ** Options: |
| 1991 | 1993 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1992 | 1994 | ** Defaults to the type used by |
| 1993 | 1995 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | ||
| 1996 | 1998 | ** text/x-markdown and text/plain. fossil, |
| 1997 | 1999 | ** markdown or plain can be specified as |
| 1998 | 2000 | ** synonyms of these values. |
| 1999 | 2001 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2000 | 2002 | ** the technote to be created or |
| 2001 | -** updated. When updating a tech note | |
| 2002 | -** the most recently modified tech note | |
| 2003 | -** with the specified timestamp will be | |
| 2004 | -** updated. | |
| 2003 | +** updated. The timestamp specifies when | |
| 2004 | +** this technote appears in the timeline | |
| 2005 | +** and is its permanent handle although | |
| 2006 | +** it may not be unique. When updating | |
| 2007 | +** a technote the most recently modified | |
| 2008 | +** tech note with the specified timestamp | |
| 2009 | +** will be updated. | |
| 2005 | 2010 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2006 | -** updated by its technote id. | |
| 2011 | +** updated by its technote id, which is | |
| 2012 | +** its UUID. | |
| 2007 | 2013 | ** --technote-tags TAGS The set of tags for a technote. |
| 2008 | 2014 | ** --technote-bgcolor COLOR The color used for the technote |
| 2009 | 2015 | ** on the timeline. |
| 2010 | 2016 | ** |
| 2011 | 2017 | ** > fossil wiki list ?OPTIONS? |
| 2012 | 2018 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -367,10 +367,12 @@ | |
| 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | } |
| 369 | } |
| 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | @ available on this server.</li> |
| 372 | if( g.perm.ModWiki ){ |
| 373 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 374 | } |
| 375 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 376 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | |
| 1978 | ** <html><body>...</body></html>. |
| 1979 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1980 | ** the text/plain mimetype, its HTML-escaped output |
| 1981 | ** will be wrapped in <pre>...</pre>. |
| 1982 | ** |
| 1983 | ** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? |
| 1984 | ** |
| 1985 | ** Create a new or commit changes to an existing wiki page or |
| 1986 | ** technote from FILE or from standard input. PAGENAME is the |
| 1987 | ** name of the wiki entry or the timeline comment of the |
| 1988 | ** technote. |
| 1989 | ** |
| 1990 | ** Options: |
| 1991 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1992 | ** Defaults to the type used by |
| 1993 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | |
| 1996 | ** text/x-markdown and text/plain. fossil, |
| 1997 | ** markdown or plain can be specified as |
| 1998 | ** synonyms of these values. |
| 1999 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2000 | ** the technote to be created or |
| 2001 | ** updated. When updating a tech note |
| 2002 | ** the most recently modified tech note |
| 2003 | ** with the specified timestamp will be |
| 2004 | ** updated. |
| 2005 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2006 | ** updated by its technote id. |
| 2007 | ** --technote-tags TAGS The set of tags for a technote. |
| 2008 | ** --technote-bgcolor COLOR The color used for the technote |
| 2009 | ** on the timeline. |
| 2010 | ** |
| 2011 | ** > fossil wiki list ?OPTIONS? |
| 2012 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -367,10 +367,12 @@ | |
| 367 | @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> |
| 368 | } |
| 369 | } |
| 370 | @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> |
| 371 | @ available on this server.</li> |
| 372 | @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a> |
| 373 | @ available on this server.</li> |
| 374 | if( g.perm.ModWiki ){ |
| 375 | @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> |
| 376 | } |
| 377 | if( search_restrict(SRCH_WIKI)!=0 ){ |
| 378 | @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key |
| @@ -1978,16 +1980,16 @@ | |
| 1980 | ** <html><body>...</body></html>. |
| 1981 | ** -p|--pre If -h|-H is used and the page or technote has |
| 1982 | ** the text/plain mimetype, its HTML-escaped output |
| 1983 | ** will be wrapped in <pre>...</pre>. |
| 1984 | ** |
| 1985 | ** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS? |
| 1986 | ** |
| 1987 | ** Create a new or commit changes to an existing wiki page or |
| 1988 | ** technote from FILE or from standard input. PAGENAME is the |
| 1989 | ** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of |
| 1990 | ** the technote. |
| 1991 | ** |
| 1992 | ** Options: |
| 1993 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1994 | ** Defaults to the type used by |
| 1995 | ** the previous version of the |
| @@ -1996,16 +1998,20 @@ | |
| 1998 | ** text/x-markdown and text/plain. fossil, |
| 1999 | ** markdown or plain can be specified as |
| 2000 | ** synonyms of these values. |
| 2001 | ** -t|--technote DATETIME Specifies the timestamp of |
| 2002 | ** the technote to be created or |
| 2003 | ** updated. The timestamp specifies when |
| 2004 | ** this technote appears in the timeline |
| 2005 | ** and is its permanent handle although |
| 2006 | ** it may not be unique. When updating |
| 2007 | ** a technote the most recently modified |
| 2008 | ** tech note with the specified timestamp |
| 2009 | ** will be updated. |
| 2010 | ** -t|--technote TECHNOTE-ID Specifies the technote to be |
| 2011 | ** updated by its technote id, which is |
| 2012 | ** its UUID. |
| 2013 | ** --technote-tags TAGS The set of tags for a technote. |
| 2014 | ** --technote-bgcolor COLOR The color used for the technote |
| 2015 | ** on the timeline. |
| 2016 | ** |
| 2017 | ** > fossil wiki list ?OPTIONS? |
| 2018 |
+12
-2
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -251,11 +251,10 @@ | ||
| 251 | 251 | # |
| 252 | 252 | # TODO: If the list of supported versionable settings in "db.c" is modified, |
| 253 | 253 | # this list (and procedure) most likely needs to be modified as well. |
| 254 | 254 | # |
| 255 | 255 | set result [list \ |
| 256 | - allow-symlinks \ | |
| 257 | 256 | binary-glob \ |
| 258 | 257 | clean-glob \ |
| 259 | 258 | crlf-glob \ |
| 260 | 259 | crnl-glob \ |
| 261 | 260 | dotfiles \ |
| @@ -287,10 +286,16 @@ | ||
| 287 | 286 | backoffice-disable \ |
| 288 | 287 | backoffice-logfile \ |
| 289 | 288 | backoffice-nodelay \ |
| 290 | 289 | binary-glob \ |
| 291 | 290 | case-sensitive \ |
| 291 | + chat-alert-sound \ | |
| 292 | + chat-initial-history \ | |
| 293 | + chat-inline-images \ | |
| 294 | + chat-keep-count \ | |
| 295 | + chat-keep-days \ | |
| 296 | + chat-poll-timeout \ | |
| 292 | 297 | clean-glob \ |
| 293 | 298 | clearsign \ |
| 294 | 299 | comment-format \ |
| 295 | 300 | crlf-glob \ |
| 296 | 301 | crnl-glob \ |
| @@ -316,36 +321,41 @@ | ||
| 316 | 321 | fileedit-glob \ |
| 317 | 322 | forbid-delta-manifests \ |
| 318 | 323 | gdiff-command \ |
| 319 | 324 | gmerge-command \ |
| 320 | 325 | hash-digits \ |
| 326 | + hooks \ | |
| 321 | 327 | http-port \ |
| 322 | 328 | https-login \ |
| 323 | 329 | ignore-glob \ |
| 324 | 330 | keep-glob \ |
| 325 | 331 | localauth \ |
| 326 | 332 | lock-timeout \ |
| 327 | 333 | main-branch \ |
| 334 | + mainmenu \ | |
| 328 | 335 | manifest \ |
| 329 | 336 | max-loadavg \ |
| 330 | 337 | max-upload \ |
| 331 | 338 | mimetypes \ |
| 332 | 339 | mtime-changes \ |
| 333 | 340 | pgp-command \ |
| 341 | + preferred-diff-type \ | |
| 334 | 342 | proxy \ |
| 335 | 343 | redirect-to-https \ |
| 336 | 344 | relative-paths \ |
| 337 | 345 | repo-cksum \ |
| 338 | 346 | repolist-skin \ |
| 339 | 347 | safe-html \ |
| 340 | 348 | self-register \ |
| 349 | + sitemap-extra \ | |
| 341 | 350 | ssh-command \ |
| 342 | 351 | ssl-ca-location \ |
| 343 | 352 | ssl-identity \ |
| 344 | 353 | tclsh \ |
| 345 | 354 | th1-setup \ |
| 346 | 355 | th1-uri-regexp \ |
| 356 | + user-color-map \ | |
| 347 | 357 | uv-sync \ |
| 348 | 358 | web-browser] |
| 349 | 359 | |
| 350 | 360 | fossil test-th-eval "hasfeature legacyMvRm" |
| 351 | 361 | |
| @@ -602,11 +612,11 @@ | ||
| 602 | 612 | proc test_block_in_checkout { name rscript {tscript ""} } { |
| 603 | 613 | if {$::outside_fossil_repo || $::dirty_ckout} { |
| 604 | 614 | set $::CODE 0 |
| 605 | 615 | set $::RESULT "" |
| 606 | 616 | } else { |
| 607 | - run_in_checkout $rscript | |
| 617 | + uplevel 1 [list run_in_checkout $rscript] | |
| 608 | 618 | if {[string length $tscript] == 0} { |
| 609 | 619 | return "" |
| 610 | 620 | } else { |
| 611 | 621 | set code [catch { |
| 612 | 622 | uplevel 1 $tscript |
| 613 | 623 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -251,11 +251,10 @@ | |
| 251 | # |
| 252 | # TODO: If the list of supported versionable settings in "db.c" is modified, |
| 253 | # this list (and procedure) most likely needs to be modified as well. |
| 254 | # |
| 255 | set result [list \ |
| 256 | allow-symlinks \ |
| 257 | binary-glob \ |
| 258 | clean-glob \ |
| 259 | crlf-glob \ |
| 260 | crnl-glob \ |
| 261 | dotfiles \ |
| @@ -287,10 +286,16 @@ | |
| 287 | backoffice-disable \ |
| 288 | backoffice-logfile \ |
| 289 | backoffice-nodelay \ |
| 290 | binary-glob \ |
| 291 | case-sensitive \ |
| 292 | clean-glob \ |
| 293 | clearsign \ |
| 294 | comment-format \ |
| 295 | crlf-glob \ |
| 296 | crnl-glob \ |
| @@ -316,36 +321,41 @@ | |
| 316 | fileedit-glob \ |
| 317 | forbid-delta-manifests \ |
| 318 | gdiff-command \ |
| 319 | gmerge-command \ |
| 320 | hash-digits \ |
| 321 | http-port \ |
| 322 | https-login \ |
| 323 | ignore-glob \ |
| 324 | keep-glob \ |
| 325 | localauth \ |
| 326 | lock-timeout \ |
| 327 | main-branch \ |
| 328 | manifest \ |
| 329 | max-loadavg \ |
| 330 | max-upload \ |
| 331 | mimetypes \ |
| 332 | mtime-changes \ |
| 333 | pgp-command \ |
| 334 | proxy \ |
| 335 | redirect-to-https \ |
| 336 | relative-paths \ |
| 337 | repo-cksum \ |
| 338 | repolist-skin \ |
| 339 | safe-html \ |
| 340 | self-register \ |
| 341 | ssh-command \ |
| 342 | ssl-ca-location \ |
| 343 | ssl-identity \ |
| 344 | tclsh \ |
| 345 | th1-setup \ |
| 346 | th1-uri-regexp \ |
| 347 | uv-sync \ |
| 348 | web-browser] |
| 349 | |
| 350 | fossil test-th-eval "hasfeature legacyMvRm" |
| 351 | |
| @@ -602,11 +612,11 @@ | |
| 602 | proc test_block_in_checkout { name rscript {tscript ""} } { |
| 603 | if {$::outside_fossil_repo || $::dirty_ckout} { |
| 604 | set $::CODE 0 |
| 605 | set $::RESULT "" |
| 606 | } else { |
| 607 | run_in_checkout $rscript |
| 608 | if {[string length $tscript] == 0} { |
| 609 | return "" |
| 610 | } else { |
| 611 | set code [catch { |
| 612 | uplevel 1 $tscript |
| 613 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -251,11 +251,10 @@ | |
| 251 | # |
| 252 | # TODO: If the list of supported versionable settings in "db.c" is modified, |
| 253 | # this list (and procedure) most likely needs to be modified as well. |
| 254 | # |
| 255 | set result [list \ |
| 256 | binary-glob \ |
| 257 | clean-glob \ |
| 258 | crlf-glob \ |
| 259 | crnl-glob \ |
| 260 | dotfiles \ |
| @@ -287,10 +286,16 @@ | |
| 286 | backoffice-disable \ |
| 287 | backoffice-logfile \ |
| 288 | backoffice-nodelay \ |
| 289 | binary-glob \ |
| 290 | case-sensitive \ |
| 291 | chat-alert-sound \ |
| 292 | chat-initial-history \ |
| 293 | chat-inline-images \ |
| 294 | chat-keep-count \ |
| 295 | chat-keep-days \ |
| 296 | chat-poll-timeout \ |
| 297 | clean-glob \ |
| 298 | clearsign \ |
| 299 | comment-format \ |
| 300 | crlf-glob \ |
| 301 | crnl-glob \ |
| @@ -316,36 +321,41 @@ | |
| 321 | fileedit-glob \ |
| 322 | forbid-delta-manifests \ |
| 323 | gdiff-command \ |
| 324 | gmerge-command \ |
| 325 | hash-digits \ |
| 326 | hooks \ |
| 327 | http-port \ |
| 328 | https-login \ |
| 329 | ignore-glob \ |
| 330 | keep-glob \ |
| 331 | localauth \ |
| 332 | lock-timeout \ |
| 333 | main-branch \ |
| 334 | mainmenu \ |
| 335 | manifest \ |
| 336 | max-loadavg \ |
| 337 | max-upload \ |
| 338 | mimetypes \ |
| 339 | mtime-changes \ |
| 340 | pgp-command \ |
| 341 | preferred-diff-type \ |
| 342 | proxy \ |
| 343 | redirect-to-https \ |
| 344 | relative-paths \ |
| 345 | repo-cksum \ |
| 346 | repolist-skin \ |
| 347 | safe-html \ |
| 348 | self-register \ |
| 349 | sitemap-extra \ |
| 350 | ssh-command \ |
| 351 | ssl-ca-location \ |
| 352 | ssl-identity \ |
| 353 | tclsh \ |
| 354 | th1-setup \ |
| 355 | th1-uri-regexp \ |
| 356 | user-color-map \ |
| 357 | uv-sync \ |
| 358 | web-browser] |
| 359 | |
| 360 | fossil test-th-eval "hasfeature legacyMvRm" |
| 361 | |
| @@ -602,11 +612,11 @@ | |
| 612 | proc test_block_in_checkout { name rscript {tscript ""} } { |
| 613 | if {$::outside_fossil_repo || $::dirty_ckout} { |
| 614 | set $::CODE 0 |
| 615 | set $::RESULT "" |
| 616 | } else { |
| 617 | uplevel 1 [list run_in_checkout $rscript] |
| 618 | if {[string length $tscript] == 0} { |
| 619 | return "" |
| 620 | } else { |
| 621 | set code [catch { |
| 622 | uplevel 1 $tscript |
| 623 |
+12
-10
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -778,11 +778,12 @@ | ||
| 778 | 778 | {TH_ERROR: wrong # args: should be "defHeader"}} |
| 779 | 779 | |
| 780 | 780 | ############################################################################### |
| 781 | 781 | |
| 782 | 782 | fossil test-th-eval "defHeader" |
| 783 | -test th1-defHeader-2 {[string match *<body> [normalize_result]]} | |
| 783 | +test th1-defHeader-2 {[string match *<body> [normalize_result]] || \ | |
| 784 | + [string match "*<body class=\"\$current_feature\">" [normalize_result]]} | |
| 784 | 785 | |
| 785 | 786 | ############################################################################### |
| 786 | 787 | |
| 787 | 788 | fossil test-th-eval "styleHeader {Page Title Here}" |
| 788 | 789 | test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}} |
| @@ -1046,19 +1047,20 @@ | ||
| 1046 | 1047 | # command lists eliminates a dependence on order. |
| 1047 | 1048 | # |
| 1048 | 1049 | fossil test-th-eval "info commands" |
| 1049 | 1050 | set sorted_result [lsort $RESULT] |
| 1050 | 1051 | protOut "Sorted: $sorted_result" |
| 1051 | -set base_commands {anoncap anycap array artifact break breakpoint catch\ | |
| 1052 | - cgiHeaderLine checkout combobox continue copybtn date decorate \ | |
| 1053 | - defHeader dir enable_output encode64 error expr for getParameter \ | |
| 1054 | - glob_match globalState hascap hasfeature html htmlize http httpize if \ | |
| 1055 | - info insertCsrf lindex linecount list llength lsearch markdown nonce \ | |
| 1056 | - proc puts query randhex redirect regexp reinitialize rename render \ | |
| 1057 | - repository return searchable set setParameter setting stime string \ | |
| 1058 | - styleFooter styleHeader styleScript tclReady trace unset unversioned \ | |
| 1059 | - uplevel upvar utime verifyCsrf verifyLogin wiki} | |
| 1052 | +set base_commands {anoncap anycap array artifact break breakpoint \ | |
| 1053 | + builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \ | |
| 1054 | + combobox continue copybtn date decorate defHeader dir enable_htmlify \ | |
| 1055 | + enable_output encode64 error expr for foreach getParameter glob_match \ | |
| 1056 | + globalState hascap hasfeature html htmlize http httpize if info \ | |
| 1057 | + insertCsrf lappend lindex linecount list llength lsearch markdown \ | |
| 1058 | + nonce proc puts query randhex redirect regexp reinitialize rename \ | |
| 1059 | + render repository return searchable set setParameter setting stime \ | |
| 1060 | + string styleFooter styleHeader styleScript tclReady trace unset \ | |
| 1061 | + unversioned uplevel upvar utime verifyCsrf verifyLogin wiki} | |
| 1060 | 1062 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1061 | 1063 | if {$th1Tcl} { |
| 1062 | 1064 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1063 | 1065 | } else { |
| 1064 | 1066 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1065 | 1067 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -778,11 +778,12 @@ | |
| 778 | {TH_ERROR: wrong # args: should be "defHeader"}} |
| 779 | |
| 780 | ############################################################################### |
| 781 | |
| 782 | fossil test-th-eval "defHeader" |
| 783 | test th1-defHeader-2 {[string match *<body> [normalize_result]]} |
| 784 | |
| 785 | ############################################################################### |
| 786 | |
| 787 | fossil test-th-eval "styleHeader {Page Title Here}" |
| 788 | test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}} |
| @@ -1046,19 +1047,20 @@ | |
| 1046 | # command lists eliminates a dependence on order. |
| 1047 | # |
| 1048 | fossil test-th-eval "info commands" |
| 1049 | set sorted_result [lsort $RESULT] |
| 1050 | protOut "Sorted: $sorted_result" |
| 1051 | set base_commands {anoncap anycap array artifact break breakpoint catch\ |
| 1052 | cgiHeaderLine checkout combobox continue copybtn date decorate \ |
| 1053 | defHeader dir enable_output encode64 error expr for getParameter \ |
| 1054 | glob_match globalState hascap hasfeature html htmlize http httpize if \ |
| 1055 | info insertCsrf lindex linecount list llength lsearch markdown nonce \ |
| 1056 | proc puts query randhex redirect regexp reinitialize rename render \ |
| 1057 | repository return searchable set setParameter setting stime string \ |
| 1058 | styleFooter styleHeader styleScript tclReady trace unset unversioned \ |
| 1059 | uplevel upvar utime verifyCsrf verifyLogin wiki} |
| 1060 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1061 | if {$th1Tcl} { |
| 1062 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1063 | } else { |
| 1064 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1065 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -778,11 +778,12 @@ | |
| 778 | {TH_ERROR: wrong # args: should be "defHeader"}} |
| 779 | |
| 780 | ############################################################################### |
| 781 | |
| 782 | fossil test-th-eval "defHeader" |
| 783 | test th1-defHeader-2 {[string match *<body> [normalize_result]] || \ |
| 784 | [string match "*<body class=\"\$current_feature\">" [normalize_result]]} |
| 785 | |
| 786 | ############################################################################### |
| 787 | |
| 788 | fossil test-th-eval "styleHeader {Page Title Here}" |
| 789 | test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}} |
| @@ -1046,19 +1047,20 @@ | |
| 1047 | # command lists eliminates a dependence on order. |
| 1048 | # |
| 1049 | fossil test-th-eval "info commands" |
| 1050 | set sorted_result [lsort $RESULT] |
| 1051 | protOut "Sorted: $sorted_result" |
| 1052 | set base_commands {anoncap anycap array artifact break breakpoint \ |
| 1053 | builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \ |
| 1054 | combobox continue copybtn date decorate defHeader dir enable_htmlify \ |
| 1055 | enable_output encode64 error expr for foreach getParameter glob_match \ |
| 1056 | globalState hascap hasfeature html htmlize http httpize if info \ |
| 1057 | insertCsrf lappend lindex linecount list llength lsearch markdown \ |
| 1058 | nonce proc puts query randhex redirect regexp reinitialize rename \ |
| 1059 | render repository return searchable set setParameter setting stime \ |
| 1060 | string styleFooter styleHeader styleScript tclReady trace unset \ |
| 1061 | unversioned uplevel upvar utime verifyCsrf verifyLogin wiki} |
| 1062 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1063 | if {$th1Tcl} { |
| 1064 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1065 | } else { |
| 1066 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1067 |
+1
-1
| --- test/unversioned.test | ||
| +++ test/unversioned.test | ||
| @@ -328,11 +328,11 @@ | ||
| 328 | 328 | write_file unversioned-client1.txt "This is unversioned client file #1." |
| 329 | 329 | |
| 330 | 330 | ############################################################################### |
| 331 | 331 | |
| 332 | 332 | fossil_maybe_answer y clone $remote uvrepo.fossil |
| 333 | -fossil open uvrepo.fossil | |
| 333 | +fossil open -f uvrepo.fossil | |
| 334 | 334 | |
| 335 | 335 | ############################################################################### |
| 336 | 336 | |
| 337 | 337 | fossil unversioned list |
| 338 | 338 | test unversioned-45 {[normalize_result] eq {}} |
| 339 | 339 |
| --- test/unversioned.test | |
| +++ test/unversioned.test | |
| @@ -328,11 +328,11 @@ | |
| 328 | write_file unversioned-client1.txt "This is unversioned client file #1." |
| 329 | |
| 330 | ############################################################################### |
| 331 | |
| 332 | fossil_maybe_answer y clone $remote uvrepo.fossil |
| 333 | fossil open uvrepo.fossil |
| 334 | |
| 335 | ############################################################################### |
| 336 | |
| 337 | fossil unversioned list |
| 338 | test unversioned-45 {[normalize_result] eq {}} |
| 339 |
| --- test/unversioned.test | |
| +++ test/unversioned.test | |
| @@ -328,11 +328,11 @@ | |
| 328 | write_file unversioned-client1.txt "This is unversioned client file #1." |
| 329 | |
| 330 | ############################################################################### |
| 331 | |
| 332 | fossil_maybe_answer y clone $remote uvrepo.fossil |
| 333 | fossil open -f uvrepo.fossil |
| 334 | |
| 335 | ############################################################################### |
| 336 | |
| 337 | fossil unversioned list |
| 338 | test unversioned-45 {[normalize_result] eq {}} |
| 339 |
+4
-10
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -28,13 +28,13 @@ | ||
| 28 | 28 | |
| 29 | 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | 30 | |
| 31 | 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | 32 | |
| 33 | -SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c | |
| 33 | +SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c | |
| 34 | 34 | |
| 35 | -OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 35 | +OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | RC=$(DMDIR)\bin\rcc |
| 39 | 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | 40 | |
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | |
| 50 | 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | 52 | |
| 53 | 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | - +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 54 | + +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 55 | 55 | +echo fossil >> $@ |
| 56 | 56 | +echo fossil >> $@ |
| 57 | 57 | +echo $(LIBS) >> $@ |
| 58 | 58 | +echo. >> $@ |
| 59 | 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | ||
| 962 | 962 | $(TCC) -o$@ -c vfile_.c |
| 963 | 963 | |
| 964 | 964 | vfile_.c : $(SRCDIR)\vfile.c |
| 965 | 965 | +translate$E $** > $@ |
| 966 | 966 | |
| 967 | -$(OBJDIR)\webmail$O : webmail_.c webmail.h | |
| 968 | - $(TCC) -o$@ -c webmail_.c | |
| 969 | - | |
| 970 | -webmail_.c : $(SRCDIR)\webmail.c | |
| 971 | - +translate$E $** > $@ | |
| 972 | - | |
| 973 | 967 | $(OBJDIR)\wiki$O : wiki_.c wiki.h |
| 974 | 968 | $(TCC) -o$@ -c wiki_.c |
| 975 | 969 | |
| 976 | 970 | wiki_.c : $(SRCDIR)\wiki.c |
| 977 | 971 | +translate$E $** > $@ |
| @@ -1011,7 +1005,7 @@ | ||
| 1011 | 1005 | |
| 1012 | 1006 | zip_.c : $(SRCDIR)\zip.c |
| 1013 | 1007 | +translate$E $** > $@ |
| 1014 | 1008 | |
| 1015 | 1009 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 1016 | - +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 1010 | + +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 1017 | 1011 | @copy /Y nul: headers |
| 1018 | 1012 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | |
| 962 | $(TCC) -o$@ -c vfile_.c |
| 963 | |
| 964 | vfile_.c : $(SRCDIR)\vfile.c |
| 965 | +translate$E $** > $@ |
| 966 | |
| 967 | $(OBJDIR)\webmail$O : webmail_.c webmail.h |
| 968 | $(TCC) -o$@ -c webmail_.c |
| 969 | |
| 970 | webmail_.c : $(SRCDIR)\webmail.c |
| 971 | +translate$E $** > $@ |
| 972 | |
| 973 | $(OBJDIR)\wiki$O : wiki_.c wiki.h |
| 974 | $(TCC) -o$@ -c wiki_.c |
| 975 | |
| 976 | wiki_.c : $(SRCDIR)\wiki.c |
| 977 | +translate$E $** > $@ |
| @@ -1011,7 +1005,7 @@ | |
| 1011 | |
| 1012 | zip_.c : $(SRCDIR)\zip.c |
| 1013 | +translate$E $** > $@ |
| 1014 | |
| 1015 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 1016 | +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 1017 | @copy /Y nul: headers |
| 1018 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | |
| 962 | $(TCC) -o$@ -c vfile_.c |
| 963 | |
| 964 | vfile_.c : $(SRCDIR)\vfile.c |
| 965 | +translate$E $** > $@ |
| 966 | |
| 967 | $(OBJDIR)\wiki$O : wiki_.c wiki.h |
| 968 | $(TCC) -o$@ -c wiki_.c |
| 969 | |
| 970 | wiki_.c : $(SRCDIR)\wiki.c |
| 971 | +translate$E $** > $@ |
| @@ -1011,7 +1005,7 @@ | |
| 1005 | |
| 1006 | zip_.c : $(SRCDIR)\zip.c |
| 1007 | +translate$E $** > $@ |
| 1008 | |
| 1009 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 1010 | +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 1011 | @copy /Y nul: headers |
| 1012 |
-12
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -563,11 +563,10 @@ | ||
| 563 | 563 | $(SRCDIR)/user.c \ |
| 564 | 564 | $(SRCDIR)/utf8.c \ |
| 565 | 565 | $(SRCDIR)/util.c \ |
| 566 | 566 | $(SRCDIR)/verify.c \ |
| 567 | 567 | $(SRCDIR)/vfile.c \ |
| 568 | - $(SRCDIR)/webmail.c \ | |
| 569 | 568 | $(SRCDIR)/wiki.c \ |
| 570 | 569 | $(SRCDIR)/wikiformat.c \ |
| 571 | 570 | $(SRCDIR)/winfile.c \ |
| 572 | 571 | $(SRCDIR)/winhttp.c \ |
| 573 | 572 | $(SRCDIR)/xfer.c \ |
| @@ -819,11 +818,10 @@ | ||
| 819 | 818 | $(OBJDIR)/user_.c \ |
| 820 | 819 | $(OBJDIR)/utf8_.c \ |
| 821 | 820 | $(OBJDIR)/util_.c \ |
| 822 | 821 | $(OBJDIR)/verify_.c \ |
| 823 | 822 | $(OBJDIR)/vfile_.c \ |
| 824 | - $(OBJDIR)/webmail_.c \ | |
| 825 | 823 | $(OBJDIR)/wiki_.c \ |
| 826 | 824 | $(OBJDIR)/wikiformat_.c \ |
| 827 | 825 | $(OBJDIR)/winfile_.c \ |
| 828 | 826 | $(OBJDIR)/winhttp_.c \ |
| 829 | 827 | $(OBJDIR)/xfer_.c \ |
| @@ -969,11 +967,10 @@ | ||
| 969 | 967 | $(OBJDIR)/user.o \ |
| 970 | 968 | $(OBJDIR)/utf8.o \ |
| 971 | 969 | $(OBJDIR)/util.o \ |
| 972 | 970 | $(OBJDIR)/verify.o \ |
| 973 | 971 | $(OBJDIR)/vfile.o \ |
| 974 | - $(OBJDIR)/webmail.o \ | |
| 975 | 972 | $(OBJDIR)/wiki.o \ |
| 976 | 973 | $(OBJDIR)/wikiformat.o \ |
| 977 | 974 | $(OBJDIR)/winfile.o \ |
| 978 | 975 | $(OBJDIR)/winhttp.o \ |
| 979 | 976 | $(OBJDIR)/xfer.o \ |
| @@ -1334,11 +1331,10 @@ | ||
| 1334 | 1331 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1335 | 1332 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1336 | 1333 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1337 | 1334 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1338 | 1335 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1339 | - $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ | |
| 1340 | 1336 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1341 | 1337 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1342 | 1338 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1343 | 1339 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1344 | 1340 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2471,18 +2467,10 @@ | ||
| 2471 | 2467 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2472 | 2468 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2473 | 2469 | |
| 2474 | 2470 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2475 | 2471 | |
| 2476 | -$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) | |
| 2477 | - $(TRANSLATE) $(SRCDIR)/webmail.c >$@ | |
| 2478 | - | |
| 2479 | -$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h | |
| 2480 | - $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c | |
| 2481 | - | |
| 2482 | -$(OBJDIR)/webmail.h: $(OBJDIR)/headers | |
| 2483 | - | |
| 2484 | 2472 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2485 | 2473 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2486 | 2474 | |
| 2487 | 2475 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2488 | 2476 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2489 | 2477 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -563,11 +563,10 @@ | |
| 563 | $(SRCDIR)/user.c \ |
| 564 | $(SRCDIR)/utf8.c \ |
| 565 | $(SRCDIR)/util.c \ |
| 566 | $(SRCDIR)/verify.c \ |
| 567 | $(SRCDIR)/vfile.c \ |
| 568 | $(SRCDIR)/webmail.c \ |
| 569 | $(SRCDIR)/wiki.c \ |
| 570 | $(SRCDIR)/wikiformat.c \ |
| 571 | $(SRCDIR)/winfile.c \ |
| 572 | $(SRCDIR)/winhttp.c \ |
| 573 | $(SRCDIR)/xfer.c \ |
| @@ -819,11 +818,10 @@ | |
| 819 | $(OBJDIR)/user_.c \ |
| 820 | $(OBJDIR)/utf8_.c \ |
| 821 | $(OBJDIR)/util_.c \ |
| 822 | $(OBJDIR)/verify_.c \ |
| 823 | $(OBJDIR)/vfile_.c \ |
| 824 | $(OBJDIR)/webmail_.c \ |
| 825 | $(OBJDIR)/wiki_.c \ |
| 826 | $(OBJDIR)/wikiformat_.c \ |
| 827 | $(OBJDIR)/winfile_.c \ |
| 828 | $(OBJDIR)/winhttp_.c \ |
| 829 | $(OBJDIR)/xfer_.c \ |
| @@ -969,11 +967,10 @@ | |
| 969 | $(OBJDIR)/user.o \ |
| 970 | $(OBJDIR)/utf8.o \ |
| 971 | $(OBJDIR)/util.o \ |
| 972 | $(OBJDIR)/verify.o \ |
| 973 | $(OBJDIR)/vfile.o \ |
| 974 | $(OBJDIR)/webmail.o \ |
| 975 | $(OBJDIR)/wiki.o \ |
| 976 | $(OBJDIR)/wikiformat.o \ |
| 977 | $(OBJDIR)/winfile.o \ |
| 978 | $(OBJDIR)/winhttp.o \ |
| 979 | $(OBJDIR)/xfer.o \ |
| @@ -1334,11 +1331,10 @@ | |
| 1334 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1335 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1336 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1337 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1338 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1339 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 1340 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1341 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1342 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1343 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1344 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2471,18 +2467,10 @@ | |
| 2471 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2472 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2473 | |
| 2474 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2475 | |
| 2476 | $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) |
| 2477 | $(TRANSLATE) $(SRCDIR)/webmail.c >$@ |
| 2478 | |
| 2479 | $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h |
| 2480 | $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c |
| 2481 | |
| 2482 | $(OBJDIR)/webmail.h: $(OBJDIR)/headers |
| 2483 | |
| 2484 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2485 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2486 | |
| 2487 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2488 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2489 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -563,11 +563,10 @@ | |
| 563 | $(SRCDIR)/user.c \ |
| 564 | $(SRCDIR)/utf8.c \ |
| 565 | $(SRCDIR)/util.c \ |
| 566 | $(SRCDIR)/verify.c \ |
| 567 | $(SRCDIR)/vfile.c \ |
| 568 | $(SRCDIR)/wiki.c \ |
| 569 | $(SRCDIR)/wikiformat.c \ |
| 570 | $(SRCDIR)/winfile.c \ |
| 571 | $(SRCDIR)/winhttp.c \ |
| 572 | $(SRCDIR)/xfer.c \ |
| @@ -819,11 +818,10 @@ | |
| 818 | $(OBJDIR)/user_.c \ |
| 819 | $(OBJDIR)/utf8_.c \ |
| 820 | $(OBJDIR)/util_.c \ |
| 821 | $(OBJDIR)/verify_.c \ |
| 822 | $(OBJDIR)/vfile_.c \ |
| 823 | $(OBJDIR)/wiki_.c \ |
| 824 | $(OBJDIR)/wikiformat_.c \ |
| 825 | $(OBJDIR)/winfile_.c \ |
| 826 | $(OBJDIR)/winhttp_.c \ |
| 827 | $(OBJDIR)/xfer_.c \ |
| @@ -969,11 +967,10 @@ | |
| 967 | $(OBJDIR)/user.o \ |
| 968 | $(OBJDIR)/utf8.o \ |
| 969 | $(OBJDIR)/util.o \ |
| 970 | $(OBJDIR)/verify.o \ |
| 971 | $(OBJDIR)/vfile.o \ |
| 972 | $(OBJDIR)/wiki.o \ |
| 973 | $(OBJDIR)/wikiformat.o \ |
| 974 | $(OBJDIR)/winfile.o \ |
| 975 | $(OBJDIR)/winhttp.o \ |
| 976 | $(OBJDIR)/xfer.o \ |
| @@ -1334,11 +1331,10 @@ | |
| 1331 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1332 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1333 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1334 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1335 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1336 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1337 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1338 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1339 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1340 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -2471,18 +2467,10 @@ | |
| 2467 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2468 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2469 | |
| 2470 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2471 | |
| 2472 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2473 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2474 | |
| 2475 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2476 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2477 |
+38
-33
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -166,15 +166,12 @@ | ||
| 166 | 166 | ifndef FOSSIL_ENABLE_MINIZ |
| 167 | 167 | SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib |
| 168 | 168 | endif |
| 169 | 169 | |
| 170 | 170 | #### The directories where the OpenSSL include and library files are located. |
| 171 | -# The recommended usage here is to use the Sysinternals junction tool | |
| 172 | -# to create a hard link between an "openssl-1.x" sub-directory of the | |
| 173 | -# Fossil source code directory and the target OpenSSL source directory. | |
| 174 | 171 | # |
| 175 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g | |
| 172 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl | |
| 176 | 173 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 177 | 174 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 178 | 175 | |
| 179 | 176 | #### Either the directory where the Tcl library is installed or the Tcl |
| 180 | 177 | # source code directory resides (depending on the value of the macro |
| @@ -444,14 +441,16 @@ | ||
| 444 | 441 | $(SRCDIR)/bundle.c \ |
| 445 | 442 | $(SRCDIR)/cache.c \ |
| 446 | 443 | $(SRCDIR)/capabilities.c \ |
| 447 | 444 | $(SRCDIR)/captcha.c \ |
| 448 | 445 | $(SRCDIR)/cgi.c \ |
| 446 | + $(SRCDIR)/chat.c \ | |
| 449 | 447 | $(SRCDIR)/checkin.c \ |
| 450 | 448 | $(SRCDIR)/checkout.c \ |
| 451 | 449 | $(SRCDIR)/clearsign.c \ |
| 452 | 450 | $(SRCDIR)/clone.c \ |
| 451 | + $(SRCDIR)/color.c \ | |
| 453 | 452 | $(SRCDIR)/comformat.c \ |
| 454 | 453 | $(SRCDIR)/configure.c \ |
| 455 | 454 | $(SRCDIR)/content.c \ |
| 456 | 455 | $(SRCDIR)/cookies.c \ |
| 457 | 456 | $(SRCDIR)/db.c \ |
| @@ -564,21 +563,19 @@ | ||
| 564 | 563 | $(SRCDIR)/user.c \ |
| 565 | 564 | $(SRCDIR)/utf8.c \ |
| 566 | 565 | $(SRCDIR)/util.c \ |
| 567 | 566 | $(SRCDIR)/verify.c \ |
| 568 | 567 | $(SRCDIR)/vfile.c \ |
| 569 | - $(SRCDIR)/webmail.c \ | |
| 570 | 568 | $(SRCDIR)/wiki.c \ |
| 571 | 569 | $(SRCDIR)/wikiformat.c \ |
| 572 | 570 | $(SRCDIR)/winfile.c \ |
| 573 | 571 | $(SRCDIR)/winhttp.c \ |
| 574 | 572 | $(SRCDIR)/xfer.c \ |
| 575 | 573 | $(SRCDIR)/xfersetup.c \ |
| 576 | 574 | $(SRCDIR)/zip.c |
| 577 | 575 | |
| 578 | 576 | EXTRA_FILES = \ |
| 579 | - $(SRCDIR)/../skins/aht/details.txt \ | |
| 580 | 577 | $(SRCDIR)/../skins/ardoise/css.txt \ |
| 581 | 578 | $(SRCDIR)/../skins/ardoise/details.txt \ |
| 582 | 579 | $(SRCDIR)/../skins/ardoise/footer.txt \ |
| 583 | 580 | $(SRCDIR)/../skins/ardoise/header.txt \ |
| 584 | 581 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| @@ -588,32 +585,26 @@ | ||
| 588 | 585 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 589 | 586 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 590 | 587 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 591 | 588 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 592 | 589 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 593 | - $(SRCDIR)/../skins/blitz_no_logo/css.txt \ | |
| 594 | - $(SRCDIR)/../skins/blitz_no_logo/details.txt \ | |
| 595 | - $(SRCDIR)/../skins/blitz_no_logo/footer.txt \ | |
| 596 | - $(SRCDIR)/../skins/blitz_no_logo/header.txt \ | |
| 597 | - $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \ | |
| 598 | 590 | $(SRCDIR)/../skins/bootstrap/css.txt \ |
| 599 | 591 | $(SRCDIR)/../skins/bootstrap/details.txt \ |
| 600 | 592 | $(SRCDIR)/../skins/bootstrap/footer.txt \ |
| 601 | 593 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 594 | + $(SRCDIR)/../skins/darkmode/css.txt \ | |
| 595 | + $(SRCDIR)/../skins/darkmode/details.txt \ | |
| 596 | + $(SRCDIR)/../skins/darkmode/footer.txt \ | |
| 597 | + $(SRCDIR)/../skins/darkmode/header.txt \ | |
| 602 | 598 | $(SRCDIR)/../skins/default/css.txt \ |
| 603 | 599 | $(SRCDIR)/../skins/default/details.txt \ |
| 604 | 600 | $(SRCDIR)/../skins/default/footer.txt \ |
| 605 | 601 | $(SRCDIR)/../skins/default/header.txt \ |
| 606 | - $(SRCDIR)/../skins/default/js.txt \ | |
| 607 | 602 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 608 | 603 | $(SRCDIR)/../skins/eagle/details.txt \ |
| 609 | 604 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 610 | 605 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 611 | - $(SRCDIR)/../skins/enhanced1/css.txt \ | |
| 612 | - $(SRCDIR)/../skins/enhanced1/details.txt \ | |
| 613 | - $(SRCDIR)/../skins/enhanced1/footer.txt \ | |
| 614 | - $(SRCDIR)/../skins/enhanced1/header.txt \ | |
| 615 | 606 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 616 | 607 | $(SRCDIR)/../skins/khaki/details.txt \ |
| 617 | 608 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 618 | 609 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 619 | 610 | $(SRCDIR)/../skins/original/css.txt \ |
| @@ -622,19 +613,20 @@ | ||
| 622 | 613 | $(SRCDIR)/../skins/original/header.txt \ |
| 623 | 614 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 624 | 615 | $(SRCDIR)/../skins/plain_gray/details.txt \ |
| 625 | 616 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 626 | 617 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 627 | - $(SRCDIR)/../skins/rounded1/css.txt \ | |
| 628 | - $(SRCDIR)/../skins/rounded1/details.txt \ | |
| 629 | - $(SRCDIR)/../skins/rounded1/footer.txt \ | |
| 630 | - $(SRCDIR)/../skins/rounded1/header.txt \ | |
| 631 | 618 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 632 | 619 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 633 | 620 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 634 | 621 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 635 | 622 | $(SRCDIR)/accordion.js \ |
| 623 | + $(SRCDIR)/alerts/bflat2.wav \ | |
| 624 | + $(SRCDIR)/alerts/bflat3.wav \ | |
| 625 | + $(SRCDIR)/alerts/bloop.wav \ | |
| 626 | + $(SRCDIR)/alerts/plunk.wav \ | |
| 627 | + $(SRCDIR)/chat.js \ | |
| 636 | 628 | $(SRCDIR)/ci_edit.js \ |
| 637 | 629 | $(SRCDIR)/copybtn.js \ |
| 638 | 630 | $(SRCDIR)/default.css \ |
| 639 | 631 | $(SRCDIR)/diff.tcl \ |
| 640 | 632 | $(SRCDIR)/forum.js \ |
| @@ -641,10 +633,11 @@ | ||
| 641 | 633 | $(SRCDIR)/fossil.bootstrap.js \ |
| 642 | 634 | $(SRCDIR)/fossil.confirmer.js \ |
| 643 | 635 | $(SRCDIR)/fossil.copybutton.js \ |
| 644 | 636 | $(SRCDIR)/fossil.dom.js \ |
| 645 | 637 | $(SRCDIR)/fossil.fetch.js \ |
| 638 | + $(SRCDIR)/fossil.info-diff.js \ | |
| 646 | 639 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 647 | 640 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 648 | 641 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 649 | 642 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 650 | 643 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| @@ -652,10 +645,11 @@ | ||
| 652 | 645 | $(SRCDIR)/fossil.popupwidget.js \ |
| 653 | 646 | $(SRCDIR)/fossil.storage.js \ |
| 654 | 647 | $(SRCDIR)/fossil.tabs.js \ |
| 655 | 648 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| 656 | 649 | $(SRCDIR)/graph.js \ |
| 650 | + $(SRCDIR)/hbmenu.js \ | |
| 657 | 651 | $(SRCDIR)/href.js \ |
| 658 | 652 | $(SRCDIR)/login.js \ |
| 659 | 653 | $(SRCDIR)/markdown.md \ |
| 660 | 654 | $(SRCDIR)/menu.js \ |
| 661 | 655 | $(SRCDIR)/sbsdiff.js \ |
| @@ -702,14 +696,16 @@ | ||
| 702 | 696 | $(OBJDIR)/bundle_.c \ |
| 703 | 697 | $(OBJDIR)/cache_.c \ |
| 704 | 698 | $(OBJDIR)/capabilities_.c \ |
| 705 | 699 | $(OBJDIR)/captcha_.c \ |
| 706 | 700 | $(OBJDIR)/cgi_.c \ |
| 701 | + $(OBJDIR)/chat_.c \ | |
| 707 | 702 | $(OBJDIR)/checkin_.c \ |
| 708 | 703 | $(OBJDIR)/checkout_.c \ |
| 709 | 704 | $(OBJDIR)/clearsign_.c \ |
| 710 | 705 | $(OBJDIR)/clone_.c \ |
| 706 | + $(OBJDIR)/color_.c \ | |
| 711 | 707 | $(OBJDIR)/comformat_.c \ |
| 712 | 708 | $(OBJDIR)/configure_.c \ |
| 713 | 709 | $(OBJDIR)/content_.c \ |
| 714 | 710 | $(OBJDIR)/cookies_.c \ |
| 715 | 711 | $(OBJDIR)/db_.c \ |
| @@ -822,11 +818,10 @@ | ||
| 822 | 818 | $(OBJDIR)/user_.c \ |
| 823 | 819 | $(OBJDIR)/utf8_.c \ |
| 824 | 820 | $(OBJDIR)/util_.c \ |
| 825 | 821 | $(OBJDIR)/verify_.c \ |
| 826 | 822 | $(OBJDIR)/vfile_.c \ |
| 827 | - $(OBJDIR)/webmail_.c \ | |
| 828 | 823 | $(OBJDIR)/wiki_.c \ |
| 829 | 824 | $(OBJDIR)/wikiformat_.c \ |
| 830 | 825 | $(OBJDIR)/winfile_.c \ |
| 831 | 826 | $(OBJDIR)/winhttp_.c \ |
| 832 | 827 | $(OBJDIR)/xfer_.c \ |
| @@ -850,14 +845,16 @@ | ||
| 850 | 845 | $(OBJDIR)/bundle.o \ |
| 851 | 846 | $(OBJDIR)/cache.o \ |
| 852 | 847 | $(OBJDIR)/capabilities.o \ |
| 853 | 848 | $(OBJDIR)/captcha.o \ |
| 854 | 849 | $(OBJDIR)/cgi.o \ |
| 850 | + $(OBJDIR)/chat.o \ | |
| 855 | 851 | $(OBJDIR)/checkin.o \ |
| 856 | 852 | $(OBJDIR)/checkout.o \ |
| 857 | 853 | $(OBJDIR)/clearsign.o \ |
| 858 | 854 | $(OBJDIR)/clone.o \ |
| 855 | + $(OBJDIR)/color.o \ | |
| 859 | 856 | $(OBJDIR)/comformat.o \ |
| 860 | 857 | $(OBJDIR)/configure.o \ |
| 861 | 858 | $(OBJDIR)/content.o \ |
| 862 | 859 | $(OBJDIR)/cookies.o \ |
| 863 | 860 | $(OBJDIR)/db.o \ |
| @@ -970,11 +967,10 @@ | ||
| 970 | 967 | $(OBJDIR)/user.o \ |
| 971 | 968 | $(OBJDIR)/utf8.o \ |
| 972 | 969 | $(OBJDIR)/util.o \ |
| 973 | 970 | $(OBJDIR)/verify.o \ |
| 974 | 971 | $(OBJDIR)/vfile.o \ |
| 975 | - $(OBJDIR)/webmail.o \ | |
| 976 | 972 | $(OBJDIR)/wiki.o \ |
| 977 | 973 | $(OBJDIR)/wikiformat.o \ |
| 978 | 974 | $(OBJDIR)/winfile.o \ |
| 979 | 975 | $(OBJDIR)/winhttp.o \ |
| 980 | 976 | $(OBJDIR)/xfer.o \ |
| @@ -1163,13 +1159,13 @@ | ||
| 1163 | 1159 | |
| 1164 | 1160 | ifdef FOSSIL_BUILD_SSL |
| 1165 | 1161 | APPTARGETS += openssl |
| 1166 | 1162 | endif |
| 1167 | 1163 | |
| 1168 | -$(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o | |
| 1164 | +$(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o | |
| 1169 | 1165 | $(CODECHECK1) $(TRANS_SRC) |
| 1170 | - $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB) | |
| 1166 | + $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB) | |
| 1171 | 1167 | |
| 1172 | 1168 | # This rule prevents make from using its default rules to try build |
| 1173 | 1169 | # an executable named "manifest" out of the file named "manifest.c" |
| 1174 | 1170 | # |
| 1175 | 1171 | $(SRCDIR)/../manifest: |
| @@ -1213,14 +1209,16 @@ | ||
| 1213 | 1209 | $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ |
| 1214 | 1210 | $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ |
| 1215 | 1211 | $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ |
| 1216 | 1212 | $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ |
| 1217 | 1213 | $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ |
| 1214 | + $(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \ | |
| 1218 | 1215 | $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ |
| 1219 | 1216 | $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ |
| 1220 | 1217 | $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ |
| 1221 | 1218 | $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ |
| 1219 | + $(OBJDIR)/color_.c:$(OBJDIR)/color.h \ | |
| 1222 | 1220 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ |
| 1223 | 1221 | $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ |
| 1224 | 1222 | $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ |
| 1225 | 1223 | $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ |
| 1226 | 1224 | $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ |
| @@ -1333,11 +1331,10 @@ | ||
| 1333 | 1331 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1334 | 1332 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1335 | 1333 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1336 | 1334 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1337 | 1335 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1338 | - $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ | |
| 1339 | 1336 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1340 | 1337 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1341 | 1338 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1342 | 1339 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1343 | 1340 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -1493,10 +1490,18 @@ | ||
| 1493 | 1490 | |
| 1494 | 1491 | $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h |
| 1495 | 1492 | $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c |
| 1496 | 1493 | |
| 1497 | 1494 | $(OBJDIR)/cgi.h: $(OBJDIR)/headers |
| 1495 | + | |
| 1496 | +$(OBJDIR)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE) | |
| 1497 | + $(TRANSLATE) $(SRCDIR)/chat.c >$@ | |
| 1498 | + | |
| 1499 | +$(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h | |
| 1500 | + $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c | |
| 1501 | + | |
| 1502 | +$(OBJDIR)/chat.h: $(OBJDIR)/headers | |
| 1498 | 1503 | |
| 1499 | 1504 | $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE) |
| 1500 | 1505 | $(TRANSLATE) $(SRCDIR)/checkin.c >$@ |
| 1501 | 1506 | |
| 1502 | 1507 | $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h |
| @@ -1525,10 +1530,18 @@ | ||
| 1525 | 1530 | |
| 1526 | 1531 | $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h |
| 1527 | 1532 | $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c |
| 1528 | 1533 | |
| 1529 | 1534 | $(OBJDIR)/clone.h: $(OBJDIR)/headers |
| 1535 | + | |
| 1536 | +$(OBJDIR)/color_.c: $(SRCDIR)/color.c $(TRANSLATE) | |
| 1537 | + $(TRANSLATE) $(SRCDIR)/color.c >$@ | |
| 1538 | + | |
| 1539 | +$(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h | |
| 1540 | + $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c | |
| 1541 | + | |
| 1542 | +$(OBJDIR)/color.h: $(OBJDIR)/headers | |
| 1530 | 1543 | |
| 1531 | 1544 | $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE) |
| 1532 | 1545 | $(TRANSLATE) $(SRCDIR)/comformat.c >$@ |
| 1533 | 1546 | |
| 1534 | 1547 | $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h |
| @@ -2454,18 +2467,10 @@ | ||
| 2454 | 2467 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2455 | 2468 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2456 | 2469 | |
| 2457 | 2470 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2458 | 2471 | |
| 2459 | -$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) | |
| 2460 | - $(TRANSLATE) $(SRCDIR)/webmail.c >$@ | |
| 2461 | - | |
| 2462 | -$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h | |
| 2463 | - $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c | |
| 2464 | - | |
| 2465 | -$(OBJDIR)/webmail.h: $(OBJDIR)/headers | |
| 2466 | - | |
| 2467 | 2472 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2468 | 2473 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2469 | 2474 | |
| 2470 | 2475 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2471 | 2476 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2472 | 2477 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -166,15 +166,12 @@ | |
| 166 | ifndef FOSSIL_ENABLE_MINIZ |
| 167 | SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib |
| 168 | endif |
| 169 | |
| 170 | #### The directories where the OpenSSL include and library files are located. |
| 171 | # The recommended usage here is to use the Sysinternals junction tool |
| 172 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 173 | # Fossil source code directory and the target OpenSSL source directory. |
| 174 | # |
| 175 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g |
| 176 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 177 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 178 | |
| 179 | #### Either the directory where the Tcl library is installed or the Tcl |
| 180 | # source code directory resides (depending on the value of the macro |
| @@ -444,14 +441,16 @@ | |
| 444 | $(SRCDIR)/bundle.c \ |
| 445 | $(SRCDIR)/cache.c \ |
| 446 | $(SRCDIR)/capabilities.c \ |
| 447 | $(SRCDIR)/captcha.c \ |
| 448 | $(SRCDIR)/cgi.c \ |
| 449 | $(SRCDIR)/checkin.c \ |
| 450 | $(SRCDIR)/checkout.c \ |
| 451 | $(SRCDIR)/clearsign.c \ |
| 452 | $(SRCDIR)/clone.c \ |
| 453 | $(SRCDIR)/comformat.c \ |
| 454 | $(SRCDIR)/configure.c \ |
| 455 | $(SRCDIR)/content.c \ |
| 456 | $(SRCDIR)/cookies.c \ |
| 457 | $(SRCDIR)/db.c \ |
| @@ -564,21 +563,19 @@ | |
| 564 | $(SRCDIR)/user.c \ |
| 565 | $(SRCDIR)/utf8.c \ |
| 566 | $(SRCDIR)/util.c \ |
| 567 | $(SRCDIR)/verify.c \ |
| 568 | $(SRCDIR)/vfile.c \ |
| 569 | $(SRCDIR)/webmail.c \ |
| 570 | $(SRCDIR)/wiki.c \ |
| 571 | $(SRCDIR)/wikiformat.c \ |
| 572 | $(SRCDIR)/winfile.c \ |
| 573 | $(SRCDIR)/winhttp.c \ |
| 574 | $(SRCDIR)/xfer.c \ |
| 575 | $(SRCDIR)/xfersetup.c \ |
| 576 | $(SRCDIR)/zip.c |
| 577 | |
| 578 | EXTRA_FILES = \ |
| 579 | $(SRCDIR)/../skins/aht/details.txt \ |
| 580 | $(SRCDIR)/../skins/ardoise/css.txt \ |
| 581 | $(SRCDIR)/../skins/ardoise/details.txt \ |
| 582 | $(SRCDIR)/../skins/ardoise/footer.txt \ |
| 583 | $(SRCDIR)/../skins/ardoise/header.txt \ |
| 584 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| @@ -588,32 +585,26 @@ | |
| 588 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 589 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 590 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 591 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 592 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 593 | $(SRCDIR)/../skins/blitz_no_logo/css.txt \ |
| 594 | $(SRCDIR)/../skins/blitz_no_logo/details.txt \ |
| 595 | $(SRCDIR)/../skins/blitz_no_logo/footer.txt \ |
| 596 | $(SRCDIR)/../skins/blitz_no_logo/header.txt \ |
| 597 | $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \ |
| 598 | $(SRCDIR)/../skins/bootstrap/css.txt \ |
| 599 | $(SRCDIR)/../skins/bootstrap/details.txt \ |
| 600 | $(SRCDIR)/../skins/bootstrap/footer.txt \ |
| 601 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 602 | $(SRCDIR)/../skins/default/css.txt \ |
| 603 | $(SRCDIR)/../skins/default/details.txt \ |
| 604 | $(SRCDIR)/../skins/default/footer.txt \ |
| 605 | $(SRCDIR)/../skins/default/header.txt \ |
| 606 | $(SRCDIR)/../skins/default/js.txt \ |
| 607 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 608 | $(SRCDIR)/../skins/eagle/details.txt \ |
| 609 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 610 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 611 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| 612 | $(SRCDIR)/../skins/enhanced1/details.txt \ |
| 613 | $(SRCDIR)/../skins/enhanced1/footer.txt \ |
| 614 | $(SRCDIR)/../skins/enhanced1/header.txt \ |
| 615 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 616 | $(SRCDIR)/../skins/khaki/details.txt \ |
| 617 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 618 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 619 | $(SRCDIR)/../skins/original/css.txt \ |
| @@ -622,19 +613,20 @@ | |
| 622 | $(SRCDIR)/../skins/original/header.txt \ |
| 623 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 624 | $(SRCDIR)/../skins/plain_gray/details.txt \ |
| 625 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 626 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 627 | $(SRCDIR)/../skins/rounded1/css.txt \ |
| 628 | $(SRCDIR)/../skins/rounded1/details.txt \ |
| 629 | $(SRCDIR)/../skins/rounded1/footer.txt \ |
| 630 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 631 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 632 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 633 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 634 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 635 | $(SRCDIR)/accordion.js \ |
| 636 | $(SRCDIR)/ci_edit.js \ |
| 637 | $(SRCDIR)/copybtn.js \ |
| 638 | $(SRCDIR)/default.css \ |
| 639 | $(SRCDIR)/diff.tcl \ |
| 640 | $(SRCDIR)/forum.js \ |
| @@ -641,10 +633,11 @@ | |
| 641 | $(SRCDIR)/fossil.bootstrap.js \ |
| 642 | $(SRCDIR)/fossil.confirmer.js \ |
| 643 | $(SRCDIR)/fossil.copybutton.js \ |
| 644 | $(SRCDIR)/fossil.dom.js \ |
| 645 | $(SRCDIR)/fossil.fetch.js \ |
| 646 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 647 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 648 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 649 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 650 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| @@ -652,10 +645,11 @@ | |
| 652 | $(SRCDIR)/fossil.popupwidget.js \ |
| 653 | $(SRCDIR)/fossil.storage.js \ |
| 654 | $(SRCDIR)/fossil.tabs.js \ |
| 655 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| 656 | $(SRCDIR)/graph.js \ |
| 657 | $(SRCDIR)/href.js \ |
| 658 | $(SRCDIR)/login.js \ |
| 659 | $(SRCDIR)/markdown.md \ |
| 660 | $(SRCDIR)/menu.js \ |
| 661 | $(SRCDIR)/sbsdiff.js \ |
| @@ -702,14 +696,16 @@ | |
| 702 | $(OBJDIR)/bundle_.c \ |
| 703 | $(OBJDIR)/cache_.c \ |
| 704 | $(OBJDIR)/capabilities_.c \ |
| 705 | $(OBJDIR)/captcha_.c \ |
| 706 | $(OBJDIR)/cgi_.c \ |
| 707 | $(OBJDIR)/checkin_.c \ |
| 708 | $(OBJDIR)/checkout_.c \ |
| 709 | $(OBJDIR)/clearsign_.c \ |
| 710 | $(OBJDIR)/clone_.c \ |
| 711 | $(OBJDIR)/comformat_.c \ |
| 712 | $(OBJDIR)/configure_.c \ |
| 713 | $(OBJDIR)/content_.c \ |
| 714 | $(OBJDIR)/cookies_.c \ |
| 715 | $(OBJDIR)/db_.c \ |
| @@ -822,11 +818,10 @@ | |
| 822 | $(OBJDIR)/user_.c \ |
| 823 | $(OBJDIR)/utf8_.c \ |
| 824 | $(OBJDIR)/util_.c \ |
| 825 | $(OBJDIR)/verify_.c \ |
| 826 | $(OBJDIR)/vfile_.c \ |
| 827 | $(OBJDIR)/webmail_.c \ |
| 828 | $(OBJDIR)/wiki_.c \ |
| 829 | $(OBJDIR)/wikiformat_.c \ |
| 830 | $(OBJDIR)/winfile_.c \ |
| 831 | $(OBJDIR)/winhttp_.c \ |
| 832 | $(OBJDIR)/xfer_.c \ |
| @@ -850,14 +845,16 @@ | |
| 850 | $(OBJDIR)/bundle.o \ |
| 851 | $(OBJDIR)/cache.o \ |
| 852 | $(OBJDIR)/capabilities.o \ |
| 853 | $(OBJDIR)/captcha.o \ |
| 854 | $(OBJDIR)/cgi.o \ |
| 855 | $(OBJDIR)/checkin.o \ |
| 856 | $(OBJDIR)/checkout.o \ |
| 857 | $(OBJDIR)/clearsign.o \ |
| 858 | $(OBJDIR)/clone.o \ |
| 859 | $(OBJDIR)/comformat.o \ |
| 860 | $(OBJDIR)/configure.o \ |
| 861 | $(OBJDIR)/content.o \ |
| 862 | $(OBJDIR)/cookies.o \ |
| 863 | $(OBJDIR)/db.o \ |
| @@ -970,11 +967,10 @@ | |
| 970 | $(OBJDIR)/user.o \ |
| 971 | $(OBJDIR)/utf8.o \ |
| 972 | $(OBJDIR)/util.o \ |
| 973 | $(OBJDIR)/verify.o \ |
| 974 | $(OBJDIR)/vfile.o \ |
| 975 | $(OBJDIR)/webmail.o \ |
| 976 | $(OBJDIR)/wiki.o \ |
| 977 | $(OBJDIR)/wikiformat.o \ |
| 978 | $(OBJDIR)/winfile.o \ |
| 979 | $(OBJDIR)/winhttp.o \ |
| 980 | $(OBJDIR)/xfer.o \ |
| @@ -1163,13 +1159,13 @@ | |
| 1163 | |
| 1164 | ifdef FOSSIL_BUILD_SSL |
| 1165 | APPTARGETS += openssl |
| 1166 | endif |
| 1167 | |
| 1168 | $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o |
| 1169 | $(CODECHECK1) $(TRANS_SRC) |
| 1170 | $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB) |
| 1171 | |
| 1172 | # This rule prevents make from using its default rules to try build |
| 1173 | # an executable named "manifest" out of the file named "manifest.c" |
| 1174 | # |
| 1175 | $(SRCDIR)/../manifest: |
| @@ -1213,14 +1209,16 @@ | |
| 1213 | $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ |
| 1214 | $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ |
| 1215 | $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ |
| 1216 | $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ |
| 1217 | $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ |
| 1218 | $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ |
| 1219 | $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ |
| 1220 | $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ |
| 1221 | $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ |
| 1222 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ |
| 1223 | $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ |
| 1224 | $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ |
| 1225 | $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ |
| 1226 | $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ |
| @@ -1333,11 +1331,10 @@ | |
| 1333 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1334 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1335 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1336 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1337 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1338 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 1339 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1340 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1341 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1342 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1343 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -1493,10 +1490,18 @@ | |
| 1493 | |
| 1494 | $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h |
| 1495 | $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c |
| 1496 | |
| 1497 | $(OBJDIR)/cgi.h: $(OBJDIR)/headers |
| 1498 | |
| 1499 | $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE) |
| 1500 | $(TRANSLATE) $(SRCDIR)/checkin.c >$@ |
| 1501 | |
| 1502 | $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h |
| @@ -1525,10 +1530,18 @@ | |
| 1525 | |
| 1526 | $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h |
| 1527 | $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c |
| 1528 | |
| 1529 | $(OBJDIR)/clone.h: $(OBJDIR)/headers |
| 1530 | |
| 1531 | $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE) |
| 1532 | $(TRANSLATE) $(SRCDIR)/comformat.c >$@ |
| 1533 | |
| 1534 | $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h |
| @@ -2454,18 +2467,10 @@ | |
| 2454 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2455 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2456 | |
| 2457 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2458 | |
| 2459 | $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) |
| 2460 | $(TRANSLATE) $(SRCDIR)/webmail.c >$@ |
| 2461 | |
| 2462 | $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h |
| 2463 | $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c |
| 2464 | |
| 2465 | $(OBJDIR)/webmail.h: $(OBJDIR)/headers |
| 2466 | |
| 2467 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2468 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2469 | |
| 2470 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2471 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2472 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -166,15 +166,12 @@ | |
| 166 | ifndef FOSSIL_ENABLE_MINIZ |
| 167 | SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib |
| 168 | endif |
| 169 | |
| 170 | #### The directories where the OpenSSL include and library files are located. |
| 171 | # |
| 172 | OPENSSLDIR = $(SRCDIR)/../compat/openssl |
| 173 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 174 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 175 | |
| 176 | #### Either the directory where the Tcl library is installed or the Tcl |
| 177 | # source code directory resides (depending on the value of the macro |
| @@ -444,14 +441,16 @@ | |
| 441 | $(SRCDIR)/bundle.c \ |
| 442 | $(SRCDIR)/cache.c \ |
| 443 | $(SRCDIR)/capabilities.c \ |
| 444 | $(SRCDIR)/captcha.c \ |
| 445 | $(SRCDIR)/cgi.c \ |
| 446 | $(SRCDIR)/chat.c \ |
| 447 | $(SRCDIR)/checkin.c \ |
| 448 | $(SRCDIR)/checkout.c \ |
| 449 | $(SRCDIR)/clearsign.c \ |
| 450 | $(SRCDIR)/clone.c \ |
| 451 | $(SRCDIR)/color.c \ |
| 452 | $(SRCDIR)/comformat.c \ |
| 453 | $(SRCDIR)/configure.c \ |
| 454 | $(SRCDIR)/content.c \ |
| 455 | $(SRCDIR)/cookies.c \ |
| 456 | $(SRCDIR)/db.c \ |
| @@ -564,21 +563,19 @@ | |
| 563 | $(SRCDIR)/user.c \ |
| 564 | $(SRCDIR)/utf8.c \ |
| 565 | $(SRCDIR)/util.c \ |
| 566 | $(SRCDIR)/verify.c \ |
| 567 | $(SRCDIR)/vfile.c \ |
| 568 | $(SRCDIR)/wiki.c \ |
| 569 | $(SRCDIR)/wikiformat.c \ |
| 570 | $(SRCDIR)/winfile.c \ |
| 571 | $(SRCDIR)/winhttp.c \ |
| 572 | $(SRCDIR)/xfer.c \ |
| 573 | $(SRCDIR)/xfersetup.c \ |
| 574 | $(SRCDIR)/zip.c |
| 575 | |
| 576 | EXTRA_FILES = \ |
| 577 | $(SRCDIR)/../skins/ardoise/css.txt \ |
| 578 | $(SRCDIR)/../skins/ardoise/details.txt \ |
| 579 | $(SRCDIR)/../skins/ardoise/footer.txt \ |
| 580 | $(SRCDIR)/../skins/ardoise/header.txt \ |
| 581 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| @@ -588,32 +585,26 @@ | |
| 585 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 586 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 587 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 588 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 589 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 590 | $(SRCDIR)/../skins/bootstrap/css.txt \ |
| 591 | $(SRCDIR)/../skins/bootstrap/details.txt \ |
| 592 | $(SRCDIR)/../skins/bootstrap/footer.txt \ |
| 593 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 594 | $(SRCDIR)/../skins/darkmode/css.txt \ |
| 595 | $(SRCDIR)/../skins/darkmode/details.txt \ |
| 596 | $(SRCDIR)/../skins/darkmode/footer.txt \ |
| 597 | $(SRCDIR)/../skins/darkmode/header.txt \ |
| 598 | $(SRCDIR)/../skins/default/css.txt \ |
| 599 | $(SRCDIR)/../skins/default/details.txt \ |
| 600 | $(SRCDIR)/../skins/default/footer.txt \ |
| 601 | $(SRCDIR)/../skins/default/header.txt \ |
| 602 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 603 | $(SRCDIR)/../skins/eagle/details.txt \ |
| 604 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 605 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 606 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 607 | $(SRCDIR)/../skins/khaki/details.txt \ |
| 608 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 609 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 610 | $(SRCDIR)/../skins/original/css.txt \ |
| @@ -622,19 +613,20 @@ | |
| 613 | $(SRCDIR)/../skins/original/header.txt \ |
| 614 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 615 | $(SRCDIR)/../skins/plain_gray/details.txt \ |
| 616 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 617 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 618 | $(SRCDIR)/../skins/xekri/css.txt \ |
| 619 | $(SRCDIR)/../skins/xekri/details.txt \ |
| 620 | $(SRCDIR)/../skins/xekri/footer.txt \ |
| 621 | $(SRCDIR)/../skins/xekri/header.txt \ |
| 622 | $(SRCDIR)/accordion.js \ |
| 623 | $(SRCDIR)/alerts/bflat2.wav \ |
| 624 | $(SRCDIR)/alerts/bflat3.wav \ |
| 625 | $(SRCDIR)/alerts/bloop.wav \ |
| 626 | $(SRCDIR)/alerts/plunk.wav \ |
| 627 | $(SRCDIR)/chat.js \ |
| 628 | $(SRCDIR)/ci_edit.js \ |
| 629 | $(SRCDIR)/copybtn.js \ |
| 630 | $(SRCDIR)/default.css \ |
| 631 | $(SRCDIR)/diff.tcl \ |
| 632 | $(SRCDIR)/forum.js \ |
| @@ -641,10 +633,11 @@ | |
| 633 | $(SRCDIR)/fossil.bootstrap.js \ |
| 634 | $(SRCDIR)/fossil.confirmer.js \ |
| 635 | $(SRCDIR)/fossil.copybutton.js \ |
| 636 | $(SRCDIR)/fossil.dom.js \ |
| 637 | $(SRCDIR)/fossil.fetch.js \ |
| 638 | $(SRCDIR)/fossil.info-diff.js \ |
| 639 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 640 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 641 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 642 | $(SRCDIR)/fossil.page.pikchrshow.js \ |
| 643 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| @@ -652,10 +645,11 @@ | |
| 645 | $(SRCDIR)/fossil.popupwidget.js \ |
| 646 | $(SRCDIR)/fossil.storage.js \ |
| 647 | $(SRCDIR)/fossil.tabs.js \ |
| 648 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| 649 | $(SRCDIR)/graph.js \ |
| 650 | $(SRCDIR)/hbmenu.js \ |
| 651 | $(SRCDIR)/href.js \ |
| 652 | $(SRCDIR)/login.js \ |
| 653 | $(SRCDIR)/markdown.md \ |
| 654 | $(SRCDIR)/menu.js \ |
| 655 | $(SRCDIR)/sbsdiff.js \ |
| @@ -702,14 +696,16 @@ | |
| 696 | $(OBJDIR)/bundle_.c \ |
| 697 | $(OBJDIR)/cache_.c \ |
| 698 | $(OBJDIR)/capabilities_.c \ |
| 699 | $(OBJDIR)/captcha_.c \ |
| 700 | $(OBJDIR)/cgi_.c \ |
| 701 | $(OBJDIR)/chat_.c \ |
| 702 | $(OBJDIR)/checkin_.c \ |
| 703 | $(OBJDIR)/checkout_.c \ |
| 704 | $(OBJDIR)/clearsign_.c \ |
| 705 | $(OBJDIR)/clone_.c \ |
| 706 | $(OBJDIR)/color_.c \ |
| 707 | $(OBJDIR)/comformat_.c \ |
| 708 | $(OBJDIR)/configure_.c \ |
| 709 | $(OBJDIR)/content_.c \ |
| 710 | $(OBJDIR)/cookies_.c \ |
| 711 | $(OBJDIR)/db_.c \ |
| @@ -822,11 +818,10 @@ | |
| 818 | $(OBJDIR)/user_.c \ |
| 819 | $(OBJDIR)/utf8_.c \ |
| 820 | $(OBJDIR)/util_.c \ |
| 821 | $(OBJDIR)/verify_.c \ |
| 822 | $(OBJDIR)/vfile_.c \ |
| 823 | $(OBJDIR)/wiki_.c \ |
| 824 | $(OBJDIR)/wikiformat_.c \ |
| 825 | $(OBJDIR)/winfile_.c \ |
| 826 | $(OBJDIR)/winhttp_.c \ |
| 827 | $(OBJDIR)/xfer_.c \ |
| @@ -850,14 +845,16 @@ | |
| 845 | $(OBJDIR)/bundle.o \ |
| 846 | $(OBJDIR)/cache.o \ |
| 847 | $(OBJDIR)/capabilities.o \ |
| 848 | $(OBJDIR)/captcha.o \ |
| 849 | $(OBJDIR)/cgi.o \ |
| 850 | $(OBJDIR)/chat.o \ |
| 851 | $(OBJDIR)/checkin.o \ |
| 852 | $(OBJDIR)/checkout.o \ |
| 853 | $(OBJDIR)/clearsign.o \ |
| 854 | $(OBJDIR)/clone.o \ |
| 855 | $(OBJDIR)/color.o \ |
| 856 | $(OBJDIR)/comformat.o \ |
| 857 | $(OBJDIR)/configure.o \ |
| 858 | $(OBJDIR)/content.o \ |
| 859 | $(OBJDIR)/cookies.o \ |
| 860 | $(OBJDIR)/db.o \ |
| @@ -970,11 +967,10 @@ | |
| 967 | $(OBJDIR)/user.o \ |
| 968 | $(OBJDIR)/utf8.o \ |
| 969 | $(OBJDIR)/util.o \ |
| 970 | $(OBJDIR)/verify.o \ |
| 971 | $(OBJDIR)/vfile.o \ |
| 972 | $(OBJDIR)/wiki.o \ |
| 973 | $(OBJDIR)/wikiformat.o \ |
| 974 | $(OBJDIR)/winfile.o \ |
| 975 | $(OBJDIR)/winhttp.o \ |
| 976 | $(OBJDIR)/xfer.o \ |
| @@ -1163,13 +1159,13 @@ | |
| 1159 | |
| 1160 | ifdef FOSSIL_BUILD_SSL |
| 1161 | APPTARGETS += openssl |
| 1162 | endif |
| 1163 | |
| 1164 | $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o |
| 1165 | $(CODECHECK1) $(TRANS_SRC) |
| 1166 | $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB) |
| 1167 | |
| 1168 | # This rule prevents make from using its default rules to try build |
| 1169 | # an executable named "manifest" out of the file named "manifest.c" |
| 1170 | # |
| 1171 | $(SRCDIR)/../manifest: |
| @@ -1213,14 +1209,16 @@ | |
| 1209 | $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ |
| 1210 | $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ |
| 1211 | $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ |
| 1212 | $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ |
| 1213 | $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ |
| 1214 | $(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \ |
| 1215 | $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ |
| 1216 | $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ |
| 1217 | $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ |
| 1218 | $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ |
| 1219 | $(OBJDIR)/color_.c:$(OBJDIR)/color.h \ |
| 1220 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ |
| 1221 | $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ |
| 1222 | $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ |
| 1223 | $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ |
| 1224 | $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ |
| @@ -1333,11 +1331,10 @@ | |
| 1331 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 1332 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 1333 | $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ |
| 1334 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| 1335 | $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ |
| 1336 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1337 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1338 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1339 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1340 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| @@ -1493,10 +1490,18 @@ | |
| 1490 | |
| 1491 | $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h |
| 1492 | $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c |
| 1493 | |
| 1494 | $(OBJDIR)/cgi.h: $(OBJDIR)/headers |
| 1495 | |
| 1496 | $(OBJDIR)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE) |
| 1497 | $(TRANSLATE) $(SRCDIR)/chat.c >$@ |
| 1498 | |
| 1499 | $(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h |
| 1500 | $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c |
| 1501 | |
| 1502 | $(OBJDIR)/chat.h: $(OBJDIR)/headers |
| 1503 | |
| 1504 | $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE) |
| 1505 | $(TRANSLATE) $(SRCDIR)/checkin.c >$@ |
| 1506 | |
| 1507 | $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h |
| @@ -1525,10 +1530,18 @@ | |
| 1530 | |
| 1531 | $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h |
| 1532 | $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c |
| 1533 | |
| 1534 | $(OBJDIR)/clone.h: $(OBJDIR)/headers |
| 1535 | |
| 1536 | $(OBJDIR)/color_.c: $(SRCDIR)/color.c $(TRANSLATE) |
| 1537 | $(TRANSLATE) $(SRCDIR)/color.c >$@ |
| 1538 | |
| 1539 | $(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h |
| 1540 | $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c |
| 1541 | |
| 1542 | $(OBJDIR)/color.h: $(OBJDIR)/headers |
| 1543 | |
| 1544 | $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE) |
| 1545 | $(TRANSLATE) $(SRCDIR)/comformat.c >$@ |
| 1546 | |
| 1547 | $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h |
| @@ -2454,18 +2467,10 @@ | |
| 2467 | $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h |
| 2468 | $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c |
| 2469 | |
| 2470 | $(OBJDIR)/vfile.h: $(OBJDIR)/headers |
| 2471 | |
| 2472 | $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) |
| 2473 | $(TRANSLATE) $(SRCDIR)/wiki.c >$@ |
| 2474 | |
| 2475 | $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h |
| 2476 | $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
| 2477 |
+20
-13
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -14,11 +14,11 @@ | ||
| 14 | 14 | OBJDIR = $(T) |
| 15 | 15 | OX = $(OBJDIR) |
| 16 | 16 | O = .obj |
| 17 | 17 | E = .exe |
| 18 | 18 | P = .pdb |
| 19 | -OPTLEVEL= /Os | |
| 19 | +DBGOPTS = /Od | |
| 20 | 20 | |
| 21 | 21 | INSTALLDIR = . |
| 22 | 22 | !ifdef DESTDIR |
| 23 | 23 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 24 | 24 | !endif |
| @@ -37,10 +37,15 @@ | ||
| 37 | 37 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 38 | 38 | # source code. The PERLDIR environment variable, if it exists, should point |
| 39 | 39 | # to the directory containing the main Perl executable specified here (i.e. |
| 40 | 40 | # "perl.exe"). |
| 41 | 41 | PERL = perl.exe |
| 42 | + | |
| 43 | +# Enable use of available compiler optimizations? | |
| 44 | +!ifndef OPTIMIZATIONS | |
| 45 | +OPTIMIZATIONS = 2 | |
| 46 | +!endif | |
| 42 | 47 | |
| 43 | 48 | # Enable debugging symbols? |
| 44 | 49 | !ifndef DEBUG |
| 45 | 50 | DEBUG = 0 |
| 46 | 51 | !endif |
| @@ -209,16 +214,28 @@ | ||
| 209 | 214 | CRTFLAGS = /MTd |
| 210 | 215 | !else |
| 211 | 216 | CRTFLAGS = /MT |
| 212 | 217 | !endif |
| 213 | 218 | !endif |
| 219 | + | |
| 220 | +!if $(OPTIMIZATIONS)>3 | |
| 221 | +RELOPTS = /Os | |
| 222 | +!elseif $(OPTIMIZATIONS)>2 | |
| 223 | +RELOPTS = /Ox | |
| 224 | +!elseif $(OPTIMIZATIONS)>1 | |
| 225 | +RELOPTS = /O2 | |
| 226 | +!elseif $(OPTIMIZATIONS)>0 | |
| 227 | +RELOPTS = /O1 | |
| 228 | +!else | |
| 229 | +RELOPTS = | |
| 230 | +!endif | |
| 214 | 231 | |
| 215 | 232 | !if $(DEBUG)!=0 |
| 216 | -CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG | |
| 233 | +CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG | |
| 217 | 234 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 218 | 235 | !else |
| 219 | -CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) | |
| 236 | +CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) | |
| 220 | 237 | !endif |
| 221 | 238 | |
| 222 | 239 | BCC = $(CC) $(CFLAGS) |
| 223 | 240 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 224 | 241 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -489,11 +506,10 @@ | ||
| 489 | 506 | "$(OX)\user_.c" \ |
| 490 | 507 | "$(OX)\utf8_.c" \ |
| 491 | 508 | "$(OX)\util_.c" \ |
| 492 | 509 | "$(OX)\verify_.c" \ |
| 493 | 510 | "$(OX)\vfile_.c" \ |
| 494 | - "$(OX)\webmail_.c" \ | |
| 495 | 511 | "$(OX)\wiki_.c" \ |
| 496 | 512 | "$(OX)\wikiformat_.c" \ |
| 497 | 513 | "$(OX)\winfile_.c" \ |
| 498 | 514 | "$(OX)\winhttp_.c" \ |
| 499 | 515 | "$(OX)\xfer_.c" \ |
| @@ -749,11 +765,10 @@ | ||
| 749 | 765 | "$(OX)\user$O" \ |
| 750 | 766 | "$(OX)\utf8$O" \ |
| 751 | 767 | "$(OX)\util$O" \ |
| 752 | 768 | "$(OX)\verify$O" \ |
| 753 | 769 | "$(OX)\vfile$O" \ |
| 754 | - "$(OX)\webmail$O" \ | |
| 755 | 770 | "$(OX)\wiki$O" \ |
| 756 | 771 | "$(OX)\wikiformat$O" \ |
| 757 | 772 | "$(OX)\winfile$O" \ |
| 758 | 773 | "$(OX)\winhttp$O" \ |
| 759 | 774 | "$(OX)\xfer$O" \ |
| @@ -980,11 +995,10 @@ | ||
| 980 | 995 | echo "$(OX)\user.obj" >> $@ |
| 981 | 996 | echo "$(OX)\utf8.obj" >> $@ |
| 982 | 997 | echo "$(OX)\util.obj" >> $@ |
| 983 | 998 | echo "$(OX)\verify.obj" >> $@ |
| 984 | 999 | echo "$(OX)\vfile.obj" >> $@ |
| 985 | - echo "$(OX)\webmail.obj" >> $@ | |
| 986 | 1000 | echo "$(OX)\wiki.obj" >> $@ |
| 987 | 1001 | echo "$(OX)\wikiformat.obj" >> $@ |
| 988 | 1002 | echo "$(OX)\winfile.obj" >> $@ |
| 989 | 1003 | echo "$(OX)\winhttp.obj" >> $@ |
| 990 | 1004 | echo "$(OX)\xfer.obj" >> $@ |
| @@ -2049,16 +2063,10 @@ | ||
| 2049 | 2063 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c" |
| 2050 | 2064 | |
| 2051 | 2065 | "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c" |
| 2052 | 2066 | "$(OBJDIR)\translate$E" $** > $@ |
| 2053 | 2067 | |
| 2054 | -"$(OX)\webmail$O" : "$(OX)\webmail_.c" "$(OX)\webmail.h" | |
| 2055 | - $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\webmail_.c" | |
| 2056 | - | |
| 2057 | -"$(OX)\webmail_.c" : "$(SRCDIR)\webmail.c" | |
| 2058 | - "$(OBJDIR)\translate$E" $** > $@ | |
| 2059 | - | |
| 2060 | 2068 | "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h" |
| 2061 | 2069 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c" |
| 2062 | 2070 | |
| 2063 | 2071 | "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c" |
| 2064 | 2072 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2241,11 +2249,10 @@ | ||
| 2241 | 2249 | "$(OX)\user_.c":"$(OX)\user.h" \ |
| 2242 | 2250 | "$(OX)\utf8_.c":"$(OX)\utf8.h" \ |
| 2243 | 2251 | "$(OX)\util_.c":"$(OX)\util.h" \ |
| 2244 | 2252 | "$(OX)\verify_.c":"$(OX)\verify.h" \ |
| 2245 | 2253 | "$(OX)\vfile_.c":"$(OX)\vfile.h" \ |
| 2246 | - "$(OX)\webmail_.c":"$(OX)\webmail.h" \ | |
| 2247 | 2254 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2248 | 2255 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2249 | 2256 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2250 | 2257 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2251 | 2258 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2252 | 2259 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -14,11 +14,11 @@ | |
| 14 | OBJDIR = $(T) |
| 15 | OX = $(OBJDIR) |
| 16 | O = .obj |
| 17 | E = .exe |
| 18 | P = .pdb |
| 19 | OPTLEVEL= /Os |
| 20 | |
| 21 | INSTALLDIR = . |
| 22 | !ifdef DESTDIR |
| 23 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 24 | !endif |
| @@ -37,10 +37,15 @@ | |
| 37 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 38 | # source code. The PERLDIR environment variable, if it exists, should point |
| 39 | # to the directory containing the main Perl executable specified here (i.e. |
| 40 | # "perl.exe"). |
| 41 | PERL = perl.exe |
| 42 | |
| 43 | # Enable debugging symbols? |
| 44 | !ifndef DEBUG |
| 45 | DEBUG = 0 |
| 46 | !endif |
| @@ -209,16 +214,28 @@ | |
| 209 | CRTFLAGS = /MTd |
| 210 | !else |
| 211 | CRTFLAGS = /MT |
| 212 | !endif |
| 213 | !endif |
| 214 | |
| 215 | !if $(DEBUG)!=0 |
| 216 | CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG |
| 217 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 218 | !else |
| 219 | CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL) |
| 220 | !endif |
| 221 | |
| 222 | BCC = $(CC) $(CFLAGS) |
| 223 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 224 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -489,11 +506,10 @@ | |
| 489 | "$(OX)\user_.c" \ |
| 490 | "$(OX)\utf8_.c" \ |
| 491 | "$(OX)\util_.c" \ |
| 492 | "$(OX)\verify_.c" \ |
| 493 | "$(OX)\vfile_.c" \ |
| 494 | "$(OX)\webmail_.c" \ |
| 495 | "$(OX)\wiki_.c" \ |
| 496 | "$(OX)\wikiformat_.c" \ |
| 497 | "$(OX)\winfile_.c" \ |
| 498 | "$(OX)\winhttp_.c" \ |
| 499 | "$(OX)\xfer_.c" \ |
| @@ -749,11 +765,10 @@ | |
| 749 | "$(OX)\user$O" \ |
| 750 | "$(OX)\utf8$O" \ |
| 751 | "$(OX)\util$O" \ |
| 752 | "$(OX)\verify$O" \ |
| 753 | "$(OX)\vfile$O" \ |
| 754 | "$(OX)\webmail$O" \ |
| 755 | "$(OX)\wiki$O" \ |
| 756 | "$(OX)\wikiformat$O" \ |
| 757 | "$(OX)\winfile$O" \ |
| 758 | "$(OX)\winhttp$O" \ |
| 759 | "$(OX)\xfer$O" \ |
| @@ -980,11 +995,10 @@ | |
| 980 | echo "$(OX)\user.obj" >> $@ |
| 981 | echo "$(OX)\utf8.obj" >> $@ |
| 982 | echo "$(OX)\util.obj" >> $@ |
| 983 | echo "$(OX)\verify.obj" >> $@ |
| 984 | echo "$(OX)\vfile.obj" >> $@ |
| 985 | echo "$(OX)\webmail.obj" >> $@ |
| 986 | echo "$(OX)\wiki.obj" >> $@ |
| 987 | echo "$(OX)\wikiformat.obj" >> $@ |
| 988 | echo "$(OX)\winfile.obj" >> $@ |
| 989 | echo "$(OX)\winhttp.obj" >> $@ |
| 990 | echo "$(OX)\xfer.obj" >> $@ |
| @@ -2049,16 +2063,10 @@ | |
| 2049 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c" |
| 2050 | |
| 2051 | "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c" |
| 2052 | "$(OBJDIR)\translate$E" $** > $@ |
| 2053 | |
| 2054 | "$(OX)\webmail$O" : "$(OX)\webmail_.c" "$(OX)\webmail.h" |
| 2055 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\webmail_.c" |
| 2056 | |
| 2057 | "$(OX)\webmail_.c" : "$(SRCDIR)\webmail.c" |
| 2058 | "$(OBJDIR)\translate$E" $** > $@ |
| 2059 | |
| 2060 | "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h" |
| 2061 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c" |
| 2062 | |
| 2063 | "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c" |
| 2064 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2241,11 +2249,10 @@ | |
| 2241 | "$(OX)\user_.c":"$(OX)\user.h" \ |
| 2242 | "$(OX)\utf8_.c":"$(OX)\utf8.h" \ |
| 2243 | "$(OX)\util_.c":"$(OX)\util.h" \ |
| 2244 | "$(OX)\verify_.c":"$(OX)\verify.h" \ |
| 2245 | "$(OX)\vfile_.c":"$(OX)\vfile.h" \ |
| 2246 | "$(OX)\webmail_.c":"$(OX)\webmail.h" \ |
| 2247 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2248 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2249 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2250 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2251 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2252 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -14,11 +14,11 @@ | |
| 14 | OBJDIR = $(T) |
| 15 | OX = $(OBJDIR) |
| 16 | O = .obj |
| 17 | E = .exe |
| 18 | P = .pdb |
| 19 | DBGOPTS = /Od |
| 20 | |
| 21 | INSTALLDIR = . |
| 22 | !ifdef DESTDIR |
| 23 | INSTALLDIR = $(DESTDIR)\$(INSTALLDIR) |
| 24 | !endif |
| @@ -37,10 +37,15 @@ | |
| 37 | # Perl is only necessary if OpenSSL support is enabled and it is built from |
| 38 | # source code. The PERLDIR environment variable, if it exists, should point |
| 39 | # to the directory containing the main Perl executable specified here (i.e. |
| 40 | # "perl.exe"). |
| 41 | PERL = perl.exe |
| 42 | |
| 43 | # Enable use of available compiler optimizations? |
| 44 | !ifndef OPTIMIZATIONS |
| 45 | OPTIMIZATIONS = 2 |
| 46 | !endif |
| 47 | |
| 48 | # Enable debugging symbols? |
| 49 | !ifndef DEBUG |
| 50 | DEBUG = 0 |
| 51 | !endif |
| @@ -209,16 +214,28 @@ | |
| 214 | CRTFLAGS = /MTd |
| 215 | !else |
| 216 | CRTFLAGS = /MT |
| 217 | !endif |
| 218 | !endif |
| 219 | |
| 220 | !if $(OPTIMIZATIONS)>3 |
| 221 | RELOPTS = /Os |
| 222 | !elseif $(OPTIMIZATIONS)>2 |
| 223 | RELOPTS = /Ox |
| 224 | !elseif $(OPTIMIZATIONS)>1 |
| 225 | RELOPTS = /O2 |
| 226 | !elseif $(OPTIMIZATIONS)>0 |
| 227 | RELOPTS = /O1 |
| 228 | !else |
| 229 | RELOPTS = |
| 230 | !endif |
| 231 | |
| 232 | !if $(DEBUG)!=0 |
| 233 | CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG |
| 234 | LDFLAGS = $(LDFLAGS) /DEBUG |
| 235 | !else |
| 236 | CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS) |
| 237 | !endif |
| 238 | |
| 239 | BCC = $(CC) $(CFLAGS) |
| 240 | TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) |
| 241 | RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) |
| @@ -489,11 +506,10 @@ | |
| 506 | "$(OX)\user_.c" \ |
| 507 | "$(OX)\utf8_.c" \ |
| 508 | "$(OX)\util_.c" \ |
| 509 | "$(OX)\verify_.c" \ |
| 510 | "$(OX)\vfile_.c" \ |
| 511 | "$(OX)\wiki_.c" \ |
| 512 | "$(OX)\wikiformat_.c" \ |
| 513 | "$(OX)\winfile_.c" \ |
| 514 | "$(OX)\winhttp_.c" \ |
| 515 | "$(OX)\xfer_.c" \ |
| @@ -749,11 +765,10 @@ | |
| 765 | "$(OX)\user$O" \ |
| 766 | "$(OX)\utf8$O" \ |
| 767 | "$(OX)\util$O" \ |
| 768 | "$(OX)\verify$O" \ |
| 769 | "$(OX)\vfile$O" \ |
| 770 | "$(OX)\wiki$O" \ |
| 771 | "$(OX)\wikiformat$O" \ |
| 772 | "$(OX)\winfile$O" \ |
| 773 | "$(OX)\winhttp$O" \ |
| 774 | "$(OX)\xfer$O" \ |
| @@ -980,11 +995,10 @@ | |
| 995 | echo "$(OX)\user.obj" >> $@ |
| 996 | echo "$(OX)\utf8.obj" >> $@ |
| 997 | echo "$(OX)\util.obj" >> $@ |
| 998 | echo "$(OX)\verify.obj" >> $@ |
| 999 | echo "$(OX)\vfile.obj" >> $@ |
| 1000 | echo "$(OX)\wiki.obj" >> $@ |
| 1001 | echo "$(OX)\wikiformat.obj" >> $@ |
| 1002 | echo "$(OX)\winfile.obj" >> $@ |
| 1003 | echo "$(OX)\winhttp.obj" >> $@ |
| 1004 | echo "$(OX)\xfer.obj" >> $@ |
| @@ -2049,16 +2063,10 @@ | |
| 2063 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c" |
| 2064 | |
| 2065 | "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c" |
| 2066 | "$(OBJDIR)\translate$E" $** > $@ |
| 2067 | |
| 2068 | "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h" |
| 2069 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c" |
| 2070 | |
| 2071 | "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c" |
| 2072 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2241,11 +2249,10 @@ | |
| 2249 | "$(OX)\user_.c":"$(OX)\user.h" \ |
| 2250 | "$(OX)\utf8_.c":"$(OX)\utf8.h" \ |
| 2251 | "$(OX)\util_.c":"$(OX)\util.h" \ |
| 2252 | "$(OX)\verify_.c":"$(OX)\verify.h" \ |
| 2253 | "$(OX)\vfile_.c":"$(OX)\vfile.h" \ |
| 2254 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2255 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2256 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2257 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2258 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2259 |
+41
-1
| --- win/buildmsvc.bat | ||
| +++ win/buildmsvc.bat | ||
| @@ -33,11 +33,11 @@ | ||
| 33 | 33 | |
| 34 | 34 | REM |
| 35 | 35 | REM Visual C++ ???? |
| 36 | 36 | REM |
| 37 | 37 | IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" ( |
| 38 | - %_AECHO% Build environment appears to be setup. | |
| 38 | + %_AECHO% Build environment appears to be set up. | |
| 39 | 39 | GOTO skip_setupVisualStudio |
| 40 | 40 | ) |
| 41 | 41 | |
| 42 | 42 | REM |
| 43 | 43 | REM Visual Studio ???? |
| @@ -45,10 +45,22 @@ | ||
| 45 | 45 | IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" ( |
| 46 | 46 | %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"... |
| 47 | 47 | GOTO skip_detectVisualStudio |
| 48 | 48 | ) |
| 49 | 49 | |
| 50 | +REM | |
| 51 | +REM Visual Studio 2017 / 2019 | |
| 52 | +REM | |
| 53 | +CALL :fn_TryUseVsWhereExe | |
| 54 | +IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017 | |
| 55 | +SET VSVARS32=%VSWHEREINSTALLDIR%\Common7\Tools\VsDevCmd.bat | |
| 56 | +IF EXIST "%VSVARS32%" ( | |
| 57 | + %_AECHO% Using Visual Studio 2017 / 2019... | |
| 58 | + GOTO skip_detectVisualStudio | |
| 59 | +) | |
| 60 | +:skip_detectVisualStudio2017 | |
| 61 | + | |
| 50 | 62 | REM |
| 51 | 63 | REM Visual Studio 2015 |
| 52 | 64 | REM |
| 53 | 65 | IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015 |
| 54 | 66 | SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat |
| @@ -341,10 +353,38 @@ | ||
| 341 | 353 | GOTO :EOF |
| 342 | 354 | |
| 343 | 355 | :fn_SetErrorLevel |
| 344 | 356 | VERIFY MAYBE 2> NUL |
| 345 | 357 | GOTO :EOF |
| 358 | + | |
| 359 | +:fn_TryUseVsWhereExe | |
| 360 | + IF DEFINED VSWHERE_EXE GOTO skip_setVsWhereExe | |
| 361 | + SET VSWHERE_EXE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe | |
| 362 | + IF NOT EXIST "%VSWHERE_EXE%" SET VSWHERE_EXE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe | |
| 363 | + :skip_setVsWhereExe | |
| 364 | + IF NOT EXIST "%VSWHERE_EXE%" ( | |
| 365 | + %_AECHO% The "VsWhere" tool does not appear to be installed. | |
| 366 | + GOTO :EOF | |
| 367 | + ) | |
| 368 | + SET VS_WHEREIS_CMD="%VSWHERE_EXE%" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -latest | |
| 369 | + IF DEFINED __ECHO ( | |
| 370 | + %__ECHO% %VS_WHEREIS_CMD% | |
| 371 | + REM | |
| 372 | + REM NOTE: This will not be executed, any reasonable fake path will work. | |
| 373 | + REM | |
| 374 | + SET VSWHEREINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2017\Community | |
| 375 | + GOTO skip_setVsWhereInstallDir | |
| 376 | + ) | |
| 377 | + FOR /F "delims=" %%D IN ('%VS_WHEREIS_CMD%') DO (SET VSWHEREINSTALLDIR=%%D) | |
| 378 | + :skip_setVsWhereInstallDir | |
| 379 | + %_VECHO% VsWhereInstallDir = '%VSWHEREINSTALLDIR%' | |
| 380 | + IF NOT DEFINED VSWHEREINSTALLDIR ( | |
| 381 | + %_AECHO% Visual Studio 2017 / 2019 is not installed. | |
| 382 | + GOTO :EOF | |
| 383 | + ) | |
| 384 | + %_AECHO% Visual Studio 2017 / 2019 is installed. | |
| 385 | + GOTO :EOF | |
| 346 | 386 | |
| 347 | 387 | :usage |
| 348 | 388 | ECHO. |
| 349 | 389 | ECHO Usage: %~nx0 [...] |
| 350 | 390 | ECHO. |
| 351 | 391 |
| --- win/buildmsvc.bat | |
| +++ win/buildmsvc.bat | |
| @@ -33,11 +33,11 @@ | |
| 33 | |
| 34 | REM |
| 35 | REM Visual C++ ???? |
| 36 | REM |
| 37 | IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" ( |
| 38 | %_AECHO% Build environment appears to be setup. |
| 39 | GOTO skip_setupVisualStudio |
| 40 | ) |
| 41 | |
| 42 | REM |
| 43 | REM Visual Studio ???? |
| @@ -45,10 +45,22 @@ | |
| 45 | IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" ( |
| 46 | %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"... |
| 47 | GOTO skip_detectVisualStudio |
| 48 | ) |
| 49 | |
| 50 | REM |
| 51 | REM Visual Studio 2015 |
| 52 | REM |
| 53 | IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015 |
| 54 | SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat |
| @@ -341,10 +353,38 @@ | |
| 341 | GOTO :EOF |
| 342 | |
| 343 | :fn_SetErrorLevel |
| 344 | VERIFY MAYBE 2> NUL |
| 345 | GOTO :EOF |
| 346 | |
| 347 | :usage |
| 348 | ECHO. |
| 349 | ECHO Usage: %~nx0 [...] |
| 350 | ECHO. |
| 351 |
| --- win/buildmsvc.bat | |
| +++ win/buildmsvc.bat | |
| @@ -33,11 +33,11 @@ | |
| 33 | |
| 34 | REM |
| 35 | REM Visual C++ ???? |
| 36 | REM |
| 37 | IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" ( |
| 38 | %_AECHO% Build environment appears to be set up. |
| 39 | GOTO skip_setupVisualStudio |
| 40 | ) |
| 41 | |
| 42 | REM |
| 43 | REM Visual Studio ???? |
| @@ -45,10 +45,22 @@ | |
| 45 | IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" ( |
| 46 | %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"... |
| 47 | GOTO skip_detectVisualStudio |
| 48 | ) |
| 49 | |
| 50 | REM |
| 51 | REM Visual Studio 2017 / 2019 |
| 52 | REM |
| 53 | CALL :fn_TryUseVsWhereExe |
| 54 | IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017 |
| 55 | SET VSVARS32=%VSWHEREINSTALLDIR%\Common7\Tools\VsDevCmd.bat |
| 56 | IF EXIST "%VSVARS32%" ( |
| 57 | %_AECHO% Using Visual Studio 2017 / 2019... |
| 58 | GOTO skip_detectVisualStudio |
| 59 | ) |
| 60 | :skip_detectVisualStudio2017 |
| 61 | |
| 62 | REM |
| 63 | REM Visual Studio 2015 |
| 64 | REM |
| 65 | IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015 |
| 66 | SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat |
| @@ -341,10 +353,38 @@ | |
| 353 | GOTO :EOF |
| 354 | |
| 355 | :fn_SetErrorLevel |
| 356 | VERIFY MAYBE 2> NUL |
| 357 | GOTO :EOF |
| 358 | |
| 359 | :fn_TryUseVsWhereExe |
| 360 | IF DEFINED VSWHERE_EXE GOTO skip_setVsWhereExe |
| 361 | SET VSWHERE_EXE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe |
| 362 | IF NOT EXIST "%VSWHERE_EXE%" SET VSWHERE_EXE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe |
| 363 | :skip_setVsWhereExe |
| 364 | IF NOT EXIST "%VSWHERE_EXE%" ( |
| 365 | %_AECHO% The "VsWhere" tool does not appear to be installed. |
| 366 | GOTO :EOF |
| 367 | ) |
| 368 | SET VS_WHEREIS_CMD="%VSWHERE_EXE%" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -latest |
| 369 | IF DEFINED __ECHO ( |
| 370 | %__ECHO% %VS_WHEREIS_CMD% |
| 371 | REM |
| 372 | REM NOTE: This will not be executed, any reasonable fake path will work. |
| 373 | REM |
| 374 | SET VSWHEREINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2017\Community |
| 375 | GOTO skip_setVsWhereInstallDir |
| 376 | ) |
| 377 | FOR /F "delims=" %%D IN ('%VS_WHEREIS_CMD%') DO (SET VSWHEREINSTALLDIR=%%D) |
| 378 | :skip_setVsWhereInstallDir |
| 379 | %_VECHO% VsWhereInstallDir = '%VSWHEREINSTALLDIR%' |
| 380 | IF NOT DEFINED VSWHEREINSTALLDIR ( |
| 381 | %_AECHO% Visual Studio 2017 / 2019 is not installed. |
| 382 | GOTO :EOF |
| 383 | ) |
| 384 | %_AECHO% Visual Studio 2017 / 2019 is installed. |
| 385 | GOTO :EOF |
| 386 | |
| 387 | :usage |
| 388 | ECHO. |
| 389 | ECHO Usage: %~nx0 [...] |
| 390 | ECHO. |
| 391 |
+7
-4
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -151,21 +151,24 @@ | ||
| 151 | 151 | |
| 152 | 152 | <i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary |
| 153 | 153 | will not work correctly, see |
| 154 | 154 | [https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket]. |
| 155 | 155 | |
| 156 | -<li><p><i>MSVC</i> → Use the MSVC makefile. First | |
| 156 | +<li><p><i>MSVC</i> → Use the MSVC makefile. | |
| 157 | + | |
| 158 | +Run all of the following from a "x64 Native Tools Command Prompt". | |
| 159 | + | |
| 160 | +First | |
| 157 | 161 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| 158 | 162 | "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch |
| 159 | 163 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to |
| 160 | 164 | detect and use the latest installed version of MSVC.<br><br>To enable |
| 161 | 165 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 162 | 166 | first <a href="https://www.openssl.org/source/">download the official |
| 163 | 167 | source code for OpenSSL</a> and extract it to an appropriately named |
| 164 | -"<b>openssl-X.Y.ZA</b>" subdirectory within the local | |
| 165 | -[/tree?ci=trunk&name=compat | compat] directory (e.g. | |
| 166 | -"<b>compat/openssl-1.1.1g</b>"), then make sure that some recent | |
| 168 | +"<b>openssl</b>" subdirectory within the local | |
| 169 | +[/tree?ci=trunk&name=compat | compat] directory then make sure that some recent | |
| 167 | 170 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 168 | 171 | and finally run one of the following commands: |
| 169 | 172 | <blockquote><pre> |
| 170 | 173 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 171 | 174 | </pre></blockquote> |
| 172 | 175 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -151,21 +151,24 @@ | |
| 151 | |
| 152 | <i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary |
| 153 | will not work correctly, see |
| 154 | [https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket]. |
| 155 | |
| 156 | <li><p><i>MSVC</i> → Use the MSVC makefile. First |
| 157 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| 158 | "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch |
| 159 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to |
| 160 | detect and use the latest installed version of MSVC.<br><br>To enable |
| 161 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 162 | first <a href="https://www.openssl.org/source/">download the official |
| 163 | source code for OpenSSL</a> and extract it to an appropriately named |
| 164 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 165 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 166 | "<b>compat/openssl-1.1.1g</b>"), then make sure that some recent |
| 167 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 168 | and finally run one of the following commands: |
| 169 | <blockquote><pre> |
| 170 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 171 | </pre></blockquote> |
| 172 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -151,21 +151,24 @@ | |
| 151 | |
| 152 | <i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary |
| 153 | will not work correctly, see |
| 154 | [https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket]. |
| 155 | |
| 156 | <li><p><i>MSVC</i> → Use the MSVC makefile. |
| 157 | |
| 158 | Run all of the following from a "x64 Native Tools Command Prompt". |
| 159 | |
| 160 | First |
| 161 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| 162 | "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch |
| 163 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to |
| 164 | detect and use the latest installed version of MSVC.<br><br>To enable |
| 165 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 166 | first <a href="https://www.openssl.org/source/">download the official |
| 167 | source code for OpenSSL</a> and extract it to an appropriately named |
| 168 | "<b>openssl</b>" subdirectory within the local |
| 169 | [/tree?ci=trunk&name=compat | compat] directory then make sure that some recent |
| 170 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 171 | and finally run one of the following commands: |
| 172 | <blockquote><pre> |
| 173 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 174 | </pre></blockquote> |
| 175 |
+32
-2
| --- www/event.wiki | ||
| +++ www/event.wiki | ||
| @@ -31,11 +31,12 @@ | ||
| 31 | 31 | * <b>Process Checkpoints</b>. For projects that have a formal process, |
| 32 | 32 | technotes can be used to record the completion or the initiation of |
| 33 | 33 | various process steps. For example, a technote can be used to record |
| 34 | 34 | the successful completion of a long-running test, perhaps with |
| 35 | 35 | performance results and details of where the test was run and who |
| 36 | - ran it recorded in the wiki content. | |
| 36 | + ran it recorded in the wiki content. The technote can be added | |
| 37 | + from a script. | |
| 37 | 38 | |
| 38 | 39 | * <b>News Articles</b>. Significant occurrences in the life cycle of |
| 39 | 40 | a project can be recorded as news articles using technotes. Perhaps the |
| 40 | 41 | domain name of the canonical website for a project changes, or new |
| 41 | 42 | server hardware is obtained. Such happenings are appropriate for |
| @@ -58,18 +59,47 @@ | ||
| 58 | 59 | and technotes. |
| 59 | 60 | |
| 60 | 61 | Technotes show up on the timeline. Click on the hyperlink beside the |
| 61 | 62 | technote title to see the complete text. |
| 62 | 63 | |
| 63 | -<h2>Creating And Editing Technotes</h2> | |
| 64 | +<h2>Creating, Editing and Viewing Technotes</h2> | |
| 64 | 65 | |
| 65 | 66 | There is a hyperlink under the /wikihelp menu that can be used to create |
| 66 | 67 | new technotes. And there is a submenu hyperlink on technote displays for |
| 67 | 68 | editing existing technotes. |
| 69 | + | |
| 70 | +Technotes can also be created using the <b>wiki create</b> command: | |
| 71 | + | |
| 72 | +<blockquote> | |
| 73 | +<b> | |
| 74 | +fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md<br> | |
| 75 | +<tt>Created new tech note 2021-03-15 13:05:56</tt><br> | |
| 76 | +</b> | |
| 77 | +</blockquote> | |
| 78 | + | |
| 79 | +This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with | |
| 80 | +the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using | |
| 81 | +<b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. | |
| 82 | +Different technotes can have the same timestamp. | |
| 83 | + | |
| 84 | +The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote | |
| 85 | +that appears in the timeline. | |
| 86 | + | |
| 87 | +To view technotes, use the <b>wiki ls</b> command: | |
| 88 | + | |
| 89 | +<blockquote> | |
| 90 | +<b> | |
| 91 | +fossil wiki ls --technote --show-technote-ids<br> | |
| 92 | +<tt>z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19</tt><br> | |
| 93 | +<tt>e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10</tt><br> | |
| 94 | +</b> | |
| 95 | +</blockquote> | |
| 96 | + | |
| 97 | +A technote ID is the UUID of the technote. | |
| 68 | 98 | |
| 69 | 99 | Users must have check-in privileges (permission "i") in order to |
| 70 | 100 | create or edit technotes. In addition, users must have create-wiki |
| 71 | 101 | privilege (permission "f") to create new technotes and edit-wiki |
| 72 | 102 | privilege (permission "k") in order to edit existing technotes. |
| 73 | 103 | |
| 74 | 104 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 75 | 105 | [/md_rules | Markdown], or a plain text. |
| 76 | 106 |
| --- www/event.wiki | |
| +++ www/event.wiki | |
| @@ -31,11 +31,12 @@ | |
| 31 | * <b>Process Checkpoints</b>. For projects that have a formal process, |
| 32 | technotes can be used to record the completion or the initiation of |
| 33 | various process steps. For example, a technote can be used to record |
| 34 | the successful completion of a long-running test, perhaps with |
| 35 | performance results and details of where the test was run and who |
| 36 | ran it recorded in the wiki content. |
| 37 | |
| 38 | * <b>News Articles</b>. Significant occurrences in the life cycle of |
| 39 | a project can be recorded as news articles using technotes. Perhaps the |
| 40 | domain name of the canonical website for a project changes, or new |
| 41 | server hardware is obtained. Such happenings are appropriate for |
| @@ -58,18 +59,47 @@ | |
| 58 | and technotes. |
| 59 | |
| 60 | Technotes show up on the timeline. Click on the hyperlink beside the |
| 61 | technote title to see the complete text. |
| 62 | |
| 63 | <h2>Creating And Editing Technotes</h2> |
| 64 | |
| 65 | There is a hyperlink under the /wikihelp menu that can be used to create |
| 66 | new technotes. And there is a submenu hyperlink on technote displays for |
| 67 | editing existing technotes. |
| 68 | |
| 69 | Users must have check-in privileges (permission "i") in order to |
| 70 | create or edit technotes. In addition, users must have create-wiki |
| 71 | privilege (permission "f") to create new technotes and edit-wiki |
| 72 | privilege (permission "k") in order to edit existing technotes. |
| 73 | |
| 74 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 75 | [/md_rules | Markdown], or a plain text. |
| 76 |
| --- www/event.wiki | |
| +++ www/event.wiki | |
| @@ -31,11 +31,12 @@ | |
| 31 | * <b>Process Checkpoints</b>. For projects that have a formal process, |
| 32 | technotes can be used to record the completion or the initiation of |
| 33 | various process steps. For example, a technote can be used to record |
| 34 | the successful completion of a long-running test, perhaps with |
| 35 | performance results and details of where the test was run and who |
| 36 | ran it recorded in the wiki content. The technote can be added |
| 37 | from a script. |
| 38 | |
| 39 | * <b>News Articles</b>. Significant occurrences in the life cycle of |
| 40 | a project can be recorded as news articles using technotes. Perhaps the |
| 41 | domain name of the canonical website for a project changes, or new |
| 42 | server hardware is obtained. Such happenings are appropriate for |
| @@ -58,18 +59,47 @@ | |
| 59 | and technotes. |
| 60 | |
| 61 | Technotes show up on the timeline. Click on the hyperlink beside the |
| 62 | technote title to see the complete text. |
| 63 | |
| 64 | <h2>Creating, Editing and Viewing Technotes</h2> |
| 65 | |
| 66 | There is a hyperlink under the /wikihelp menu that can be used to create |
| 67 | new technotes. And there is a submenu hyperlink on technote displays for |
| 68 | editing existing technotes. |
| 69 | |
| 70 | Technotes can also be created using the <b>wiki create</b> command: |
| 71 | |
| 72 | <blockquote> |
| 73 | <b> |
| 74 | fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md<br> |
| 75 | <tt>Created new tech note 2021-03-15 13:05:56</tt><br> |
| 76 | </b> |
| 77 | </blockquote> |
| 78 | |
| 79 | This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with |
| 80 | the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using |
| 81 | <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. |
| 82 | Different technotes can have the same timestamp. |
| 83 | |
| 84 | The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote |
| 85 | that appears in the timeline. |
| 86 | |
| 87 | To view technotes, use the <b>wiki ls</b> command: |
| 88 | |
| 89 | <blockquote> |
| 90 | <b> |
| 91 | fossil wiki ls --technote --show-technote-ids<br> |
| 92 | <tt>z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19</tt><br> |
| 93 | <tt>e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10</tt><br> |
| 94 | </b> |
| 95 | </blockquote> |
| 96 | |
| 97 | A technote ID is the UUID of the technote. |
| 98 | |
| 99 | Users must have check-in privileges (permission "i") in order to |
| 100 | create or edit technotes. In addition, users must have create-wiki |
| 101 | privilege (permission "f") to create new technotes and edit-wiki |
| 102 | privilege (permission "k") in order to edit existing technotes. |
| 103 | |
| 104 | Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| 105 | [/md_rules | Markdown], or a plain text. |
| 106 |
+39
-27
| --- www/gsoc-ideas.md | ||
| +++ www/gsoc-ideas.md | ||
| @@ -1,27 +1,50 @@ | ||
| 1 | -# Project Ideas for Google Summer of Code 2021 | |
| 2 | - | |
| 3 | -This list was made for the Fossil project's application for [Google Summer of | |
| 4 | -Code](https://summerofcode.withgoogle.com/) in 2021. GSoC pays students to | |
| 5 | -contribute to free software projects during the Northern Hemiphere summer. If | |
| 6 | -you are a student, you will be able to apply for GSoC starting March 29th 2021. | |
| 7 | - | |
| 8 | -This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org) | |
| 9 | -and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations | |
| 10 | -have an identical implementation of the Fossil data model, are 100% compatible in terms of | |
| 11 | -data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage. | |
| 12 | - | |
| 13 | -# General Features | |
| 14 | - | |
| 1 | +# List of Projects and Tasks | |
| 2 | + | |
| 3 | +This list was made for the Fossil project's application for [Google Summer of Code](https://summerofcode.withgoogle.com/) in 2021. That application was | |
| 4 | +unsuccessful, but still this list is a starting point for anyone looking | |
| 5 | +for a place to start. We welcome newcomers, and invite developers to follow the simple | |
| 6 | +[procedures for contributing to Fossil](./contribute.wiki). The | |
| 7 | +[hacker how-to](./hacker-howto.wiki) is recommended reading. | |
| 8 | + | |
| 9 | +There are two implementations of the Fossil data model: | |
| 10 | + | |
| 11 | +* [the classic Fossil project](https://fossil-scm.org) , which is where this file is maintained and | |
| 12 | + which is as of 2021 how everyone interacts with Fossil objects | |
| 13 | +* [libfossil](https://fossil.wanderinghorse.net/r/libfossil), which is an independent project to manipulate Fossil objects from a library, or using commandline tools which are thin wrappers to the library | |
| 14 | + | |
| 15 | +As of 2021 the two implementations have an identical implementation of the | |
| 16 | +Fossil data model, are 100% compatible in terms of data access since they use | |
| 17 | +the same SQL, and are 100% binary compatible in terms of on-disk storage. | |
| 18 | + | |
| 19 | +The projects listed here are grouped by functionality - User Interface, Integration, Email, | |
| 20 | +etc. If you are looking for something easy to start with, then depending where | |
| 21 | +your interests lie, there are some small libfossil tasks and small | |
| 22 | +features to work on in the UI. | |
| 23 | + | |
| 24 | +# UI, Look and Feel | |
| 25 | + | |
| 26 | +Tasks for those interested in graphic/web design: | |
| 27 | + | |
| 28 | +* Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d) | |
| 15 | 29 | * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history) |
| 16 | 30 | * Allow diffing of Forum posts |
| 17 | -* Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil | |
| 18 | -* Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis | |
| 31 | +* General touch-ups in the existing skins. This may, depending on how deep one | |
| 32 | + cares to dig, require digging into C code to find, and potentially modify, how | |
| 33 | + the HTML is generated. | |
| 34 | +* Creation of one or more new skins. This does not specifically require any C | |
| 35 | + know-how. | |
| 36 | +* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator | |
| 37 | + | |
| 38 | +# Projects Relating to Fossil Integration | |
| 39 | + | |
| 19 | 40 | * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples |
| 20 | 41 | * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown |
| 21 | 42 | * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown) |
| 22 | 43 | * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31) |
| 44 | +* Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil | |
| 45 | +* Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis | |
| 23 | 46 | |
| 24 | 47 | # Adding Inbound (Receiving) Email to Fossil |
| 25 | 48 | |
| 26 | 49 | This task involves designing a new feature and working with Fossil developers to |
| 27 | 50 | see how it can be feasible in practice. |
| @@ -85,21 +108,10 @@ | ||
| 85 | 108 | * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory. |
| 86 | 109 | * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing, |
| 87 | 110 | and have UI features that would improve ticketing |
| 88 | 111 | * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing |
| 89 | 112 | |
| 90 | -# Look and Feel | |
| 91 | - | |
| 92 | -Tasks for those interested in graphic/web design: | |
| 93 | - | |
| 94 | -* General touch-ups in the existing skins. This may, depending on how deep one | |
| 95 | - cares to dig, require digging into C code to find, and potentially modify, how | |
| 96 | - the HTML is generated. | |
| 97 | -* Creation of one or more new skins. This does not specifically require any C | |
| 98 | - know-how. | |
| 99 | -* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator | |
| 100 | - | |
| 101 | 113 | # Tasks Requiring Fossil Data Model Knowledge |
| 102 | 114 | |
| 103 | 115 | The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model |
| 104 | 116 | is designed to [endure for centuries](./fileformat.wiki), |
| 105 | 117 | be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md). |
| 106 | 118 |
| --- www/gsoc-ideas.md | |
| +++ www/gsoc-ideas.md | |
| @@ -1,27 +1,50 @@ | |
| 1 | # Project Ideas for Google Summer of Code 2021 |
| 2 | |
| 3 | This list was made for the Fossil project's application for [Google Summer of |
| 4 | Code](https://summerofcode.withgoogle.com/) in 2021. GSoC pays students to |
| 5 | contribute to free software projects during the Northern Hemiphere summer. If |
| 6 | you are a student, you will be able to apply for GSoC starting March 29th 2021. |
| 7 | |
| 8 | This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org) |
| 9 | and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations |
| 10 | have an identical implementation of the Fossil data model, are 100% compatible in terms of |
| 11 | data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage. |
| 12 | |
| 13 | # General Features |
| 14 | |
| 15 | * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history) |
| 16 | * Allow diffing of Forum posts |
| 17 | * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil |
| 18 | * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis |
| 19 | * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples |
| 20 | * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown |
| 21 | * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown) |
| 22 | * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31) |
| 23 | |
| 24 | # Adding Inbound (Receiving) Email to Fossil |
| 25 | |
| 26 | This task involves designing a new feature and working with Fossil developers to |
| 27 | see how it can be feasible in practice. |
| @@ -85,21 +108,10 @@ | |
| 85 | * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory. |
| 86 | * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing, |
| 87 | and have UI features that would improve ticketing |
| 88 | * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing |
| 89 | |
| 90 | # Look and Feel |
| 91 | |
| 92 | Tasks for those interested in graphic/web design: |
| 93 | |
| 94 | * General touch-ups in the existing skins. This may, depending on how deep one |
| 95 | cares to dig, require digging into C code to find, and potentially modify, how |
| 96 | the HTML is generated. |
| 97 | * Creation of one or more new skins. This does not specifically require any C |
| 98 | know-how. |
| 99 | * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator |
| 100 | |
| 101 | # Tasks Requiring Fossil Data Model Knowledge |
| 102 | |
| 103 | The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model |
| 104 | is designed to [endure for centuries](./fileformat.wiki), |
| 105 | be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md). |
| 106 |
| --- www/gsoc-ideas.md | |
| +++ www/gsoc-ideas.md | |
| @@ -1,27 +1,50 @@ | |
| 1 | # List of Projects and Tasks |
| 2 | |
| 3 | This list was made for the Fossil project's application for [Google Summer of Code](https://summerofcode.withgoogle.com/) in 2021. That application was |
| 4 | unsuccessful, but still this list is a starting point for anyone looking |
| 5 | for a place to start. We welcome newcomers, and invite developers to follow the simple |
| 6 | [procedures for contributing to Fossil](./contribute.wiki). The |
| 7 | [hacker how-to](./hacker-howto.wiki) is recommended reading. |
| 8 | |
| 9 | There are two implementations of the Fossil data model: |
| 10 | |
| 11 | * [the classic Fossil project](https://fossil-scm.org) , which is where this file is maintained and |
| 12 | which is as of 2021 how everyone interacts with Fossil objects |
| 13 | * [libfossil](https://fossil.wanderinghorse.net/r/libfossil), which is an independent project to manipulate Fossil objects from a library, or using commandline tools which are thin wrappers to the library |
| 14 | |
| 15 | As of 2021 the two implementations have an identical implementation of the |
| 16 | Fossil data model, are 100% compatible in terms of data access since they use |
| 17 | the same SQL, and are 100% binary compatible in terms of on-disk storage. |
| 18 | |
| 19 | The projects listed here are grouped by functionality - User Interface, Integration, Email, |
| 20 | etc. If you are looking for something easy to start with, then depending where |
| 21 | your interests lie, there are some small libfossil tasks and small |
| 22 | features to work on in the UI. |
| 23 | |
| 24 | # UI, Look and Feel |
| 25 | |
| 26 | Tasks for those interested in graphic/web design: |
| 27 | |
| 28 | * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d) |
| 29 | * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history) |
| 30 | * Allow diffing of Forum posts |
| 31 | * General touch-ups in the existing skins. This may, depending on how deep one |
| 32 | cares to dig, require digging into C code to find, and potentially modify, how |
| 33 | the HTML is generated. |
| 34 | * Creation of one or more new skins. This does not specifically require any C |
| 35 | know-how. |
| 36 | * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator |
| 37 | |
| 38 | # Projects Relating to Fossil Integration |
| 39 | |
| 40 | * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples |
| 41 | * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown |
| 42 | * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown) |
| 43 | * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31) |
| 44 | * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil |
| 45 | * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis |
| 46 | |
| 47 | # Adding Inbound (Receiving) Email to Fossil |
| 48 | |
| 49 | This task involves designing a new feature and working with Fossil developers to |
| 50 | see how it can be feasible in practice. |
| @@ -85,21 +108,10 @@ | |
| 108 | * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory. |
| 109 | * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing, |
| 110 | and have UI features that would improve ticketing |
| 111 | * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing |
| 112 | |
| 113 | # Tasks Requiring Fossil Data Model Knowledge |
| 114 | |
| 115 | The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model |
| 116 | is designed to [endure for centuries](./fileformat.wiki), |
| 117 | be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md). |
| 118 |
| --- www/server/windows/service.md | ||
| +++ www/server/windows/service.md | ||
| @@ -85,7 +85,24 @@ | ||
| 85 | 85 | |
| 86 | 86 | in the PowerShell console. |
| 87 | 87 | |
| 88 | 88 | Congratulations, you now have a base http accessible Fossil server running on |
| 89 | 89 | Windows. |
| 90 | + | |
| 91 | +### Removing the Windows Service | |
| 92 | + | |
| 93 | +If you want to remove the Fossil service, execute the following from an | |
| 94 | +Administrative PowerShell or Command Prompt console: | |
| 95 | + | |
| 96 | +``` | |
| 97 | +sc.exe delete fossil | |
| 98 | +``` | |
| 99 | + | |
| 100 | +If you have Powershell version 6.0 or later, you can use: | |
| 101 | + | |
| 102 | +```PowerShell | |
| 103 | +Remove-Service -Name fossil | |
| 104 | +``` | |
| 105 | + | |
| 106 | +with the same effect. | |
| 90 | 107 | |
| 91 | 108 | *[Return to the top-level Fossil server article.](../)* |
| 92 | 109 |
| --- www/server/windows/service.md | |
| +++ www/server/windows/service.md | |
| @@ -85,7 +85,24 @@ | |
| 85 | |
| 86 | in the PowerShell console. |
| 87 | |
| 88 | Congratulations, you now have a base http accessible Fossil server running on |
| 89 | Windows. |
| 90 | |
| 91 | *[Return to the top-level Fossil server article.](../)* |
| 92 |
| --- www/server/windows/service.md | |
| +++ www/server/windows/service.md | |
| @@ -85,7 +85,24 @@ | |
| 85 | |
| 86 | in the PowerShell console. |
| 87 | |
| 88 | Congratulations, you now have a base http accessible Fossil server running on |
| 89 | Windows. |
| 90 | |
| 91 | ### Removing the Windows Service |
| 92 | |
| 93 | If you want to remove the Fossil service, execute the following from an |
| 94 | Administrative PowerShell or Command Prompt console: |
| 95 | |
| 96 | ``` |
| 97 | sc.exe delete fossil |
| 98 | ``` |
| 99 | |
| 100 | If you have Powershell version 6.0 or later, you can use: |
| 101 | |
| 102 | ```PowerShell |
| 103 | Remove-Service -Name fossil |
| 104 | ``` |
| 105 | |
| 106 | with the same effect. |
| 107 | |
| 108 | *[Return to the top-level Fossil server article.](../)* |
| 109 |
| --- www/server/windows/stunnel.md | ||
| +++ www/server/windows/stunnel.md | ||
| @@ -39,10 +39,24 @@ | ||
| 39 | 39 | need to enter the following to avoid rebooting the server: |
| 40 | 40 | |
| 41 | 41 | ```PowerShell |
| 42 | 42 | Start-Service -Name fossil-secure |
| 43 | 43 | ``` |
| 44 | + | |
| 45 | +To remove the service, run the following in a Powershell or cmd console: | |
| 46 | + | |
| 47 | +``` | |
| 48 | +sc.exe delete fossil | |
| 49 | +``` | |
| 50 | + | |
| 51 | +or (in a Powershell console) | |
| 52 | + | |
| 53 | +```PowerShell | |
| 54 | +Remove-Service -Name fossil | |
| 55 | +``` | |
| 56 | + | |
| 57 | +if your version of Powershell is 6.0 or above. | |
| 44 | 58 | |
| 45 | 59 | ## Install stunnel 5.55 |
| 46 | 60 | |
| 47 | 61 | Download stunnel from the [downloads](https://www.stunnel.org/downloads.html) |
| 48 | 62 | page. Select the latest stunnel windows package (at the time of writing this is |
| 49 | 63 |
| --- www/server/windows/stunnel.md | |
| +++ www/server/windows/stunnel.md | |
| @@ -39,10 +39,24 @@ | |
| 39 | need to enter the following to avoid rebooting the server: |
| 40 | |
| 41 | ```PowerShell |
| 42 | Start-Service -Name fossil-secure |
| 43 | ``` |
| 44 | |
| 45 | ## Install stunnel 5.55 |
| 46 | |
| 47 | Download stunnel from the [downloads](https://www.stunnel.org/downloads.html) |
| 48 | page. Select the latest stunnel windows package (at the time of writing this is |
| 49 |
| --- www/server/windows/stunnel.md | |
| +++ www/server/windows/stunnel.md | |
| @@ -39,10 +39,24 @@ | |
| 39 | need to enter the following to avoid rebooting the server: |
| 40 | |
| 41 | ```PowerShell |
| 42 | Start-Service -Name fossil-secure |
| 43 | ``` |
| 44 | |
| 45 | To remove the service, run the following in a Powershell or cmd console: |
| 46 | |
| 47 | ``` |
| 48 | sc.exe delete fossil |
| 49 | ``` |
| 50 | |
| 51 | or (in a Powershell console) |
| 52 | |
| 53 | ```PowerShell |
| 54 | Remove-Service -Name fossil |
| 55 | ``` |
| 56 | |
| 57 | if your version of Powershell is 6.0 or above. |
| 58 | |
| 59 | ## Install stunnel 5.55 |
| 60 | |
| 61 | Download stunnel from the [downloads](https://www.stunnel.org/downloads.html) |
| 62 | page. Select the latest stunnel windows package (at the time of writing this is |
| 63 |