Fossil SCM
Merge from trunk
Commit
df330b709f28be40469972a64ea0bf5c65fa0efe6a847f7dc9096986f2efed86
Parent
f4a75745f9d18ca…
47 files changed
+2
-2
+17
-1
+1
-1
+3
-3
+2
-2
+3
+36
-8
+11
-5
+1
-1
+23
-17
+10
+1
-1
+9
-4
+2
-1
+4
-1
+9
-5
+6
-4
+8
-37
+1
-1
+19
-4
+45
-15
+2
-2
+33
-14
+39
-16
+93
-13
+2
+37
-13
+215
-74
+5
-4
+23
-2
+144
-30
+1
+1
-1
+1
-1
+1
-1
+1
-12
+48
-11
+1
-1
+11
-11
+1
-1
+2
-2
+1
+1
+1
-1
+29
-8
+12
+1
-1
~
skins/default/header.txt
~
src/allrepo.c
~
src/backlink.c
~
src/builtin.c
~
src/checkout.c
~
src/config.h
~
src/db.c
~
src/dispatch.c
~
src/doc.c
~
src/encode.c
~
src/export.c
~
src/fileedit.c
~
src/forum.c
~
src/import.c
~
src/info.c
~
src/interwiki.c
~
src/main.c
~
src/manifest.c
~
src/markdown.c
~
src/markdown_html.c
~
src/pikchr.c
~
src/pikchrshow.c
~
src/printf.c
~
src/shell.c
~
src/sitemap.c
~
src/skins.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/timeline.c
~
src/tkt.c
~
src/update.c
~
src/wiki.c
~
src/wiki.c
~
www/alerts.md
~
www/backup.md
~
www/embeddeddoc.wiki
~
www/fileedit-page.md
~
www/fossil-v-git.wiki
~
www/hashpolicy.wiki
~
www/mkindex.tcl
~
www/permutedindex.html
~
www/server/any/althttpd.md
~
www/server/debian/nginx.md
~
www/server/openbsd/fastcgi.md
~
www/sync.wiki
+2
-2
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -27,15 +27,15 @@ | ||
| 27 | 27 | if {[hascap oh]} { |
| 28 | 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | 30 | } |
| 31 | 31 | if {[hascap o]} { |
| 32 | - menulink /brlist Branches desktoponly | |
| 32 | + menulink /brlist Branches wideonly | |
| 33 | 33 | menulink /taglist Tags wideonly |
| 34 | 34 | } |
| 35 | 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | - menulink /forum Forum wideonly | |
| 36 | + menulink /forum Forum desktoponly | |
| 37 | 37 | } |
| 38 | 38 | if {[hascap r]} { |
| 39 | 39 | menulink /ticket Tickets wideonly |
| 40 | 40 | } |
| 41 | 41 | if {[hascap j]} { |
| 42 | 42 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -27,15 +27,15 @@ | |
| 27 | if {[hascap oh]} { |
| 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | } |
| 31 | if {[hascap o]} { |
| 32 | menulink /brlist Branches desktoponly |
| 33 | menulink /taglist Tags wideonly |
| 34 | } |
| 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | menulink /forum Forum wideonly |
| 37 | } |
| 38 | if {[hascap r]} { |
| 39 | menulink /ticket Tickets wideonly |
| 40 | } |
| 41 | if {[hascap j]} { |
| 42 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -27,15 +27,15 @@ | |
| 27 | if {[hascap oh]} { |
| 28 | if {![info exists current_checkin]} {set current_checkin tip} |
| 29 | menulink /dir?ci=$current_checkin Files desktoponly |
| 30 | } |
| 31 | if {[hascap o]} { |
| 32 | menulink /brlist Branches wideonly |
| 33 | menulink /taglist Tags wideonly |
| 34 | } |
| 35 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} { |
| 36 | menulink /forum Forum desktoponly |
| 37 | } |
| 38 | if {[hascap r]} { |
| 39 | menulink /ticket Tickets wideonly |
| 40 | } |
| 41 | if {[hascap j]} { |
| 42 |
+17
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -91,10 +91,13 @@ | ||
| 91 | 91 | ** line options supported by the extra command itself, if any |
| 92 | 92 | ** are present, are passed along verbatim. |
| 93 | 93 | ** |
| 94 | 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | 95 | ** |
| 96 | +** git export Do the "git export" command on all repositories for which | |
| 97 | +** a Git mirror has been previously established. | |
| 98 | +** | |
| 96 | 99 | ** info Run the "info" command on all repositories. |
| 97 | 100 | ** |
| 98 | 101 | ** pull Run a "pull" operation on all repositories. Only the |
| 99 | 102 | ** --verbose option is supported. |
| 100 | 103 | ** |
| @@ -236,10 +239,23 @@ | ||
| 236 | 239 | collect_argument_value(&extra, "ignore"); |
| 237 | 240 | collect_argument(&extra, "rel-paths",0); |
| 238 | 241 | useCheckouts = 1; |
| 239 | 242 | stopOnError = 0; |
| 240 | 243 | quiet = 1; |
| 244 | + }else if( strncmp(zCmd, "git", n)==0 ){ | |
| 245 | + if( g.argc<4 ){ | |
| 246 | + usage("git (export|status)"); | |
| 247 | + }else{ | |
| 248 | + int n3 = (int)strlen(g.argv[3]); | |
| 249 | + if( strncmp(g.argv[3], "export", n3)==0 ){ | |
| 250 | + zCmd = "git export --if-mirrored -R"; | |
| 251 | + }else if( strncmp(g.argv[3], "status", n3)==0 ){ | |
| 252 | + zCmd = "git status -R"; | |
| 253 | + }else{ | |
| 254 | + usage("git (export|status)"); | |
| 255 | + } | |
| 256 | + } | |
| 241 | 257 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 242 | 258 | zCmd = "push -autourl -R"; |
| 243 | 259 | collect_argument(&extra, "verbose","v"); |
| 244 | 260 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 245 | 261 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | ||
| 358 | 374 | zCmd = "cache -R"; |
| 359 | 375 | showLabel = 1; |
| 360 | 376 | collect_argv(&extra, 3); |
| 361 | 377 | }else{ |
| 362 | 378 | fossil_fatal("\"all\" subcommand should be one of: " |
| 363 | - "add cache changes clean dbstat extras fts-config ignore " | |
| 379 | + "add cache changes clean dbstat extras fts-config git ignore " | |
| 364 | 380 | "info list ls pull push rebuild server setting sync ui unset"); |
| 365 | 381 | } |
| 366 | 382 | verify_all_options(); |
| 367 | 383 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 368 | 384 | if( useCheckouts ){ |
| 369 | 385 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -91,10 +91,13 @@ | |
| 91 | ** line options supported by the extra command itself, if any |
| 92 | ** are present, are passed along verbatim. |
| 93 | ** |
| 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | ** |
| 96 | ** info Run the "info" command on all repositories. |
| 97 | ** |
| 98 | ** pull Run a "pull" operation on all repositories. Only the |
| 99 | ** --verbose option is supported. |
| 100 | ** |
| @@ -236,10 +239,23 @@ | |
| 236 | collect_argument_value(&extra, "ignore"); |
| 237 | collect_argument(&extra, "rel-paths",0); |
| 238 | useCheckouts = 1; |
| 239 | stopOnError = 0; |
| 240 | quiet = 1; |
| 241 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 242 | zCmd = "push -autourl -R"; |
| 243 | collect_argument(&extra, "verbose","v"); |
| 244 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 245 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | |
| 358 | zCmd = "cache -R"; |
| 359 | showLabel = 1; |
| 360 | collect_argv(&extra, 3); |
| 361 | }else{ |
| 362 | fossil_fatal("\"all\" subcommand should be one of: " |
| 363 | "add cache changes clean dbstat extras fts-config ignore " |
| 364 | "info list ls pull push rebuild server setting sync ui unset"); |
| 365 | } |
| 366 | verify_all_options(); |
| 367 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 368 | if( useCheckouts ){ |
| 369 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -91,10 +91,13 @@ | |
| 91 | ** line options supported by the extra command itself, if any |
| 92 | ** are present, are passed along verbatim. |
| 93 | ** |
| 94 | ** fts-config Run the "fts-config" command on all repositories. |
| 95 | ** |
| 96 | ** git export Do the "git export" command on all repositories for which |
| 97 | ** a Git mirror has been previously established. |
| 98 | ** |
| 99 | ** info Run the "info" command on all repositories. |
| 100 | ** |
| 101 | ** pull Run a "pull" operation on all repositories. Only the |
| 102 | ** --verbose option is supported. |
| 103 | ** |
| @@ -236,10 +239,23 @@ | |
| 239 | collect_argument_value(&extra, "ignore"); |
| 240 | collect_argument(&extra, "rel-paths",0); |
| 241 | useCheckouts = 1; |
| 242 | stopOnError = 0; |
| 243 | quiet = 1; |
| 244 | }else if( strncmp(zCmd, "git", n)==0 ){ |
| 245 | if( g.argc<4 ){ |
| 246 | usage("git (export|status)"); |
| 247 | }else{ |
| 248 | int n3 = (int)strlen(g.argv[3]); |
| 249 | if( strncmp(g.argv[3], "export", n3)==0 ){ |
| 250 | zCmd = "git export --if-mirrored -R"; |
| 251 | }else if( strncmp(g.argv[3], "status", n3)==0 ){ |
| 252 | zCmd = "git status -R"; |
| 253 | }else{ |
| 254 | usage("git (export|status)"); |
| 255 | } |
| 256 | } |
| 257 | }else if( strncmp(zCmd, "push", n)==0 ){ |
| 258 | zCmd = "push -autourl -R"; |
| 259 | collect_argument(&extra, "verbose","v"); |
| 260 | }else if( strncmp(zCmd, "pull", n)==0 ){ |
| 261 | zCmd = "pull -autourl -R"; |
| @@ -358,11 +374,11 @@ | |
| 374 | zCmd = "cache -R"; |
| 375 | showLabel = 1; |
| 376 | collect_argv(&extra, 3); |
| 377 | }else{ |
| 378 | fossil_fatal("\"all\" subcommand should be one of: " |
| 379 | "add cache changes clean dbstat extras fts-config git ignore " |
| 380 | "info list ls pull push rebuild server setting sync ui unset"); |
| 381 | } |
| 382 | verify_all_options(); |
| 383 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 384 | if( useCheckouts ){ |
| 385 |
+1
-1
| --- src/backlink.c | ||
| +++ src/backlink.c | ||
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | int srcid = db_column_int(&q, 2); |
| 131 | 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | 133 | switch( srctype ){ |
| 134 | 134 | case BKLNK_COMMENT: { |
| 135 | - @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a> | |
| 135 | + @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a> | |
| 136 | 136 | break; |
| 137 | 137 | } |
| 138 | 138 | case BKLNK_TICKET: { |
| 139 | 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | 140 | break; |
| 141 | 141 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | int srcid = db_column_int(&q, 2); |
| 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | switch( srctype ){ |
| 134 | case BKLNK_COMMENT: { |
| 135 | @ <td><a href="%R/info?name=rid:%d(srcid)">comment-%d(srcid)</a> |
| 136 | break; |
| 137 | } |
| 138 | case BKLNK_TICKET: { |
| 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | break; |
| 141 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | int srcid = db_column_int(&q, 2); |
| 131 | const char *zMtime = db_column_text(&q, 3); |
| 132 | @ <tr><td><a href="%R/info/%h(zTarget)">%h(zTarget)</a> |
| 133 | switch( srctype ){ |
| 134 | case BKLNK_COMMENT: { |
| 135 | @ <td><a href="%R/info?name=rid:%d(srcid)">checkin-%d(srcid)</a> |
| 136 | break; |
| 137 | } |
| 138 | case BKLNK_TICKET: { |
| 139 | @ <td><a href="%R/info?name=rid:%d(srcid)">ticket-%d(srcid)</a> |
| 140 | break; |
| 141 |
+3
-3
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -843,18 +843,18 @@ | ||
| 843 | 843 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 844 | 844 | ** need. |
| 845 | 845 | ** |
| 846 | 846 | ** Example usage: |
| 847 | 847 | ** |
| 848 | -** builtin_fossil_js_bundle_or("dom", "fetch", 0); | |
| 848 | +** builtin_fossil_js_bundle_or("dom", "fetch", NULL); | |
| 849 | 849 | ** |
| 850 | 850 | ** In bundled mode, that will (the first time it is called) emit all |
| 851 | 851 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 852 | 852 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 853 | 853 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 854 | 854 | */ |
| 855 | -void builtin_fossil_js_bundle_or( const char * zApi, ... ) { | |
| 855 | +NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) { | |
| 856 | 856 | static int bundled = 0; |
| 857 | 857 | const char *zArg; |
| 858 | 858 | va_list vargs; |
| 859 | 859 | |
| 860 | 860 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +864,12 @@ | ||
| 864 | 864 | builtin_fulfill_js_requests(); |
| 865 | 865 | } |
| 866 | 866 | return; |
| 867 | 867 | } |
| 868 | 868 | va_start(vargs,zApi); |
| 869 | - for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){ | |
| 869 | + for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){ | |
| 870 | 870 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 871 | 871 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 872 | 872 | } |
| 873 | 873 | } |
| 874 | 874 | va_end(vargs); |
| 875 | 875 | } |
| 876 | 876 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -843,18 +843,18 @@ | |
| 843 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 844 | ** need. |
| 845 | ** |
| 846 | ** Example usage: |
| 847 | ** |
| 848 | ** builtin_fossil_js_bundle_or("dom", "fetch", 0); |
| 849 | ** |
| 850 | ** In bundled mode, that will (the first time it is called) emit all |
| 851 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 852 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 853 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 854 | */ |
| 855 | void builtin_fossil_js_bundle_or( const char * zApi, ... ) { |
| 856 | static int bundled = 0; |
| 857 | const char *zArg; |
| 858 | va_list vargs; |
| 859 | |
| 860 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +864,12 @@ | |
| 864 | builtin_fulfill_js_requests(); |
| 865 | } |
| 866 | return; |
| 867 | } |
| 868 | va_start(vargs,zApi); |
| 869 | for( zArg = zApi; zArg!=0; (zArg = va_arg (vargs, const char *))){ |
| 870 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 871 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 872 | } |
| 873 | } |
| 874 | va_end(vargs); |
| 875 | } |
| 876 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -843,18 +843,18 @@ | |
| 843 | ** builtin_request_js()) with any other app-/page-specific JS it may |
| 844 | ** need. |
| 845 | ** |
| 846 | ** Example usage: |
| 847 | ** |
| 848 | ** builtin_fossil_js_bundle_or("dom", "fetch", NULL); |
| 849 | ** |
| 850 | ** In bundled mode, that will (the first time it is called) emit all |
| 851 | ** builtin fossil JS APIs and "fulfill" the queue immediately. In |
| 852 | ** non-bundled mode it will queue up the "dom" and "fetch" APIs to be |
| 853 | ** emitted the next time builtin_fulfill_js_requests() is called. |
| 854 | */ |
| 855 | NULL_SENTINEL void builtin_fossil_js_bundle_or( const char * zApi, ... ) { |
| 856 | static int bundled = 0; |
| 857 | const char *zArg; |
| 858 | va_list vargs; |
| 859 | |
| 860 | if(JS_BUNDLED == builtin_get_js_delivery_mode()){ |
| @@ -864,12 +864,12 @@ | |
| 864 | builtin_fulfill_js_requests(); |
| 865 | } |
| 866 | return; |
| 867 | } |
| 868 | va_start(vargs,zApi); |
| 869 | for( zArg = zApi; zArg!=NULL; (zArg = va_arg (vargs, const char *))){ |
| 870 | if(0==builtin_emit_fossil_js_once(zArg)){ |
| 871 | fossil_fatal("Unknown fossil JS module: %s\n", zArg); |
| 872 | } |
| 873 | } |
| 874 | va_end(vargs); |
| 875 | } |
| 876 |
+2
-2
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -49,13 +49,13 @@ | ||
| 49 | 49 | void uncheckout(int vid){ |
| 50 | 50 | char *zPwd; |
| 51 | 51 | if( vid<=0 ) return; |
| 52 | 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | 53 | file_dirname_sql_function, 0, 0); |
| 54 | - sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0, | |
| 54 | + sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0, | |
| 55 | 55 | file_delete_sql_function, 0, 0); |
| 56 | - sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, | |
| 56 | + sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, | |
| 57 | 57 | file_rmdir_sql_function, 0, 0); |
| 58 | 58 | db_multi_exec( |
| 59 | 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | 60 | filename_collation() |
| 61 | 61 | ); |
| 62 | 62 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -49,13 +49,13 @@ | |
| 49 | void uncheckout(int vid){ |
| 50 | char *zPwd; |
| 51 | if( vid<=0 ) return; |
| 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | file_dirname_sql_function, 0, 0); |
| 54 | sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0, |
| 55 | file_delete_sql_function, 0, 0); |
| 56 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, |
| 57 | file_rmdir_sql_function, 0, 0); |
| 58 | db_multi_exec( |
| 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | filename_collation() |
| 61 | ); |
| 62 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -49,13 +49,13 @@ | |
| 49 | void uncheckout(int vid){ |
| 50 | char *zPwd; |
| 51 | if( vid<=0 ) return; |
| 52 | sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0, |
| 53 | file_dirname_sql_function, 0, 0); |
| 54 | sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0, |
| 55 | file_delete_sql_function, 0, 0); |
| 56 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| 57 | file_rmdir_sql_function, 0, 0); |
| 58 | db_multi_exec( |
| 59 | "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID", |
| 60 | filename_collation() |
| 61 | ); |
| 62 |
+3
| --- src/config.h | ||
| +++ src/config.h | ||
| @@ -245,14 +245,17 @@ | ||
| 245 | 245 | /* |
| 246 | 246 | ** A marker for functions that never return. |
| 247 | 247 | */ |
| 248 | 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | +# define NULL_SENTINEL __attribute__((sentinel)) | |
| 250 | 251 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 251 | 252 | # define NORETURN __declspec(noreturn) |
| 253 | +# define NULL_SENTINEL | |
| 252 | 254 | #else |
| 253 | 255 | # define NORETURN |
| 256 | +# define NULL_SENTINEL | |
| 254 | 257 | #endif |
| 255 | 258 | |
| 256 | 259 | /* |
| 257 | 260 | ** Number of elements in an array |
| 258 | 261 | */ |
| 259 | 262 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -245,14 +245,17 @@ | |
| 245 | /* |
| 246 | ** A marker for functions that never return. |
| 247 | */ |
| 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 251 | # define NORETURN __declspec(noreturn) |
| 252 | #else |
| 253 | # define NORETURN |
| 254 | #endif |
| 255 | |
| 256 | /* |
| 257 | ** Number of elements in an array |
| 258 | */ |
| 259 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -245,14 +245,17 @@ | |
| 245 | /* |
| 246 | ** A marker for functions that never return. |
| 247 | */ |
| 248 | #if defined(__GNUC__) || defined(__clang__) |
| 249 | # define NORETURN __attribute__((__noreturn__)) |
| 250 | # define NULL_SENTINEL __attribute__((sentinel)) |
| 251 | #elif defined(_MSC_VER) && (_MSC_VER >= 1310) |
| 252 | # define NORETURN __declspec(noreturn) |
| 253 | # define NULL_SENTINEL |
| 254 | #else |
| 255 | # define NORETURN |
| 256 | # define NULL_SENTINEL |
| 257 | #endif |
| 258 | |
| 259 | /* |
| 260 | ** Number of elements in an array |
| 261 | */ |
| 262 |
M
src/db.c
+36
-8
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1617,10 +1617,15 @@ | ||
| 1617 | 1617 | ); |
| 1618 | 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | 1620 | } |
| 1621 | 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | + sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); | |
| 1623 | + sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc); | |
| 1624 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc); | |
| 1625 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc); | |
| 1626 | + sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); | |
| 1622 | 1627 | sqlite3_busy_timeout(db, 15000); |
| 1623 | 1628 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1624 | 1629 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1625 | 1630 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1626 | 1631 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | ||
| 1633 | 1638 | ); |
| 1634 | 1639 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1635 | 1640 | db_add_aux_functions(db); |
| 1636 | 1641 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1637 | 1642 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1638 | - sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); | |
| 1639 | 1643 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1640 | 1644 | return db; |
| 1641 | 1645 | } |
| 1642 | 1646 | |
| 1643 | 1647 | |
| @@ -2776,11 +2780,14 @@ | ||
| 2776 | 2780 | } |
| 2777 | 2781 | if( zArg[0]=='-' ) return 0; |
| 2778 | 2782 | if( m & SQLITE_TRACE_PROFILE ){ |
| 2779 | 2783 | sqlite3_int64 nNano = *(sqlite3_int64*)pX; |
| 2780 | 2784 | double rMillisec = 0.000001 * nNano; |
| 2781 | - sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms */\n", rMillisec); | |
| 2785 | + int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0); | |
| 2786 | + int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1); | |
| 2787 | + sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n", | |
| 2788 | + rMillisec, nRun, nVmStep); | |
| 2782 | 2789 | }else{ |
| 2783 | 2790 | zEnd[0] = '\n'; |
| 2784 | 2791 | zEnd[1] = 0; |
| 2785 | 2792 | } |
| 2786 | 2793 | zSql = sqlite3_expanded_sql(pStmt); |
| @@ -3089,16 +3096,30 @@ | ||
| 3089 | 3096 | */ |
| 3090 | 3097 | char *db_get(const char *zName, const char *zDefault){ |
| 3091 | 3098 | char *z = 0; |
| 3092 | 3099 | const Setting *pSetting = db_find_setting(zName, 0); |
| 3093 | 3100 | if( g.repositoryOpen ){ |
| 3094 | - z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName); | |
| 3101 | + static Stmt q1; | |
| 3102 | + const char *zRes; | |
| 3103 | + db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n"); | |
| 3104 | + db_bind_text(&q1, "$n", zName); | |
| 3105 | + if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){ | |
| 3106 | + z = fossil_strdup(zRes); | |
| 3107 | + } | |
| 3108 | + db_reset(&q1); | |
| 3095 | 3109 | } |
| 3096 | 3110 | if( z==0 && g.zConfigDbName ){ |
| 3111 | + static Stmt q2; | |
| 3112 | + const char *zRes; | |
| 3097 | 3113 | db_swap_connections(); |
| 3098 | - z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName); | |
| 3114 | + db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); | |
| 3099 | 3115 | db_swap_connections(); |
| 3116 | + db_bind_text(&q2, "$n", zName); | |
| 3117 | + if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){ | |
| 3118 | + z = fossil_strdup(zRes); | |
| 3119 | + } | |
| 3120 | + db_reset(&q2); | |
| 3100 | 3121 | } |
| 3101 | 3122 | if( pSetting!=0 && pSetting->versionable ){ |
| 3102 | 3123 | /* This is a versionable setting, try and get the info from a |
| 3103 | 3124 | ** checked out file */ |
| 3104 | 3125 | char * zZ = z; |
| @@ -3174,24 +3195,31 @@ | ||
| 3174 | 3195 | } |
| 3175 | 3196 | int db_get_int(const char *zName, int dflt){ |
| 3176 | 3197 | int v = dflt; |
| 3177 | 3198 | int rc; |
| 3178 | 3199 | if( g.repositoryOpen ){ |
| 3179 | - Stmt q; | |
| 3180 | - db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName); | |
| 3200 | + static Stmt q; | |
| 3201 | + db_static_prepare(&q, "SELECT value FROM config WHERE name=$n"); | |
| 3202 | + db_bind_text(&q, "$n", zName); | |
| 3181 | 3203 | rc = db_step(&q); |
| 3182 | 3204 | if( rc==SQLITE_ROW ){ |
| 3183 | 3205 | v = db_column_int(&q, 0); |
| 3184 | 3206 | } |
| 3185 | - db_finalize(&q); | |
| 3207 | + db_reset(&q); | |
| 3186 | 3208 | }else{ |
| 3187 | 3209 | rc = SQLITE_DONE; |
| 3188 | 3210 | } |
| 3189 | 3211 | if( rc==SQLITE_DONE && g.zConfigDbName ){ |
| 3212 | + static Stmt q2; | |
| 3190 | 3213 | db_swap_connections(); |
| 3191 | - v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName); | |
| 3214 | + db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); | |
| 3192 | 3215 | db_swap_connections(); |
| 3216 | + db_bind_text(&q2, "$n", zName); | |
| 3217 | + if( db_step(&q2)==SQLITE_ROW ){ | |
| 3218 | + v = db_column_int(&q2, 0); | |
| 3219 | + } | |
| 3220 | + db_reset(&q2); | |
| 3193 | 3221 | } |
| 3194 | 3222 | return v; |
| 3195 | 3223 | } |
| 3196 | 3224 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 3197 | 3225 | db_assert_protection_off_or_not_sensitive(zName); |
| 3198 | 3226 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1617,10 +1617,15 @@ | |
| 1617 | ); |
| 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | } |
| 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | sqlite3_busy_timeout(db, 15000); |
| 1623 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1624 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1625 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1626 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | |
| 1633 | ); |
| 1634 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1635 | db_add_aux_functions(db); |
| 1636 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1637 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1638 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1639 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1640 | return db; |
| 1641 | } |
| 1642 | |
| 1643 | |
| @@ -2776,11 +2780,14 @@ | |
| 2776 | } |
| 2777 | if( zArg[0]=='-' ) return 0; |
| 2778 | if( m & SQLITE_TRACE_PROFILE ){ |
| 2779 | sqlite3_int64 nNano = *(sqlite3_int64*)pX; |
| 2780 | double rMillisec = 0.000001 * nNano; |
| 2781 | sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms */\n", rMillisec); |
| 2782 | }else{ |
| 2783 | zEnd[0] = '\n'; |
| 2784 | zEnd[1] = 0; |
| 2785 | } |
| 2786 | zSql = sqlite3_expanded_sql(pStmt); |
| @@ -3089,16 +3096,30 @@ | |
| 3089 | */ |
| 3090 | char *db_get(const char *zName, const char *zDefault){ |
| 3091 | char *z = 0; |
| 3092 | const Setting *pSetting = db_find_setting(zName, 0); |
| 3093 | if( g.repositoryOpen ){ |
| 3094 | z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName); |
| 3095 | } |
| 3096 | if( z==0 && g.zConfigDbName ){ |
| 3097 | db_swap_connections(); |
| 3098 | z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName); |
| 3099 | db_swap_connections(); |
| 3100 | } |
| 3101 | if( pSetting!=0 && pSetting->versionable ){ |
| 3102 | /* This is a versionable setting, try and get the info from a |
| 3103 | ** checked out file */ |
| 3104 | char * zZ = z; |
| @@ -3174,24 +3195,31 @@ | |
| 3174 | } |
| 3175 | int db_get_int(const char *zName, int dflt){ |
| 3176 | int v = dflt; |
| 3177 | int rc; |
| 3178 | if( g.repositoryOpen ){ |
| 3179 | Stmt q; |
| 3180 | db_prepare(&q, "SELECT value FROM config WHERE name=%Q", zName); |
| 3181 | rc = db_step(&q); |
| 3182 | if( rc==SQLITE_ROW ){ |
| 3183 | v = db_column_int(&q, 0); |
| 3184 | } |
| 3185 | db_finalize(&q); |
| 3186 | }else{ |
| 3187 | rc = SQLITE_DONE; |
| 3188 | } |
| 3189 | if( rc==SQLITE_DONE && g.zConfigDbName ){ |
| 3190 | db_swap_connections(); |
| 3191 | v = db_int(dflt, "SELECT value FROM global_config WHERE name=%Q", zName); |
| 3192 | db_swap_connections(); |
| 3193 | } |
| 3194 | return v; |
| 3195 | } |
| 3196 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 3197 | db_assert_protection_off_or_not_sensitive(zName); |
| 3198 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1617,10 +1617,15 @@ | |
| 1617 | ); |
| 1618 | if( rc!=SQLITE_OK ){ |
| 1619 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1620 | } |
| 1621 | db_maybe_set_encryption_key(db, zDbName); |
| 1622 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1623 | sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc); |
| 1624 | sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc); |
| 1625 | sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc); |
| 1626 | sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc); |
| 1627 | sqlite3_busy_timeout(db, 15000); |
| 1628 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1629 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1630 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1631 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| @@ -1633,11 +1638,10 @@ | |
| 1638 | ); |
| 1639 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1640 | db_add_aux_functions(db); |
| 1641 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1642 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1643 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1644 | return db; |
| 1645 | } |
| 1646 | |
| 1647 | |
| @@ -2776,11 +2780,14 @@ | |
| 2780 | } |
| 2781 | if( zArg[0]=='-' ) return 0; |
| 2782 | if( m & SQLITE_TRACE_PROFILE ){ |
| 2783 | sqlite3_int64 nNano = *(sqlite3_int64*)pX; |
| 2784 | double rMillisec = 0.000001 * nNano; |
| 2785 | int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0); |
| 2786 | int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1); |
| 2787 | sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n", |
| 2788 | rMillisec, nRun, nVmStep); |
| 2789 | }else{ |
| 2790 | zEnd[0] = '\n'; |
| 2791 | zEnd[1] = 0; |
| 2792 | } |
| 2793 | zSql = sqlite3_expanded_sql(pStmt); |
| @@ -3089,16 +3096,30 @@ | |
| 3096 | */ |
| 3097 | char *db_get(const char *zName, const char *zDefault){ |
| 3098 | char *z = 0; |
| 3099 | const Setting *pSetting = db_find_setting(zName, 0); |
| 3100 | if( g.repositoryOpen ){ |
| 3101 | static Stmt q1; |
| 3102 | const char *zRes; |
| 3103 | db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n"); |
| 3104 | db_bind_text(&q1, "$n", zName); |
| 3105 | if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){ |
| 3106 | z = fossil_strdup(zRes); |
| 3107 | } |
| 3108 | db_reset(&q1); |
| 3109 | } |
| 3110 | if( z==0 && g.zConfigDbName ){ |
| 3111 | static Stmt q2; |
| 3112 | const char *zRes; |
| 3113 | db_swap_connections(); |
| 3114 | db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); |
| 3115 | db_swap_connections(); |
| 3116 | db_bind_text(&q2, "$n", zName); |
| 3117 | if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){ |
| 3118 | z = fossil_strdup(zRes); |
| 3119 | } |
| 3120 | db_reset(&q2); |
| 3121 | } |
| 3122 | if( pSetting!=0 && pSetting->versionable ){ |
| 3123 | /* This is a versionable setting, try and get the info from a |
| 3124 | ** checked out file */ |
| 3125 | char * zZ = z; |
| @@ -3174,24 +3195,31 @@ | |
| 3195 | } |
| 3196 | int db_get_int(const char *zName, int dflt){ |
| 3197 | int v = dflt; |
| 3198 | int rc; |
| 3199 | if( g.repositoryOpen ){ |
| 3200 | static Stmt q; |
| 3201 | db_static_prepare(&q, "SELECT value FROM config WHERE name=$n"); |
| 3202 | db_bind_text(&q, "$n", zName); |
| 3203 | rc = db_step(&q); |
| 3204 | if( rc==SQLITE_ROW ){ |
| 3205 | v = db_column_int(&q, 0); |
| 3206 | } |
| 3207 | db_reset(&q); |
| 3208 | }else{ |
| 3209 | rc = SQLITE_DONE; |
| 3210 | } |
| 3211 | if( rc==SQLITE_DONE && g.zConfigDbName ){ |
| 3212 | static Stmt q2; |
| 3213 | db_swap_connections(); |
| 3214 | db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n"); |
| 3215 | db_swap_connections(); |
| 3216 | db_bind_text(&q2, "$n", zName); |
| 3217 | if( db_step(&q2)==SQLITE_ROW ){ |
| 3218 | v = db_column_int(&q2, 0); |
| 3219 | } |
| 3220 | db_reset(&q2); |
| 3221 | } |
| 3222 | return v; |
| 3223 | } |
| 3224 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 3225 | db_assert_protection_off_or_not_sensitive(zName); |
| 3226 |
+11
-5
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -1008,13 +1008,15 @@ | ||
| 1008 | 1008 | ** -w|--www List all web pages |
| 1009 | 1009 | ** |
| 1010 | 1010 | ** These options can be used when TOPIC is present: |
| 1011 | 1011 | ** |
| 1012 | 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | +** -c|--commands Restrict TOPIC search to commands | |
| 1013 | 1014 | */ |
| 1014 | 1015 | void help_cmd(void){ |
| 1015 | 1016 | int rc; |
| 1017 | + int mask = CMDFLAG_ANY; | |
| 1016 | 1018 | int isPage = 0; |
| 1017 | 1019 | const char *z; |
| 1018 | 1020 | const char *zCmdOrPage; |
| 1019 | 1021 | const CmdOrPage *pCmd = 0; |
| 1020 | 1022 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | ||
| 1056 | 1058 | } |
| 1057 | 1059 | useHtml = find_option("html","h",0)!=0; |
| 1058 | 1060 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1059 | 1061 | if(isPage){ |
| 1060 | 1062 | zCmdOrPage = "page"; |
| 1063 | + }else if( find_option("commands","c",0)!=0 ){ | |
| 1064 | + mask = CMDFLAG_COMMAND; | |
| 1065 | + zCmdOrPage = "command"; | |
| 1061 | 1066 | }else{ |
| 1062 | 1067 | zCmdOrPage = "command or setting"; |
| 1063 | 1068 | } |
| 1064 | - rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); | |
| 1069 | + rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); | |
| 1065 | 1070 | if( rc ){ |
| 1066 | 1071 | int i, n; |
| 1067 | 1072 | const char *az[5]; |
| 1068 | 1073 | if( rc==1 ){ |
| 1069 | 1074 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1070 | 1075 | }else{ |
| 1071 | 1076 | fossil_print("ambiguous %s prefix: %s\n", |
| 1072 | 1077 | zCmdOrPage, g.argv[2]); |
| 1073 | 1078 | } |
| 1074 | - fossil_print("Did you mean one of:\n"); | |
| 1079 | + fossil_print("Did you mean one of these TOPICs:\n"); | |
| 1075 | 1080 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1076 | 1081 | for(i=0; i<n; i++){ |
| 1077 | 1082 | fossil_print(" * %s\n", az[i]); |
| 1078 | 1083 | } |
| 1079 | 1084 | fossil_print("Also consider using:\n"); |
| 1080 | - fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1081 | - fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1082 | - fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1085 | + fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); | |
| 1086 | + fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1087 | + fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1088 | + fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1083 | 1089 | fossil_exit(1); |
| 1084 | 1090 | } |
| 1085 | 1091 | z = pCmd->zHelp; |
| 1086 | 1092 | if( z==0 ){ |
| 1087 | 1093 | fossil_fatal("no help available for the %s %s", |
| 1088 | 1094 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1008,13 +1008,15 @@ | |
| 1008 | ** -w|--www List all web pages |
| 1009 | ** |
| 1010 | ** These options can be used when TOPIC is present: |
| 1011 | ** |
| 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | */ |
| 1014 | void help_cmd(void){ |
| 1015 | int rc; |
| 1016 | int isPage = 0; |
| 1017 | const char *z; |
| 1018 | const char *zCmdOrPage; |
| 1019 | const CmdOrPage *pCmd = 0; |
| 1020 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | |
| 1056 | } |
| 1057 | useHtml = find_option("html","h",0)!=0; |
| 1058 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1059 | if(isPage){ |
| 1060 | zCmdOrPage = "page"; |
| 1061 | }else{ |
| 1062 | zCmdOrPage = "command or setting"; |
| 1063 | } |
| 1064 | rc = dispatch_name_search(g.argv[2], CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 1065 | if( rc ){ |
| 1066 | int i, n; |
| 1067 | const char *az[5]; |
| 1068 | if( rc==1 ){ |
| 1069 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1070 | }else{ |
| 1071 | fossil_print("ambiguous %s prefix: %s\n", |
| 1072 | zCmdOrPage, g.argv[2]); |
| 1073 | } |
| 1074 | fossil_print("Did you mean one of:\n"); |
| 1075 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1076 | for(i=0; i<n; i++){ |
| 1077 | fossil_print(" * %s\n", az[i]); |
| 1078 | } |
| 1079 | fossil_print("Also consider using:\n"); |
| 1080 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1081 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1082 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1083 | fossil_exit(1); |
| 1084 | } |
| 1085 | z = pCmd->zHelp; |
| 1086 | if( z==0 ){ |
| 1087 | fossil_fatal("no help available for the %s %s", |
| 1088 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -1008,13 +1008,15 @@ | |
| 1008 | ** -w|--www List all web pages |
| 1009 | ** |
| 1010 | ** These options can be used when TOPIC is present: |
| 1011 | ** |
| 1012 | ** -h|--html Format output as HTML rather than plain text |
| 1013 | ** -c|--commands Restrict TOPIC search to commands |
| 1014 | */ |
| 1015 | void help_cmd(void){ |
| 1016 | int rc; |
| 1017 | int mask = CMDFLAG_ANY; |
| 1018 | int isPage = 0; |
| 1019 | const char *z; |
| 1020 | const char *zCmdOrPage; |
| 1021 | const CmdOrPage *pCmd = 0; |
| 1022 | int useHtml = 0; |
| @@ -1056,32 +1058,36 @@ | |
| 1058 | } |
| 1059 | useHtml = find_option("html","h",0)!=0; |
| 1060 | isPage = ('/' == *g.argv[2]) ? 1 : 0; |
| 1061 | if(isPage){ |
| 1062 | zCmdOrPage = "page"; |
| 1063 | }else if( find_option("commands","c",0)!=0 ){ |
| 1064 | mask = CMDFLAG_COMMAND; |
| 1065 | zCmdOrPage = "command"; |
| 1066 | }else{ |
| 1067 | zCmdOrPage = "command or setting"; |
| 1068 | } |
| 1069 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1070 | if( rc ){ |
| 1071 | int i, n; |
| 1072 | const char *az[5]; |
| 1073 | if( rc==1 ){ |
| 1074 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1075 | }else{ |
| 1076 | fossil_print("ambiguous %s prefix: %s\n", |
| 1077 | zCmdOrPage, g.argv[2]); |
| 1078 | } |
| 1079 | fossil_print("Did you mean one of these TOPICs:\n"); |
| 1080 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1081 | for(i=0; i<n; i++){ |
| 1082 | fossil_print(" * %s\n", az[i]); |
| 1083 | } |
| 1084 | fossil_print("Also consider using:\n"); |
| 1085 | fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); |
| 1086 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1087 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1088 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1089 | fossil_exit(1); |
| 1090 | } |
| 1091 | z = pCmd->zHelp; |
| 1092 | if( z==0 ){ |
| 1093 | fossil_fatal("no help available for the %s %s", |
| 1094 |
+1
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -408,11 +408,11 @@ | ||
| 408 | 408 | ** no-ops. |
| 409 | 409 | */ |
| 410 | 410 | void document_emit_js(void){ |
| 411 | 411 | static int once = 0; |
| 412 | 412 | if(0==once++){ |
| 413 | - builtin_fossil_js_bundle_or("pikchr", 0); | |
| 413 | + builtin_fossil_js_bundle_or("pikchr", NULL); | |
| 414 | 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | 415 | CX("window.addEventListener('load', " |
| 416 | 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | 417 | "false);\n"); |
| 418 | 418 | style_script_end(); |
| 419 | 419 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -408,11 +408,11 @@ | |
| 408 | ** no-ops. |
| 409 | */ |
| 410 | void document_emit_js(void){ |
| 411 | static int once = 0; |
| 412 | if(0==once++){ |
| 413 | builtin_fossil_js_bundle_or("pikchr", 0); |
| 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | CX("window.addEventListener('load', " |
| 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | "false);\n"); |
| 418 | style_script_end(); |
| 419 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -408,11 +408,11 @@ | |
| 408 | ** no-ops. |
| 409 | */ |
| 410 | void document_emit_js(void){ |
| 411 | static int once = 0; |
| 412 | if(0==once++){ |
| 413 | builtin_fossil_js_bundle_or("pikchr", NULL); |
| 414 | style_script_begin(__FILE__,__LINE__); |
| 415 | CX("window.addEventListener('load', " |
| 416 | "()=>window.fossil.pikchr.addSrcView(), " |
| 417 | "false);\n"); |
| 418 | style_script_end(); |
| 419 |
+23
-17
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -26,31 +26,38 @@ | ||
| 26 | 26 | ** to a new string obtained from malloc(). |
| 27 | 27 | ** |
| 28 | 28 | ** We also encode " as " and ' as ' so they can appear as an argument |
| 29 | 29 | ** to markup. |
| 30 | 30 | */ |
| 31 | -char *htmlize(const char *zIn, int n){ | |
| 32 | - int c; | |
| 31 | +char *htmlize(const char *z, int n){ | |
| 32 | + unsigned char c; | |
| 33 | 33 | int i = 0; |
| 34 | 34 | int count = 0; |
| 35 | - char *zOut; | |
| 36 | - | |
| 37 | - if( n<0 ) n = strlen(zIn); | |
| 38 | - while( i<n && (c = zIn[i])!=0 ){ | |
| 39 | - switch( c ){ | |
| 40 | - case '<': count += 4; break; | |
| 41 | - case '>': count += 4; break; | |
| 42 | - case '&': count += 5; break; | |
| 43 | - case '"': count += 6; break; | |
| 44 | - case '\'': count += 5; break; | |
| 45 | - default: count++; break; | |
| 35 | + unsigned char *zOut; | |
| 36 | + const unsigned char *zIn = (const unsigned char*)z; | |
| 37 | + | |
| 38 | + if( n<0 ) n = strlen(z); | |
| 39 | + while( i<n ){ | |
| 40 | + switch( zIn[i] ){ | |
| 41 | + case '<': count += 3; break; | |
| 42 | + case '>': count += 3; break; | |
| 43 | + case '&': count += 4; break; | |
| 44 | + case '"': count += 5; break; | |
| 45 | + case '\'': count += 4; break; | |
| 46 | + case 0: n = i; break; | |
| 46 | 47 | } |
| 47 | 48 | i++; |
| 48 | 49 | } |
| 49 | 50 | i = 0; |
| 50 | - zOut = fossil_malloc( count+1 ); | |
| 51 | - while( n-->0 && (c = *zIn)!=0 ){ | |
| 51 | + zOut = fossil_malloc( count+n+1 ); | |
| 52 | + if( count==0 ){ | |
| 53 | + memcpy(zOut, zIn, n); | |
| 54 | + zOut[n] = 0; | |
| 55 | + return (char*)zOut; | |
| 56 | + } | |
| 57 | + while( n-->0 ){ | |
| 58 | + c = *(zIn++); | |
| 52 | 59 | switch( c ){ |
| 53 | 60 | case '<': |
| 54 | 61 | zOut[i++] = '&'; |
| 55 | 62 | zOut[i++] = 'l'; |
| 56 | 63 | zOut[i++] = 't'; |
| @@ -86,14 +93,13 @@ | ||
| 86 | 93 | break; |
| 87 | 94 | default: |
| 88 | 95 | zOut[i++] = c; |
| 89 | 96 | break; |
| 90 | 97 | } |
| 91 | - zIn++; | |
| 92 | 98 | } |
| 93 | 99 | zOut[i] = 0; |
| 94 | - return zOut; | |
| 100 | + return (char*)zOut; | |
| 95 | 101 | } |
| 96 | 102 | |
| 97 | 103 | /* |
| 98 | 104 | ** Append HTML-escaped text to a Blob. |
| 99 | 105 | */ |
| 100 | 106 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -26,31 +26,38 @@ | |
| 26 | ** to a new string obtained from malloc(). |
| 27 | ** |
| 28 | ** We also encode " as " and ' as ' so they can appear as an argument |
| 29 | ** to markup. |
| 30 | */ |
| 31 | char *htmlize(const char *zIn, int n){ |
| 32 | int c; |
| 33 | int i = 0; |
| 34 | int count = 0; |
| 35 | char *zOut; |
| 36 | |
| 37 | if( n<0 ) n = strlen(zIn); |
| 38 | while( i<n && (c = zIn[i])!=0 ){ |
| 39 | switch( c ){ |
| 40 | case '<': count += 4; break; |
| 41 | case '>': count += 4; break; |
| 42 | case '&': count += 5; break; |
| 43 | case '"': count += 6; break; |
| 44 | case '\'': count += 5; break; |
| 45 | default: count++; break; |
| 46 | } |
| 47 | i++; |
| 48 | } |
| 49 | i = 0; |
| 50 | zOut = fossil_malloc( count+1 ); |
| 51 | while( n-->0 && (c = *zIn)!=0 ){ |
| 52 | switch( c ){ |
| 53 | case '<': |
| 54 | zOut[i++] = '&'; |
| 55 | zOut[i++] = 'l'; |
| 56 | zOut[i++] = 't'; |
| @@ -86,14 +93,13 @@ | |
| 86 | break; |
| 87 | default: |
| 88 | zOut[i++] = c; |
| 89 | break; |
| 90 | } |
| 91 | zIn++; |
| 92 | } |
| 93 | zOut[i] = 0; |
| 94 | return zOut; |
| 95 | } |
| 96 | |
| 97 | /* |
| 98 | ** Append HTML-escaped text to a Blob. |
| 99 | */ |
| 100 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -26,31 +26,38 @@ | |
| 26 | ** to a new string obtained from malloc(). |
| 27 | ** |
| 28 | ** We also encode " as " and ' as ' so they can appear as an argument |
| 29 | ** to markup. |
| 30 | */ |
| 31 | char *htmlize(const char *z, int n){ |
| 32 | unsigned char c; |
| 33 | int i = 0; |
| 34 | int count = 0; |
| 35 | unsigned char *zOut; |
| 36 | const unsigned char *zIn = (const unsigned char*)z; |
| 37 | |
| 38 | if( n<0 ) n = strlen(z); |
| 39 | while( i<n ){ |
| 40 | switch( zIn[i] ){ |
| 41 | case '<': count += 3; break; |
| 42 | case '>': count += 3; break; |
| 43 | case '&': count += 4; break; |
| 44 | case '"': count += 5; break; |
| 45 | case '\'': count += 4; break; |
| 46 | case 0: n = i; break; |
| 47 | } |
| 48 | i++; |
| 49 | } |
| 50 | i = 0; |
| 51 | zOut = fossil_malloc( count+n+1 ); |
| 52 | if( count==0 ){ |
| 53 | memcpy(zOut, zIn, n); |
| 54 | zOut[n] = 0; |
| 55 | return (char*)zOut; |
| 56 | } |
| 57 | while( n-->0 ){ |
| 58 | c = *(zIn++); |
| 59 | switch( c ){ |
| 60 | case '<': |
| 61 | zOut[i++] = '&'; |
| 62 | zOut[i++] = 'l'; |
| 63 | zOut[i++] = 't'; |
| @@ -86,14 +93,13 @@ | |
| 93 | break; |
| 94 | default: |
| 95 | zOut[i++] = c; |
| 96 | break; |
| 97 | } |
| 98 | } |
| 99 | zOut[i] = 0; |
| 100 | return (char*)zOut; |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | ** Append HTML-escaped text to a Blob. |
| 105 | */ |
| 106 |
+10
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -1170,10 +1170,11 @@ | ||
| 1170 | 1170 | /* |
| 1171 | 1171 | ** Check for 'fx_' table from previous Git import, otherwise take contact info |
| 1172 | 1172 | ** from user table for <emailaddr> in committer field. If no emailaddr, check |
| 1173 | 1173 | ** if username is in email form, otherwise use generic '[email protected]'. |
| 1174 | 1174 | */ |
| 1175 | + char *zTmp; | |
| 1175 | 1176 | if (db_table_exists("repository", "fx_git")) { |
| 1176 | 1177 | zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); |
| 1177 | 1178 | } else { |
| 1178 | 1179 | zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); |
| 1179 | 1180 | } |
| @@ -1183,10 +1184,15 @@ | ||
| 1183 | 1184 | if (strchr(pMan->zUser, '@') == NULL) { |
| 1184 | 1185 | zEmail = mprintf("%[email protected]", pMan->zUser); |
| 1185 | 1186 | } else { |
| 1186 | 1187 | zEmail = fossil_strdup(pMan->zUser); |
| 1187 | 1188 | } |
| 1189 | + } else if ((zTmp = strchr(zEmail, '<')) != NULL) { | |
| 1190 | + ++zTmp; | |
| 1191 | + char *zTmpEnd = strchr(zTmp, '>'); | |
| 1192 | + *(zTmpEnd) = '\0'; | |
| 1193 | + zEmail = fossil_strdup(zTmp); | |
| 1188 | 1194 | } |
| 1189 | 1195 | fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf); |
| 1190 | 1196 | fossil_free(zEmail); |
| 1191 | 1197 | blob_init(&comment, pMan->zComment, -1); |
| 1192 | 1198 | if( blob_size(&comment)==0 ){ |
| @@ -1294,10 +1300,11 @@ | ||
| 1294 | 1300 | double rEnd; /* time of most recent export */ |
| 1295 | 1301 | int rc; /* Result code */ |
| 1296 | 1302 | int bForce; /* Do the export and sync even if no changes*/ |
| 1297 | 1303 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1298 | 1304 | int fManifest; /* Current "manifest" setting */ |
| 1305 | + int bIfExists; /* The --if-mirrored flag */ | |
| 1299 | 1306 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1300 | 1307 | FILE *pMarks; /* Git mark files */ |
| 1301 | 1308 | Stmt q; /* Queries */ |
| 1302 | 1309 | char zLine[200]; /* One line of a mark file */ |
| 1303 | 1310 | |
| @@ -1308,10 +1315,11 @@ | ||
| 1308 | 1315 | nLimit = (unsigned int)atoi(zLimit); |
| 1309 | 1316 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1310 | 1317 | } |
| 1311 | 1318 | zAutoPush = find_option("autopush",0,1); |
| 1312 | 1319 | bForce = find_option("force","f",0)!=0; |
| 1320 | + bIfExists = find_option("if-mirrored",0,0)!=0; | |
| 1313 | 1321 | gitmirror_verbosity = VERB_NORMAL; |
| 1314 | 1322 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1315 | 1323 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1316 | 1324 | verify_all_options(); |
| 1317 | 1325 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1321,10 +1329,11 @@ | ||
| 1321 | 1329 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1322 | 1330 | blob_reset(&mirror); |
| 1323 | 1331 | } |
| 1324 | 1332 | zMirror = db_get("last-git-export-repo", 0); |
| 1325 | 1333 | if( zMirror==0 ){ |
| 1334 | + if( bIfExists ) return; | |
| 1326 | 1335 | fossil_fatal("no Git repository specified"); |
| 1327 | 1336 | } |
| 1328 | 1337 | |
| 1329 | 1338 | /* Make sure the GIT repository directory exists */ |
| 1330 | 1339 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1694,10 +1703,11 @@ | ||
| 1694 | 1703 | ** to the same repository. Or if URL is "off" the |
| 1695 | 1704 | ** auto-push mechanism is disabled |
| 1696 | 1705 | ** --debug FILE Write fast-export text to FILE rather than |
| 1697 | 1706 | ** piping it into "git fast-import". |
| 1698 | 1707 | ** --force|-f Do the export even if nothing has changed |
| 1708 | +** --if-mirrored No-op if the mirror does not already exist. | |
| 1699 | 1709 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1700 | 1710 | ** Useful for debugging |
| 1701 | 1711 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1702 | 1712 | ** --verbose|-v More output. |
| 1703 | 1713 | ** |
| 1704 | 1714 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1170,10 +1170,11 @@ | |
| 1170 | /* |
| 1171 | ** Check for 'fx_' table from previous Git import, otherwise take contact info |
| 1172 | ** from user table for <emailaddr> in committer field. If no emailaddr, check |
| 1173 | ** if username is in email form, otherwise use generic '[email protected]'. |
| 1174 | */ |
| 1175 | if (db_table_exists("repository", "fx_git")) { |
| 1176 | zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); |
| 1177 | } else { |
| 1178 | zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); |
| 1179 | } |
| @@ -1183,10 +1184,15 @@ | |
| 1183 | if (strchr(pMan->zUser, '@') == NULL) { |
| 1184 | zEmail = mprintf("%[email protected]", pMan->zUser); |
| 1185 | } else { |
| 1186 | zEmail = fossil_strdup(pMan->zUser); |
| 1187 | } |
| 1188 | } |
| 1189 | fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf); |
| 1190 | fossil_free(zEmail); |
| 1191 | blob_init(&comment, pMan->zComment, -1); |
| 1192 | if( blob_size(&comment)==0 ){ |
| @@ -1294,10 +1300,11 @@ | |
| 1294 | double rEnd; /* time of most recent export */ |
| 1295 | int rc; /* Result code */ |
| 1296 | int bForce; /* Do the export and sync even if no changes*/ |
| 1297 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1298 | int fManifest; /* Current "manifest" setting */ |
| 1299 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1300 | FILE *pMarks; /* Git mark files */ |
| 1301 | Stmt q; /* Queries */ |
| 1302 | char zLine[200]; /* One line of a mark file */ |
| 1303 | |
| @@ -1308,10 +1315,11 @@ | |
| 1308 | nLimit = (unsigned int)atoi(zLimit); |
| 1309 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1310 | } |
| 1311 | zAutoPush = find_option("autopush",0,1); |
| 1312 | bForce = find_option("force","f",0)!=0; |
| 1313 | gitmirror_verbosity = VERB_NORMAL; |
| 1314 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1315 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1316 | verify_all_options(); |
| 1317 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1321,10 +1329,11 @@ | |
| 1321 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1322 | blob_reset(&mirror); |
| 1323 | } |
| 1324 | zMirror = db_get("last-git-export-repo", 0); |
| 1325 | if( zMirror==0 ){ |
| 1326 | fossil_fatal("no Git repository specified"); |
| 1327 | } |
| 1328 | |
| 1329 | /* Make sure the GIT repository directory exists */ |
| 1330 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1694,10 +1703,11 @@ | |
| 1694 | ** to the same repository. Or if URL is "off" the |
| 1695 | ** auto-push mechanism is disabled |
| 1696 | ** --debug FILE Write fast-export text to FILE rather than |
| 1697 | ** piping it into "git fast-import". |
| 1698 | ** --force|-f Do the export even if nothing has changed |
| 1699 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1700 | ** Useful for debugging |
| 1701 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1702 | ** --verbose|-v More output. |
| 1703 | ** |
| 1704 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1170,10 +1170,11 @@ | |
| 1170 | /* |
| 1171 | ** Check for 'fx_' table from previous Git import, otherwise take contact info |
| 1172 | ** from user table for <emailaddr> in committer field. If no emailaddr, check |
| 1173 | ** if username is in email form, otherwise use generic '[email protected]'. |
| 1174 | */ |
| 1175 | char *zTmp; |
| 1176 | if (db_table_exists("repository", "fx_git")) { |
| 1177 | zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); |
| 1178 | } else { |
| 1179 | zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); |
| 1180 | } |
| @@ -1183,10 +1184,15 @@ | |
| 1184 | if (strchr(pMan->zUser, '@') == NULL) { |
| 1185 | zEmail = mprintf("%[email protected]", pMan->zUser); |
| 1186 | } else { |
| 1187 | zEmail = fossil_strdup(pMan->zUser); |
| 1188 | } |
| 1189 | } else if ((zTmp = strchr(zEmail, '<')) != NULL) { |
| 1190 | ++zTmp; |
| 1191 | char *zTmpEnd = strchr(zTmp, '>'); |
| 1192 | *(zTmpEnd) = '\0'; |
| 1193 | zEmail = fossil_strdup(zTmp); |
| 1194 | } |
| 1195 | fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf); |
| 1196 | fossil_free(zEmail); |
| 1197 | blob_init(&comment, pMan->zComment, -1); |
| 1198 | if( blob_size(&comment)==0 ){ |
| @@ -1294,10 +1300,11 @@ | |
| 1300 | double rEnd; /* time of most recent export */ |
| 1301 | int rc; /* Result code */ |
| 1302 | int bForce; /* Do the export and sync even if no changes*/ |
| 1303 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1304 | int fManifest; /* Current "manifest" setting */ |
| 1305 | int bIfExists; /* The --if-mirrored flag */ |
| 1306 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1307 | FILE *pMarks; /* Git mark files */ |
| 1308 | Stmt q; /* Queries */ |
| 1309 | char zLine[200]; /* One line of a mark file */ |
| 1310 | |
| @@ -1308,10 +1315,11 @@ | |
| 1315 | nLimit = (unsigned int)atoi(zLimit); |
| 1316 | if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
| 1317 | } |
| 1318 | zAutoPush = find_option("autopush",0,1); |
| 1319 | bForce = find_option("force","f",0)!=0; |
| 1320 | bIfExists = find_option("if-mirrored",0,0)!=0; |
| 1321 | gitmirror_verbosity = VERB_NORMAL; |
| 1322 | while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
| 1323 | while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
| 1324 | verify_all_options(); |
| 1325 | if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
| @@ -1321,10 +1329,11 @@ | |
| 1329 | db_set("last-git-export-repo", blob_str(&mirror), 0); |
| 1330 | blob_reset(&mirror); |
| 1331 | } |
| 1332 | zMirror = db_get("last-git-export-repo", 0); |
| 1333 | if( zMirror==0 ){ |
| 1334 | if( bIfExists ) return; |
| 1335 | fossil_fatal("no Git repository specified"); |
| 1336 | } |
| 1337 | |
| 1338 | /* Make sure the GIT repository directory exists */ |
| 1339 | rc = file_mkdir(zMirror, ExtFILE, 0); |
| @@ -1694,10 +1703,11 @@ | |
| 1703 | ** to the same repository. Or if URL is "off" the |
| 1704 | ** auto-push mechanism is disabled |
| 1705 | ** --debug FILE Write fast-export text to FILE rather than |
| 1706 | ** piping it into "git fast-import". |
| 1707 | ** --force|-f Do the export even if nothing has changed |
| 1708 | ** --if-mirrored No-op if the mirror does not already exist. |
| 1709 | ** --limit N Add no more than N new check-ins to MIRROR. |
| 1710 | ** Useful for debugging |
| 1711 | ** --quiet|-q Reduce output. Repeat for even less output. |
| 1712 | ** --verbose|-v More output. |
| 1713 | ** |
| 1714 |
+1
-1
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -1989,11 +1989,11 @@ | ||
| 1989 | 1989 | } |
| 1990 | 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | 1991 | |
| 1992 | 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | - "pikchr", 0); | |
| 1994 | + "pikchr", NULL); | |
| 1995 | 1995 | /* |
| 1996 | 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 | 2000 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1989,11 +1989,11 @@ | |
| 1989 | } |
| 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | |
| 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | "pikchr", 0); |
| 1995 | /* |
| 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1989,11 +1989,11 @@ | |
| 1989 | } |
| 1990 | CX("</div>"/*#fileedit-tab-help*/); |
| 1991 | |
| 1992 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1993 | "storage", "popupwidget", "copybutton", |
| 1994 | "pikchr", NULL); |
| 1995 | /* |
| 1996 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1997 | ** used for dynamically toggling certain UI components on and off. |
| 1998 | ** Must come after window.fossil has been intialized and before |
| 1999 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 2000 |
+9
-4
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -287,11 +287,11 @@ | ||
| 287 | 287 | fpid = symbolic_name_to_rid(zName, "f"); |
| 288 | 288 | if( fpid<=0 ){ |
| 289 | 289 | fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName)); |
| 290 | 290 | } |
| 291 | 291 | if( fpid<=0 ){ |
| 292 | - fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName); | |
| 292 | + fossil_fatal("unknown or ambiguous forum id: \"%s\"", zName); | |
| 293 | 293 | } |
| 294 | 294 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 295 | 295 | if( froot==0 ){ |
| 296 | 296 | fossil_fatal("Not a forum post: \"%s\"", zName); |
| 297 | 297 | } |
| @@ -743,11 +743,11 @@ | ||
| 743 | 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | 745 | ** code (e.g. "forum.js"). |
| 746 | 746 | */ |
| 747 | 747 | static void forum_emit_js(void){ |
| 748 | - builtin_fossil_js_bundle_or("copybutton", "pikchr", 0); | |
| 748 | + builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL); | |
| 749 | 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | 750 | } |
| 751 | 751 | |
| 752 | 752 | /* |
| 753 | 753 | ** WEBPAGE: forumpost |
| @@ -813,15 +813,20 @@ | ||
| 813 | 813 | if( zName==0 ){ |
| 814 | 814 | webpage_error("Missing \"name=\" query parameter"); |
| 815 | 815 | } |
| 816 | 816 | fpid = symbolic_name_to_rid(zName, "f"); |
| 817 | 817 | if( fpid<=0 ){ |
| 818 | - webpage_error("Unknown or ambiguous forum id: \"%s\"", zName); | |
| 818 | + if( fpid==0 ){ | |
| 819 | + webpage_notfound_error("Unknown forum id: \"%s\"", zName); | |
| 820 | + }else{ | |
| 821 | + ambiguous_page(); | |
| 822 | + } | |
| 823 | + return; | |
| 819 | 824 | } |
| 820 | 825 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 821 | 826 | if( froot==0 ){ |
| 822 | - webpage_error("Not a forum post: \"%s\"", zName); | |
| 827 | + webpage_notfound_error("Not a forum post: \"%s\"", zName); | |
| 823 | 828 | } |
| 824 | 829 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 825 | 830 | |
| 826 | 831 | /* Decode the mode parameters. */ |
| 827 | 832 | if( bRaw ){ |
| 828 | 833 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -287,11 +287,11 @@ | |
| 287 | fpid = symbolic_name_to_rid(zName, "f"); |
| 288 | if( fpid<=0 ){ |
| 289 | fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName)); |
| 290 | } |
| 291 | if( fpid<=0 ){ |
| 292 | fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName); |
| 293 | } |
| 294 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 295 | if( froot==0 ){ |
| 296 | fossil_fatal("Not a forum post: \"%s\"", zName); |
| 297 | } |
| @@ -743,11 +743,11 @@ | |
| 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | ** code (e.g. "forum.js"). |
| 746 | */ |
| 747 | static void forum_emit_js(void){ |
| 748 | builtin_fossil_js_bundle_or("copybutton", "pikchr", 0); |
| 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | } |
| 751 | |
| 752 | /* |
| 753 | ** WEBPAGE: forumpost |
| @@ -813,15 +813,20 @@ | |
| 813 | if( zName==0 ){ |
| 814 | webpage_error("Missing \"name=\" query parameter"); |
| 815 | } |
| 816 | fpid = symbolic_name_to_rid(zName, "f"); |
| 817 | if( fpid<=0 ){ |
| 818 | webpage_error("Unknown or ambiguous forum id: \"%s\"", zName); |
| 819 | } |
| 820 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 821 | if( froot==0 ){ |
| 822 | webpage_error("Not a forum post: \"%s\"", zName); |
| 823 | } |
| 824 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 825 | |
| 826 | /* Decode the mode parameters. */ |
| 827 | if( bRaw ){ |
| 828 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -287,11 +287,11 @@ | |
| 287 | fpid = symbolic_name_to_rid(zName, "f"); |
| 288 | if( fpid<=0 ){ |
| 289 | fpid = db_int(0, "SELECT rid FROM blob WHERE rid=%d", atoi(zName)); |
| 290 | } |
| 291 | if( fpid<=0 ){ |
| 292 | fossil_fatal("unknown or ambiguous forum id: \"%s\"", zName); |
| 293 | } |
| 294 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 295 | if( froot==0 ){ |
| 296 | fossil_fatal("Not a forum post: \"%s\"", zName); |
| 297 | } |
| @@ -743,11 +743,11 @@ | |
| 743 | ** Emit Forum Javascript which applies (or optionally can apply) |
| 744 | ** to all forum-related pages. It does not include page-specific |
| 745 | ** code (e.g. "forum.js"). |
| 746 | */ |
| 747 | static void forum_emit_js(void){ |
| 748 | builtin_fossil_js_bundle_or("copybutton", "pikchr", NULL); |
| 749 | builtin_request_js("fossil.page.forumpost.js"); |
| 750 | } |
| 751 | |
| 752 | /* |
| 753 | ** WEBPAGE: forumpost |
| @@ -813,15 +813,20 @@ | |
| 813 | if( zName==0 ){ |
| 814 | webpage_error("Missing \"name=\" query parameter"); |
| 815 | } |
| 816 | fpid = symbolic_name_to_rid(zName, "f"); |
| 817 | if( fpid<=0 ){ |
| 818 | if( fpid==0 ){ |
| 819 | webpage_notfound_error("Unknown forum id: \"%s\"", zName); |
| 820 | }else{ |
| 821 | ambiguous_page(); |
| 822 | } |
| 823 | return; |
| 824 | } |
| 825 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 826 | if( froot==0 ){ |
| 827 | webpage_notfound_error("Not a forum post: \"%s\"", zName); |
| 828 | } |
| 829 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 830 | |
| 831 | /* Decode the mode parameters. */ |
| 832 | if( bRaw ){ |
| 833 |
+2
-1
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -1947,15 +1947,16 @@ | ||
| 1947 | 1947 | ** The following 'fx_' table is used to hold information needed for |
| 1948 | 1948 | ** importing and exporting to attribute Fossil check-ins or Git commits |
| 1949 | 1949 | ** to either a desired username or full contact information string. |
| 1950 | 1950 | */ |
| 1951 | 1951 | if(ggit.nGitAttr > 0) { |
| 1952 | + int idx; | |
| 1952 | 1953 | db_unprotect(PROTECT_ALL); |
| 1953 | 1954 | db_multi_exec( |
| 1954 | 1955 | "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" |
| 1955 | 1956 | ); |
| 1956 | - for( int idx = 0; idx < ggit.nGitAttr; ++idx ){ | |
| 1957 | + for(idx = 0; idx < ggit.nGitAttr; ++idx ){ | |
| 1957 | 1958 | db_multi_exec( |
| 1958 | 1959 | "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", |
| 1959 | 1960 | ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail |
| 1960 | 1961 | ); |
| 1961 | 1962 | } |
| 1962 | 1963 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1947,15 +1947,16 @@ | |
| 1947 | ** The following 'fx_' table is used to hold information needed for |
| 1948 | ** importing and exporting to attribute Fossil check-ins or Git commits |
| 1949 | ** to either a desired username or full contact information string. |
| 1950 | */ |
| 1951 | if(ggit.nGitAttr > 0) { |
| 1952 | db_unprotect(PROTECT_ALL); |
| 1953 | db_multi_exec( |
| 1954 | "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" |
| 1955 | ); |
| 1956 | for( int idx = 0; idx < ggit.nGitAttr; ++idx ){ |
| 1957 | db_multi_exec( |
| 1958 | "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", |
| 1959 | ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail |
| 1960 | ); |
| 1961 | } |
| 1962 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1947,15 +1947,16 @@ | |
| 1947 | ** The following 'fx_' table is used to hold information needed for |
| 1948 | ** importing and exporting to attribute Fossil check-ins or Git commits |
| 1949 | ** to either a desired username or full contact information string. |
| 1950 | */ |
| 1951 | if(ggit.nGitAttr > 0) { |
| 1952 | int idx; |
| 1953 | db_unprotect(PROTECT_ALL); |
| 1954 | db_multi_exec( |
| 1955 | "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" |
| 1956 | ); |
| 1957 | for(idx = 0; idx < ggit.nGitAttr; ++idx ){ |
| 1958 | db_multi_exec( |
| 1959 | "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", |
| 1960 | ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail |
| 1961 | ); |
| 1962 | } |
| 1963 |
+4
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2136,11 +2136,11 @@ | ||
| 2136 | 2136 | if(includeJS && !emittedJS){ |
| 2137 | 2137 | emittedJS = 1; |
| 2138 | 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | 2139 | builtin_request_js("scroll.js"); |
| 2140 | 2140 | } |
| 2141 | - builtin_fossil_js_bundle_or("numbered-lines", 0); | |
| 2141 | + builtin_fossil_js_bundle_or("numbered-lines", NULL); | |
| 2142 | 2142 | } |
| 2143 | 2143 | } |
| 2144 | 2144 | |
| 2145 | 2145 | /* |
| 2146 | 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | ||
| 2398 | 2398 | } |
| 2399 | 2399 | |
| 2400 | 2400 | if( isFile ){ |
| 2401 | 2401 | if( isSymbolicCI ){ |
| 2402 | 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | + style_set_current_page("doc/%t/%T", zCI, zName); | |
| 2403 | 2404 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2404 | 2405 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2406 | + style_set_current_page("doc/%S/%T", zCIUuid, zName); | |
| 2405 | 2407 | }else{ |
| 2406 | 2408 | zHeader = mprintf("%s", file_tail(zName)); |
| 2409 | + style_set_current_page("doc/tip/%T", zName); | |
| 2407 | 2410 | } |
| 2408 | 2411 | }else if( descOnly ){ |
| 2409 | 2412 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2410 | 2413 | }else{ |
| 2411 | 2414 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2412 | 2415 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2136,11 +2136,11 @@ | |
| 2136 | if(includeJS && !emittedJS){ |
| 2137 | emittedJS = 1; |
| 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | builtin_request_js("scroll.js"); |
| 2140 | } |
| 2141 | builtin_fossil_js_bundle_or("numbered-lines", 0); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | /* |
| 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | |
| 2398 | } |
| 2399 | |
| 2400 | if( isFile ){ |
| 2401 | if( isSymbolicCI ){ |
| 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2404 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2405 | }else{ |
| 2406 | zHeader = mprintf("%s", file_tail(zName)); |
| 2407 | } |
| 2408 | }else if( descOnly ){ |
| 2409 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2410 | }else{ |
| 2411 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2412 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2136,11 +2136,11 @@ | |
| 2136 | if(includeJS && !emittedJS){ |
| 2137 | emittedJS = 1; |
| 2138 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2139 | builtin_request_js("scroll.js"); |
| 2140 | } |
| 2141 | builtin_fossil_js_bundle_or("numbered-lines", NULL); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | /* |
| 2146 | ** COMMAND: test-line-numbers |
| @@ -2398,14 +2398,17 @@ | |
| 2398 | } |
| 2399 | |
| 2400 | if( isFile ){ |
| 2401 | if( isSymbolicCI ){ |
| 2402 | zHeader = mprintf("%s at %s", file_tail(zName), zCI); |
| 2403 | style_set_current_page("doc/%t/%T", zCI, zName); |
| 2404 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2405 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2406 | style_set_current_page("doc/%S/%T", zCIUuid, zName); |
| 2407 | }else{ |
| 2408 | zHeader = mprintf("%s", file_tail(zName)); |
| 2409 | style_set_current_page("doc/tip/%T", zName); |
| 2410 | } |
| 2411 | }else if( descOnly ){ |
| 2412 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2413 | }else{ |
| 2414 | zHeader = mprintf("Artifact [%S]", zUuid); |
| 2415 |
+9
-5
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -74,23 +74,26 @@ | ||
| 74 | 74 | int nCode; |
| 75 | 75 | int i; |
| 76 | 76 | const char *zPage; |
| 77 | 77 | int nPage; |
| 78 | 78 | char *zUrl = 0; |
| 79 | - Stmt q; | |
| 79 | + char *zName; | |
| 80 | + static Stmt q; | |
| 80 | 81 | for(i=0; fossil_isalnum(zTarget[i]); i++){} |
| 81 | 82 | if( zTarget[i]!=':' ) return 0; |
| 82 | 83 | nCode = i; |
| 83 | 84 | if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; |
| 84 | 85 | zPage = zTarget + nCode + 1; |
| 85 | 86 | nPage = (int)strlen(zPage); |
| 86 | - db_prepare(&q, | |
| 87 | + db_static_prepare(&q, | |
| 87 | 88 | "SELECT json_extract(value,'$.base')," |
| 88 | 89 | " json_extract(value,'$.hash')," |
| 89 | 90 | " json_extract(value,'$.wiki')" |
| 90 | - " FROM config WHERE name=lower('interwiki:%.*q')", | |
| 91 | - nCode, zTarget); | |
| 91 | + " FROM config WHERE name=lower($name)" | |
| 92 | + ); | |
| 93 | + zName = mprintf("interwiki:%.*s", nCode, zTarget); | |
| 94 | + db_bind_text(&q, "$name", zName); | |
| 92 | 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 93 | 96 | const char *zBase = db_column_text(&q,0); |
| 94 | 97 | if( zBase==0 || zBase[0]==0 ) break; |
| 95 | 98 | if( nPage==0 || zPage[0]=='/' ){ |
| 96 | 99 | /* Path */ |
| @@ -108,11 +111,12 @@ | ||
| 108 | 111 | zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); |
| 109 | 112 | } |
| 110 | 113 | } |
| 111 | 114 | break; |
| 112 | 115 | } |
| 113 | - db_finalize(&q); | |
| 116 | + db_reset(&q); | |
| 117 | + free(zName); | |
| 114 | 118 | return zUrl; |
| 115 | 119 | } |
| 116 | 120 | |
| 117 | 121 | /* |
| 118 | 122 | ** If hyperlink target zTarget begins with an interwiki tag that ought |
| 119 | 123 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -74,23 +74,26 @@ | |
| 74 | int nCode; |
| 75 | int i; |
| 76 | const char *zPage; |
| 77 | int nPage; |
| 78 | char *zUrl = 0; |
| 79 | Stmt q; |
| 80 | for(i=0; fossil_isalnum(zTarget[i]); i++){} |
| 81 | if( zTarget[i]!=':' ) return 0; |
| 82 | nCode = i; |
| 83 | if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; |
| 84 | zPage = zTarget + nCode + 1; |
| 85 | nPage = (int)strlen(zPage); |
| 86 | db_prepare(&q, |
| 87 | "SELECT json_extract(value,'$.base')," |
| 88 | " json_extract(value,'$.hash')," |
| 89 | " json_extract(value,'$.wiki')" |
| 90 | " FROM config WHERE name=lower('interwiki:%.*q')", |
| 91 | nCode, zTarget); |
| 92 | while( db_step(&q)==SQLITE_ROW ){ |
| 93 | const char *zBase = db_column_text(&q,0); |
| 94 | if( zBase==0 || zBase[0]==0 ) break; |
| 95 | if( nPage==0 || zPage[0]=='/' ){ |
| 96 | /* Path */ |
| @@ -108,11 +111,12 @@ | |
| 108 | zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); |
| 109 | } |
| 110 | } |
| 111 | break; |
| 112 | } |
| 113 | db_finalize(&q); |
| 114 | return zUrl; |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | ** If hyperlink target zTarget begins with an interwiki tag that ought |
| 119 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -74,23 +74,26 @@ | |
| 74 | int nCode; |
| 75 | int i; |
| 76 | const char *zPage; |
| 77 | int nPage; |
| 78 | char *zUrl = 0; |
| 79 | char *zName; |
| 80 | static Stmt q; |
| 81 | for(i=0; fossil_isalnum(zTarget[i]); i++){} |
| 82 | if( zTarget[i]!=':' ) return 0; |
| 83 | nCode = i; |
| 84 | if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0; |
| 85 | zPage = zTarget + nCode + 1; |
| 86 | nPage = (int)strlen(zPage); |
| 87 | db_static_prepare(&q, |
| 88 | "SELECT json_extract(value,'$.base')," |
| 89 | " json_extract(value,'$.hash')," |
| 90 | " json_extract(value,'$.wiki')" |
| 91 | " FROM config WHERE name=lower($name)" |
| 92 | ); |
| 93 | zName = mprintf("interwiki:%.*s", nCode, zTarget); |
| 94 | db_bind_text(&q, "$name", zName); |
| 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 96 | const char *zBase = db_column_text(&q,0); |
| 97 | if( zBase==0 || zBase[0]==0 ) break; |
| 98 | if( nPage==0 || zPage[0]=='/' ){ |
| 99 | /* Path */ |
| @@ -108,11 +111,12 @@ | |
| 111 | zUrl = mprintf("%s%s%s", zBase, zWiki, zPage); |
| 112 | } |
| 113 | } |
| 114 | break; |
| 115 | } |
| 116 | db_reset(&q); |
| 117 | free(zName); |
| 118 | return zUrl; |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 | ** If hyperlink target zTarget begins with an interwiki tag that ought |
| 123 |
+6
-4
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -673,10 +673,11 @@ | ||
| 673 | 673 | #endif |
| 674 | 674 | } |
| 675 | 675 | } |
| 676 | 676 | #endif |
| 677 | 677 | |
| 678 | + fossil_printf_selfcheck(); | |
| 678 | 679 | fossil_limit_memory(1); |
| 679 | 680 | if( sqlite3_libversion_number()<3034000 ){ |
| 680 | 681 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0", |
| 681 | 682 | sqlite3_libversion()); |
| 682 | 683 | } |
| @@ -795,18 +796,19 @@ | ||
| 795 | 796 | /* If --help is found anywhere on the command line, translate the command |
| 796 | 797 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 797 | 798 | * does not begin with a "-" character. If all arguments start with "-", |
| 798 | 799 | * translate to "fossil help argv[1] argv[2]...". */ |
| 799 | 800 | int i, nNewArgc; |
| 800 | - char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); | |
| 801 | + char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) ); | |
| 801 | 802 | zNewArgv[0] = g.argv[0]; |
| 802 | 803 | zNewArgv[1] = "help"; |
| 804 | + zNewArgv[2] = "-c"; | |
| 803 | 805 | for(i=1; i<g.argc; i++){ |
| 804 | 806 | if( g.argv[i][0]!='-' ){ |
| 805 | - nNewArgc = 3; | |
| 806 | - zNewArgv[2] = g.argv[i]; | |
| 807 | - zNewArgv[3] = 0; | |
| 807 | + nNewArgc = 4; | |
| 808 | + zNewArgv[3] = g.argv[i]; | |
| 809 | + zNewArgv[4] = 0; | |
| 808 | 810 | break; |
| 809 | 811 | } |
| 810 | 812 | } |
| 811 | 813 | if( i==g.argc ){ |
| 812 | 814 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 813 | 815 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -673,10 +673,11 @@ | |
| 673 | #endif |
| 674 | } |
| 675 | } |
| 676 | #endif |
| 677 | |
| 678 | fossil_limit_memory(1); |
| 679 | if( sqlite3_libversion_number()<3034000 ){ |
| 680 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0", |
| 681 | sqlite3_libversion()); |
| 682 | } |
| @@ -795,18 +796,19 @@ | |
| 795 | /* If --help is found anywhere on the command line, translate the command |
| 796 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 797 | * does not begin with a "-" character. If all arguments start with "-", |
| 798 | * translate to "fossil help argv[1] argv[2]...". */ |
| 799 | int i, nNewArgc; |
| 800 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); |
| 801 | zNewArgv[0] = g.argv[0]; |
| 802 | zNewArgv[1] = "help"; |
| 803 | for(i=1; i<g.argc; i++){ |
| 804 | if( g.argv[i][0]!='-' ){ |
| 805 | nNewArgc = 3; |
| 806 | zNewArgv[2] = g.argv[i]; |
| 807 | zNewArgv[3] = 0; |
| 808 | break; |
| 809 | } |
| 810 | } |
| 811 | if( i==g.argc ){ |
| 812 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 813 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -673,10 +673,11 @@ | |
| 673 | #endif |
| 674 | } |
| 675 | } |
| 676 | #endif |
| 677 | |
| 678 | fossil_printf_selfcheck(); |
| 679 | fossil_limit_memory(1); |
| 680 | if( sqlite3_libversion_number()<3034000 ){ |
| 681 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.34.0", |
| 682 | sqlite3_libversion()); |
| 683 | } |
| @@ -795,18 +796,19 @@ | |
| 796 | /* If --help is found anywhere on the command line, translate the command |
| 797 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 798 | * does not begin with a "-" character. If all arguments start with "-", |
| 799 | * translate to "fossil help argv[1] argv[2]...". */ |
| 800 | int i, nNewArgc; |
| 801 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+3) ); |
| 802 | zNewArgv[0] = g.argv[0]; |
| 803 | zNewArgv[1] = "help"; |
| 804 | zNewArgv[2] = "-c"; |
| 805 | for(i=1; i<g.argc; i++){ |
| 806 | if( g.argv[i][0]!='-' ){ |
| 807 | nNewArgc = 4; |
| 808 | zNewArgv[3] = g.argv[i]; |
| 809 | zNewArgv[4] = 0; |
| 810 | break; |
| 811 | } |
| 812 | } |
| 813 | if( i==g.argc ){ |
| 814 | for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; |
| 815 |
+8
-37
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -2358,14 +2358,14 @@ | ||
| 2358 | 2358 | } |
| 2359 | 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | 2362 | int prior; |
| 2363 | - char *zComment; | |
| 2364 | - const char *zPrefix; | |
| 2363 | + char cPrefix; | |
| 2365 | 2364 | int nWiki; |
| 2366 | 2365 | char zLength[40]; |
| 2366 | + | |
| 2367 | 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | 2368 | nWiki = strlen(p->zWiki); |
| 2369 | 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | ||
| 2377 | 2377 | ); |
| 2378 | 2378 | if( prior ){ |
| 2379 | 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | 2380 | } |
| 2381 | 2381 | if( nWiki<=0 ){ |
| 2382 | - zPrefix = "Deleted"; | |
| 2382 | + cPrefix = '-'; | |
| 2383 | 2383 | }else if( !prior ){ |
| 2384 | - zPrefix = "Added"; | |
| 2384 | + cPrefix = '+'; | |
| 2385 | 2385 | }else{ |
| 2386 | - zPrefix = "Changes to"; | |
| 2387 | - } | |
| 2388 | - switch( wiki_page_type(p->zWikiTitle) ){ | |
| 2389 | - case WIKITYPE_CHECKIN: { | |
| 2390 | - zComment = mprintf("%s wiki for check-in [%S]", zPrefix, | |
| 2391 | - p->zWikiTitle+8); | |
| 2392 | - break; | |
| 2393 | - } | |
| 2394 | - case WIKITYPE_BRANCH: { | |
| 2395 | - zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", | |
| 2396 | - zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); | |
| 2397 | - break; | |
| 2398 | - } | |
| 2399 | - case WIKITYPE_TAG: { | |
| 2400 | - zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", | |
| 2401 | - zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); | |
| 2402 | - break; | |
| 2403 | - } | |
| 2404 | - default: { | |
| 2405 | - zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); | |
| 2406 | - break; | |
| 2407 | - } | |
| 2386 | + cPrefix = ':'; | |
| 2408 | 2387 | } |
| 2409 | 2388 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2410 | 2389 | if( manifest_crosslink_busy ){ |
| 2411 | 2390 | add_pending_crosslink('w',p->zWikiTitle); |
| 2412 | 2391 | }else{ |
| 2413 | 2392 | backlink_wiki_refresh(p->zWikiTitle); |
| 2414 | 2393 | } |
| 2415 | 2394 | db_multi_exec( |
| 2416 | - "REPLACE INTO event(type,mtime,objid,user,comment," | |
| 2417 | - " bgcolor,euser,ecomment)" | |
| 2418 | - "VALUES('w',%.17g,%d,%Q,%Q," | |
| 2419 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," | |
| 2420 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," | |
| 2421 | - " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", | |
| 2422 | - p->rDate, rid, p->zUser, zComment, | |
| 2423 | - TAG_BGCOLOR, rid, | |
| 2424 | - TAG_USER, rid, | |
| 2425 | - TAG_COMMENT, rid | |
| 2395 | + "REPLACE INTO event(type,mtime,objid,user,comment)" | |
| 2396 | + "VALUES('w',%.17g,%d,%Q,'%c%q');", | |
| 2397 | + p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle | |
| 2426 | 2398 | ); |
| 2427 | - fossil_free(zComment); | |
| 2428 | 2399 | } |
| 2429 | 2400 | if( p->type==CFTYPE_EVENT ){ |
| 2430 | 2401 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2431 | 2402 | int tagid = tag_findid(zTag, 1); |
| 2432 | 2403 | int prior, subsequent; |
| 2433 | 2404 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2358,14 +2358,14 @@ | |
| 2358 | } |
| 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | int prior; |
| 2363 | char *zComment; |
| 2364 | const char *zPrefix; |
| 2365 | int nWiki; |
| 2366 | char zLength[40]; |
| 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | nWiki = strlen(p->zWiki); |
| 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | |
| 2377 | ); |
| 2378 | if( prior ){ |
| 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | } |
| 2381 | if( nWiki<=0 ){ |
| 2382 | zPrefix = "Deleted"; |
| 2383 | }else if( !prior ){ |
| 2384 | zPrefix = "Added"; |
| 2385 | }else{ |
| 2386 | zPrefix = "Changes to"; |
| 2387 | } |
| 2388 | switch( wiki_page_type(p->zWikiTitle) ){ |
| 2389 | case WIKITYPE_CHECKIN: { |
| 2390 | zComment = mprintf("%s wiki for check-in [%S]", zPrefix, |
| 2391 | p->zWikiTitle+8); |
| 2392 | break; |
| 2393 | } |
| 2394 | case WIKITYPE_BRANCH: { |
| 2395 | zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]", |
| 2396 | zPrefix, p->zWikiTitle+7, p->zWikiTitle+7); |
| 2397 | break; |
| 2398 | } |
| 2399 | case WIKITYPE_TAG: { |
| 2400 | zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]", |
| 2401 | zPrefix, p->zWikiTitle+4, p->zWikiTitle+4); |
| 2402 | break; |
| 2403 | } |
| 2404 | default: { |
| 2405 | zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle); |
| 2406 | break; |
| 2407 | } |
| 2408 | } |
| 2409 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2410 | if( manifest_crosslink_busy ){ |
| 2411 | add_pending_crosslink('w',p->zWikiTitle); |
| 2412 | }else{ |
| 2413 | backlink_wiki_refresh(p->zWikiTitle); |
| 2414 | } |
| 2415 | db_multi_exec( |
| 2416 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 2417 | " bgcolor,euser,ecomment)" |
| 2418 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 2419 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| 2420 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 2421 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 2422 | p->rDate, rid, p->zUser, zComment, |
| 2423 | TAG_BGCOLOR, rid, |
| 2424 | TAG_USER, rid, |
| 2425 | TAG_COMMENT, rid |
| 2426 | ); |
| 2427 | fossil_free(zComment); |
| 2428 | } |
| 2429 | if( p->type==CFTYPE_EVENT ){ |
| 2430 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2431 | int tagid = tag_findid(zTag, 1); |
| 2432 | int prior, subsequent; |
| 2433 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2358,14 +2358,14 @@ | |
| 2358 | } |
| 2359 | if( p->type==CFTYPE_WIKI ){ |
| 2360 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 2361 | int tagid = tag_findid(zTag, 1); |
| 2362 | int prior; |
| 2363 | char cPrefix; |
| 2364 | int nWiki; |
| 2365 | char zLength[40]; |
| 2366 | |
| 2367 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 2368 | nWiki = strlen(p->zWiki); |
| 2369 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 2370 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 2371 | fossil_free(zTag); |
| @@ -2377,56 +2377,27 @@ | |
| 2377 | ); |
| 2378 | if( prior ){ |
| 2379 | content_deltify(prior, &rid, 1, 0); |
| 2380 | } |
| 2381 | if( nWiki<=0 ){ |
| 2382 | cPrefix = '-'; |
| 2383 | }else if( !prior ){ |
| 2384 | cPrefix = '+'; |
| 2385 | }else{ |
| 2386 | cPrefix = ':'; |
| 2387 | } |
| 2388 | search_doc_touch('w',rid,p->zWikiTitle); |
| 2389 | if( manifest_crosslink_busy ){ |
| 2390 | add_pending_crosslink('w',p->zWikiTitle); |
| 2391 | }else{ |
| 2392 | backlink_wiki_refresh(p->zWikiTitle); |
| 2393 | } |
| 2394 | db_multi_exec( |
| 2395 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 2396 | "VALUES('w',%.17g,%d,%Q,'%c%q');", |
| 2397 | p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle |
| 2398 | ); |
| 2399 | } |
| 2400 | if( p->type==CFTYPE_EVENT ){ |
| 2401 | char *zTag = mprintf("event-%s", p->zEventId); |
| 2402 | int tagid = tag_findid(zTag, 1); |
| 2403 | int prior, subsequent; |
| 2404 |
+1
-1
| --- src/markdown.c | ||
| +++ src/markdown.c | ||
| @@ -514,11 +514,11 @@ | ||
| 514 | 514 | } |
| 515 | 515 | |
| 516 | 516 | |
| 517 | 517 | /* find_emph_char -- looks for the next emph char, skipping other constructs */ |
| 518 | 518 | static size_t find_emph_char(char *data, size_t size, char c){ |
| 519 | - size_t i = 1; | |
| 519 | + size_t i = data[0]!='`'; | |
| 520 | 520 | |
| 521 | 521 | while( i<size ){ |
| 522 | 522 | while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; } |
| 523 | 523 | if( i>=size ) return 0; |
| 524 | 524 | |
| 525 | 525 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -514,11 +514,11 @@ | |
| 514 | } |
| 515 | |
| 516 | |
| 517 | /* find_emph_char -- looks for the next emph char, skipping other constructs */ |
| 518 | static size_t find_emph_char(char *data, size_t size, char c){ |
| 519 | size_t i = 1; |
| 520 | |
| 521 | while( i<size ){ |
| 522 | while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; } |
| 523 | if( i>=size ) return 0; |
| 524 | |
| 525 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -514,11 +514,11 @@ | |
| 514 | } |
| 515 | |
| 516 | |
| 517 | /* find_emph_char -- looks for the next emph char, skipping other constructs */ |
| 518 | static size_t find_emph_char(char *data, size_t size, char c){ |
| 519 | size_t i = data[0]!='`'; |
| 520 | |
| 521 | while( i<size ){ |
| 522 | while( i<size && data[i]!=c && data[i]!='`' && data[i]!='[' ){ i++; } |
| 523 | if( i>=size ) return 0; |
| 524 | |
| 525 |
+19
-4
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -347,11 +347,12 @@ | ||
| 347 | 347 | int pikFlags = PIKCHR_PROCESS_NONCE |
| 348 | 348 | | PIKCHR_PROCESS_DIV |
| 349 | 349 | | PIKCHR_PROCESS_SRC |
| 350 | 350 | | PIKCHR_PROCESS_ERR_PRE; |
| 351 | 351 | Blob bSrc = empty_blob; |
| 352 | - const char *zFgColor; | |
| 352 | + const char *zPikVar; | |
| 353 | + double rPikVar; | |
| 353 | 354 | |
| 354 | 355 | while( nArg>0 ){ |
| 355 | 356 | int i; |
| 356 | 357 | for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){} |
| 357 | 358 | if( i==6 && strncmp(zArg, "center", 6)==0 ){ |
| @@ -374,13 +375,27 @@ | ||
| 374 | 375 | nArg -= i; |
| 375 | 376 | } |
| 376 | 377 | if( skin_detail_boolean("white-foreground") ){ |
| 377 | 378 | pikFlags |= 0x02; /* PIKCHR_DARK_MODE */ |
| 378 | 379 | } |
| 379 | - zFgColor = skin_detail("pikchr-foreground"); | |
| 380 | - if( zFgColor && zFgColor[0] ){ | |
| 381 | - blob_appendf(&bSrc, "fgcolor = %s\n", zFgColor); | |
| 380 | + zPikVar = skin_detail("pikchr-foreground"); | |
| 381 | + if( zPikVar && zPikVar[0] ){ | |
| 382 | + blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar); | |
| 383 | + } | |
| 384 | + zPikVar = skin_detail("pikchr-scale"); | |
| 385 | + if( zPikVar | |
| 386 | + && (rPikVar = atof(zPikVar))>=0.1 | |
| 387 | + && rPikVar<10.0 | |
| 388 | + ){ | |
| 389 | + blob_appendf(&bSrc, "scale = %.13g\n", rPikVar); | |
| 390 | + } | |
| 391 | + zPikVar = skin_detail("pikchr-fontscale"); | |
| 392 | + if( zPikVar | |
| 393 | + && (rPikVar = atof(zPikVar))>=0.1 | |
| 394 | + && rPikVar<10.0 | |
| 395 | + ){ | |
| 396 | + blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar); | |
| 382 | 397 | } |
| 383 | 398 | blob_append(&bSrc, zSrc, nSrc) |
| 384 | 399 | /*have to dup input to ensure a NUL-terminated source string */; |
| 385 | 400 | pikchr_process(blob_str(&bSrc), pikFlags, 0, ob); |
| 386 | 401 | blob_reset(&bSrc); |
| 387 | 402 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -347,11 +347,12 @@ | |
| 347 | int pikFlags = PIKCHR_PROCESS_NONCE |
| 348 | | PIKCHR_PROCESS_DIV |
| 349 | | PIKCHR_PROCESS_SRC |
| 350 | | PIKCHR_PROCESS_ERR_PRE; |
| 351 | Blob bSrc = empty_blob; |
| 352 | const char *zFgColor; |
| 353 | |
| 354 | while( nArg>0 ){ |
| 355 | int i; |
| 356 | for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){} |
| 357 | if( i==6 && strncmp(zArg, "center", 6)==0 ){ |
| @@ -374,13 +375,27 @@ | |
| 374 | nArg -= i; |
| 375 | } |
| 376 | if( skin_detail_boolean("white-foreground") ){ |
| 377 | pikFlags |= 0x02; /* PIKCHR_DARK_MODE */ |
| 378 | } |
| 379 | zFgColor = skin_detail("pikchr-foreground"); |
| 380 | if( zFgColor && zFgColor[0] ){ |
| 381 | blob_appendf(&bSrc, "fgcolor = %s\n", zFgColor); |
| 382 | } |
| 383 | blob_append(&bSrc, zSrc, nSrc) |
| 384 | /*have to dup input to ensure a NUL-terminated source string */; |
| 385 | pikchr_process(blob_str(&bSrc), pikFlags, 0, ob); |
| 386 | blob_reset(&bSrc); |
| 387 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -347,11 +347,12 @@ | |
| 347 | int pikFlags = PIKCHR_PROCESS_NONCE |
| 348 | | PIKCHR_PROCESS_DIV |
| 349 | | PIKCHR_PROCESS_SRC |
| 350 | | PIKCHR_PROCESS_ERR_PRE; |
| 351 | Blob bSrc = empty_blob; |
| 352 | const char *zPikVar; |
| 353 | double rPikVar; |
| 354 | |
| 355 | while( nArg>0 ){ |
| 356 | int i; |
| 357 | for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){} |
| 358 | if( i==6 && strncmp(zArg, "center", 6)==0 ){ |
| @@ -374,13 +375,27 @@ | |
| 375 | nArg -= i; |
| 376 | } |
| 377 | if( skin_detail_boolean("white-foreground") ){ |
| 378 | pikFlags |= 0x02; /* PIKCHR_DARK_MODE */ |
| 379 | } |
| 380 | zPikVar = skin_detail("pikchr-foreground"); |
| 381 | if( zPikVar && zPikVar[0] ){ |
| 382 | blob_appendf(&bSrc, "fgcolor = %s\n", zPikVar); |
| 383 | } |
| 384 | zPikVar = skin_detail("pikchr-scale"); |
| 385 | if( zPikVar |
| 386 | && (rPikVar = atof(zPikVar))>=0.1 |
| 387 | && rPikVar<10.0 |
| 388 | ){ |
| 389 | blob_appendf(&bSrc, "scale = %.13g\n", rPikVar); |
| 390 | } |
| 391 | zPikVar = skin_detail("pikchr-fontscale"); |
| 392 | if( zPikVar |
| 393 | && (rPikVar = atof(zPikVar))>=0.1 |
| 394 | && rPikVar<10.0 |
| 395 | ){ |
| 396 | blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar); |
| 397 | } |
| 398 | blob_append(&bSrc, zSrc, nSrc) |
| 399 | /*have to dup input to ensure a NUL-terminated source string */; |
| 400 | pikchr_process(blob_str(&bSrc), pikFlags, 0, ob); |
| 401 | blob_reset(&bSrc); |
| 402 |
+45
-15
| --- src/pikchr.c | ||
| +++ src/pikchr.c | ||
| @@ -3756,11 +3756,11 @@ | ||
| 3756 | 3756 | pik_append_arc(p, rad, rad, x0, y2); |
| 3757 | 3757 | if( y2>y1 ) pik_append_xy(p, "L", x0, y1); |
| 3758 | 3758 | pik_append_arc(p, rad, rad, x1, y0); |
| 3759 | 3759 | pik_append(p,"Z\" ",-1); |
| 3760 | 3760 | } |
| 3761 | - pik_append_style(p,pObj,1); | |
| 3761 | + pik_append_style(p,pObj,3); | |
| 3762 | 3762 | pik_append(p,"\" />\n", -1); |
| 3763 | 3763 | } |
| 3764 | 3764 | pik_append_txt(p, pObj, 0); |
| 3765 | 3765 | } |
| 3766 | 3766 | |
| @@ -3818,11 +3818,11 @@ | ||
| 3818 | 3818 | PPoint pt = pObj->ptAt; |
| 3819 | 3819 | if( pObj->sw>0.0 ){ |
| 3820 | 3820 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3821 | 3821 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3822 | 3822 | pik_append_dis(p," r=\"", r, "\" "); |
| 3823 | - pik_append_style(p,pObj,1); | |
| 3823 | + pik_append_style(p,pObj,3); | |
| 3824 | 3824 | pik_append(p,"\" />\n", -1); |
| 3825 | 3825 | } |
| 3826 | 3826 | pik_append_txt(p, pObj, 0); |
| 3827 | 3827 | } |
| 3828 | 3828 | |
| @@ -3832,11 +3832,11 @@ | ||
| 3832 | 3832 | pObj->h = pik_value(p, "cylht",5,0); |
| 3833 | 3833 | pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ |
| 3834 | 3834 | } |
| 3835 | 3835 | static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ |
| 3836 | 3836 | if( w>0 ) pObj->w = w; |
| 3837 | - if( h>0 ) pObj->h = h + 4*pObj->rad + pObj->sw; | |
| 3837 | + if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw; | |
| 3838 | 3838 | UNUSED_PARAMETER(p); |
| 3839 | 3839 | } |
| 3840 | 3840 | static void cylinderRender(Pik *p, PObj *pObj){ |
| 3841 | 3841 | PNum w2 = 0.5*pObj->w; |
| 3842 | 3842 | PNum h2 = 0.5*pObj->h; |
| @@ -3848,11 +3848,11 @@ | ||
| 3848 | 3848 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad); |
| 3849 | 3849 | pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad); |
| 3850 | 3850 | pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad); |
| 3851 | 3851 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad); |
| 3852 | 3852 | pik_append(p,"\" ",-1); |
| 3853 | - pik_append_style(p,pObj,1); | |
| 3853 | + pik_append_style(p,pObj,3); | |
| 3854 | 3854 | pik_append(p,"\" />\n", -1); |
| 3855 | 3855 | } |
| 3856 | 3856 | pik_append_txt(p, pObj, 0); |
| 3857 | 3857 | } |
| 3858 | 3858 | static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ |
| @@ -3910,11 +3910,11 @@ | ||
| 3910 | 3910 | PPoint pt = pObj->ptAt; |
| 3911 | 3911 | if( pObj->sw>0.0 ){ |
| 3912 | 3912 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3913 | 3913 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3914 | 3914 | pik_append_dis(p," r=\"", r, "\""); |
| 3915 | - pik_append_style(p,pObj,1); | |
| 3915 | + pik_append_style(p,pObj,2); | |
| 3916 | 3916 | pik_append(p,"\" />\n", -1); |
| 3917 | 3917 | } |
| 3918 | 3918 | pik_append_txt(p, pObj, 0); |
| 3919 | 3919 | } |
| 3920 | 3920 | |
| @@ -3969,11 +3969,11 @@ | ||
| 3969 | 3969 | if( pObj->sw>0.0 ){ |
| 3970 | 3970 | pik_append_x(p,"<ellipse cx=\"", pt.x, "\""); |
| 3971 | 3971 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3972 | 3972 | pik_append_dis(p," rx=\"", w/2.0, "\""); |
| 3973 | 3973 | pik_append_dis(p," ry=\"", h/2.0, "\" "); |
| 3974 | - pik_append_style(p,pObj,1); | |
| 3974 | + pik_append_style(p,pObj,3); | |
| 3975 | 3975 | pik_append(p,"\" />\n", -1); |
| 3976 | 3976 | } |
| 3977 | 3977 | pik_append_txt(p, pObj, 0); |
| 3978 | 3978 | } |
| 3979 | 3979 | |
| @@ -4081,11 +4081,11 @@ | ||
| 4081 | 4081 | pik_append(p,"Z",1); |
| 4082 | 4082 | }else{ |
| 4083 | 4083 | pObj->fill = -1.0; |
| 4084 | 4084 | } |
| 4085 | 4085 | pik_append(p,"\" ",-1); |
| 4086 | - pik_append_style(p,pObj,pObj->bClose); | |
| 4086 | + pik_append_style(p,pObj,pObj->bClose?3:0); | |
| 4087 | 4087 | pik_append(p,"\" />\n", -1); |
| 4088 | 4088 | } |
| 4089 | 4089 | pik_append_txt(p, pObj, 0); |
| 4090 | 4090 | } |
| 4091 | 4091 | |
| @@ -4181,11 +4181,11 @@ | ||
| 4181 | 4181 | pik_append(p,"Z",1); |
| 4182 | 4182 | }else{ |
| 4183 | 4183 | pObj->fill = -1.0; |
| 4184 | 4184 | } |
| 4185 | 4185 | pik_append(p,"\" ",-1); |
| 4186 | - pik_append_style(p,pObj,pObj->bClose); | |
| 4186 | + pik_append_style(p,pObj,pObj->bClose?3:0); | |
| 4187 | 4187 | pik_append(p,"\" />\n", -1); |
| 4188 | 4188 | } |
| 4189 | 4189 | static void splineRender(Pik *p, PObj *pObj){ |
| 4190 | 4190 | if( pObj->sw>0.0 ){ |
| 4191 | 4191 | int n = pObj->nPath; |
| @@ -4569,10 +4569,13 @@ | ||
| 4569 | 4569 | pik_append(p, buf, -1); |
| 4570 | 4570 | } |
| 4571 | 4571 | |
| 4572 | 4572 | /* |
| 4573 | 4573 | ** Invert the RGB color so that it is appropriate for dark mode. |
| 4574 | +** Variable x hold the initial color. The color is intended for use | |
| 4575 | +** as a background color if isBg is true, and as a foreground color | |
| 4576 | +** if isBg is false. | |
| 4574 | 4577 | */ |
| 4575 | 4578 | static int pik_color_to_dark_mode(int x, int isBg){ |
| 4576 | 4579 | int r, g, b; |
| 4577 | 4580 | int mn, mx; |
| 4578 | 4581 | x = 0xffffff - x; |
| @@ -4634,10 +4637,18 @@ | ||
| 4634 | 4637 | char buf[200]; |
| 4635 | 4638 | snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); |
| 4636 | 4639 | buf[sizeof(buf)-1] = 0; |
| 4637 | 4640 | pik_append(p, buf, -1); |
| 4638 | 4641 | } |
| 4642 | + | |
| 4643 | +/* Append a color specification to the output. | |
| 4644 | +** | |
| 4645 | +** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that | |
| 4646 | +** the color is intended for use as a background color if true, or as a | |
| 4647 | +** foreground color if false. The distinction only matters for color | |
| 4648 | +** inversions in PIKCHR_DARK_MODE. | |
| 4649 | +*/ | |
| 4639 | 4650 | static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ |
| 4640 | 4651 | char buf[200]; |
| 4641 | 4652 | int x = (int)v; |
| 4642 | 4653 | int r, g, b; |
| 4643 | 4654 | if( x==0 && p->fgcolor>0 && !bg ){ |
| @@ -4668,25 +4679,39 @@ | ||
| 4668 | 4679 | pik_append(p, buf, -1); |
| 4669 | 4680 | } |
| 4670 | 4681 | |
| 4671 | 4682 | /* Append a style="..." text. But, leave the quote unterminated, in case |
| 4672 | 4683 | ** the caller wants to add some more. |
| 4684 | +** | |
| 4685 | +** eFill is non-zero to fill in the background, or 0 if no fill should | |
| 4686 | +** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr() | |
| 4687 | +** for cases when pObj->fill==pObj->color | |
| 4688 | +** | |
| 4689 | +** 1 fill is background, and color is foreground. | |
| 4690 | +** 2 fill and color are both foreground. (Used by "dot" objects) | |
| 4691 | +** 3 fill and color are both background. (Used by most other objs) | |
| 4673 | 4692 | */ |
| 4674 | -static void pik_append_style(Pik *p, PObj *pObj, int bFill){ | |
| 4693 | +static void pik_append_style(Pik *p, PObj *pObj, int eFill){ | |
| 4694 | + int clrIsBg = 0; | |
| 4675 | 4695 | pik_append(p, " style=\"", -1); |
| 4676 | - if( pObj->fill>=0 && bFill ){ | |
| 4677 | - pik_append_clr(p, "fill:", pObj->fill, ";",1); | |
| 4696 | + if( pObj->fill>=0 && eFill ){ | |
| 4697 | + int fillIsBg = 1; | |
| 4698 | + if( pObj->fill==pObj->color ){ | |
| 4699 | + if( eFill==2 ) fillIsBg = 0; | |
| 4700 | + if( eFill==3 ) clrIsBg = 1; | |
| 4701 | + } | |
| 4702 | + pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg); | |
| 4678 | 4703 | }else{ |
| 4679 | 4704 | pik_append(p,"fill:none;",-1); |
| 4680 | 4705 | } |
| 4681 | 4706 | if( pObj->sw>0.0 && pObj->color>=0.0 ){ |
| 4682 | 4707 | PNum sw = pObj->sw; |
| 4683 | 4708 | pik_append_dis(p, "stroke-width:", sw, ";"); |
| 4684 | 4709 | if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ |
| 4685 | 4710 | pik_append(p, "stroke-linejoin:round;", -1); |
| 4686 | 4711 | } |
| 4687 | - pik_append_clr(p, "stroke:",pObj->color,";",0); | |
| 4712 | + pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg); | |
| 4688 | 4713 | if( pObj->dotted>0.0 ){ |
| 4689 | 4714 | PNum v = pObj->dotted; |
| 4690 | 4715 | if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; |
| 4691 | 4716 | pik_append_dis(p,"stroke-dasharray:",sw,""); |
| 4692 | 4717 | pik_append_dis(p,",",v,";"); |
| @@ -4802,10 +4827,11 @@ | ||
| 4802 | 4827 | PNum ha2 = 0.0; /* Height of the top row of text */ |
| 4803 | 4828 | PNum ha1 = 0.0; /* Height of the second "above" row */ |
| 4804 | 4829 | PNum hc = 0.0; /* Height of the center row */ |
| 4805 | 4830 | PNum hb1 = 0.0; /* Height of the first "below" row of text */ |
| 4806 | 4831 | PNum hb2 = 0.0; /* Height of the second "below" row */ |
| 4832 | + PNum yBase = 0.0; | |
| 4807 | 4833 | int n, i, nz; |
| 4808 | 4834 | PNum x, y, orig_y, s; |
| 4809 | 4835 | const char *z; |
| 4810 | 4836 | PToken *aTxt; |
| 4811 | 4837 | unsigned allMask = 0; |
| @@ -4815,11 +4841,15 @@ | ||
| 4815 | 4841 | aTxt = pObj->aTxt; |
| 4816 | 4842 | n = pObj->nTxt; |
| 4817 | 4843 | pik_txt_vertical_layout(pObj); |
| 4818 | 4844 | x = pObj->ptAt.x; |
| 4819 | 4845 | for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode; |
| 4820 | - if( pObj->type->isLine ) hc = pObj->sw*1.5; | |
| 4846 | + if( pObj->type->isLine ){ | |
| 4847 | + hc = pObj->sw*1.5; | |
| 4848 | + }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){ | |
| 4849 | + yBase = -0.75*pObj->rad; | |
| 4850 | + } | |
| 4821 | 4851 | if( allMask & TP_CENTER ){ |
| 4822 | 4852 | for(i=0; i<n; i++){ |
| 4823 | 4853 | if( pObj->aTxt[i].eCode & TP_CENTER ){ |
| 4824 | 4854 | s = pik_font_scale(pObj->aTxt+i); |
| 4825 | 4855 | if( hc<s*p->charHeight ) hc = s*p->charHeight; |
| @@ -4866,11 +4896,11 @@ | ||
| 4866 | 4896 | for(i=0; i<n; i++){ |
| 4867 | 4897 | PToken *t = &aTxt[i]; |
| 4868 | 4898 | PNum xtraFontScale = pik_font_scale(t); |
| 4869 | 4899 | PNum nx = 0; |
| 4870 | 4900 | orig_y = pObj->ptAt.y; |
| 4871 | - y = 0; | |
| 4901 | + y = yBase; | |
| 4872 | 4902 | if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; |
| 4873 | 4903 | if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; |
| 4874 | 4904 | if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; |
| 4875 | 4905 | if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; |
| 4876 | 4906 | if( t->eCode & TP_LJUST ) nx -= jw; |
| @@ -7852,6 +7882,6 @@ | ||
| 7852 | 7882 | |
| 7853 | 7883 | |
| 7854 | 7884 | #endif /* PIKCHR_TCL */ |
| 7855 | 7885 | |
| 7856 | 7886 | |
| 7857 | -#line 7882 "pikchr.c" | |
| 7887 | +#line 7912 "pikchr.c" | |
| 7858 | 7888 |
| --- src/pikchr.c | |
| +++ src/pikchr.c | |
| @@ -3756,11 +3756,11 @@ | |
| 3756 | pik_append_arc(p, rad, rad, x0, y2); |
| 3757 | if( y2>y1 ) pik_append_xy(p, "L", x0, y1); |
| 3758 | pik_append_arc(p, rad, rad, x1, y0); |
| 3759 | pik_append(p,"Z\" ",-1); |
| 3760 | } |
| 3761 | pik_append_style(p,pObj,1); |
| 3762 | pik_append(p,"\" />\n", -1); |
| 3763 | } |
| 3764 | pik_append_txt(p, pObj, 0); |
| 3765 | } |
| 3766 | |
| @@ -3818,11 +3818,11 @@ | |
| 3818 | PPoint pt = pObj->ptAt; |
| 3819 | if( pObj->sw>0.0 ){ |
| 3820 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3821 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3822 | pik_append_dis(p," r=\"", r, "\" "); |
| 3823 | pik_append_style(p,pObj,1); |
| 3824 | pik_append(p,"\" />\n", -1); |
| 3825 | } |
| 3826 | pik_append_txt(p, pObj, 0); |
| 3827 | } |
| 3828 | |
| @@ -3832,11 +3832,11 @@ | |
| 3832 | pObj->h = pik_value(p, "cylht",5,0); |
| 3833 | pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ |
| 3834 | } |
| 3835 | static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ |
| 3836 | if( w>0 ) pObj->w = w; |
| 3837 | if( h>0 ) pObj->h = h + 4*pObj->rad + pObj->sw; |
| 3838 | UNUSED_PARAMETER(p); |
| 3839 | } |
| 3840 | static void cylinderRender(Pik *p, PObj *pObj){ |
| 3841 | PNum w2 = 0.5*pObj->w; |
| 3842 | PNum h2 = 0.5*pObj->h; |
| @@ -3848,11 +3848,11 @@ | |
| 3848 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad); |
| 3849 | pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad); |
| 3850 | pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad); |
| 3851 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad); |
| 3852 | pik_append(p,"\" ",-1); |
| 3853 | pik_append_style(p,pObj,1); |
| 3854 | pik_append(p,"\" />\n", -1); |
| 3855 | } |
| 3856 | pik_append_txt(p, pObj, 0); |
| 3857 | } |
| 3858 | static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ |
| @@ -3910,11 +3910,11 @@ | |
| 3910 | PPoint pt = pObj->ptAt; |
| 3911 | if( pObj->sw>0.0 ){ |
| 3912 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3913 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3914 | pik_append_dis(p," r=\"", r, "\""); |
| 3915 | pik_append_style(p,pObj,1); |
| 3916 | pik_append(p,"\" />\n", -1); |
| 3917 | } |
| 3918 | pik_append_txt(p, pObj, 0); |
| 3919 | } |
| 3920 | |
| @@ -3969,11 +3969,11 @@ | |
| 3969 | if( pObj->sw>0.0 ){ |
| 3970 | pik_append_x(p,"<ellipse cx=\"", pt.x, "\""); |
| 3971 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3972 | pik_append_dis(p," rx=\"", w/2.0, "\""); |
| 3973 | pik_append_dis(p," ry=\"", h/2.0, "\" "); |
| 3974 | pik_append_style(p,pObj,1); |
| 3975 | pik_append(p,"\" />\n", -1); |
| 3976 | } |
| 3977 | pik_append_txt(p, pObj, 0); |
| 3978 | } |
| 3979 | |
| @@ -4081,11 +4081,11 @@ | |
| 4081 | pik_append(p,"Z",1); |
| 4082 | }else{ |
| 4083 | pObj->fill = -1.0; |
| 4084 | } |
| 4085 | pik_append(p,"\" ",-1); |
| 4086 | pik_append_style(p,pObj,pObj->bClose); |
| 4087 | pik_append(p,"\" />\n", -1); |
| 4088 | } |
| 4089 | pik_append_txt(p, pObj, 0); |
| 4090 | } |
| 4091 | |
| @@ -4181,11 +4181,11 @@ | |
| 4181 | pik_append(p,"Z",1); |
| 4182 | }else{ |
| 4183 | pObj->fill = -1.0; |
| 4184 | } |
| 4185 | pik_append(p,"\" ",-1); |
| 4186 | pik_append_style(p,pObj,pObj->bClose); |
| 4187 | pik_append(p,"\" />\n", -1); |
| 4188 | } |
| 4189 | static void splineRender(Pik *p, PObj *pObj){ |
| 4190 | if( pObj->sw>0.0 ){ |
| 4191 | int n = pObj->nPath; |
| @@ -4569,10 +4569,13 @@ | |
| 4569 | pik_append(p, buf, -1); |
| 4570 | } |
| 4571 | |
| 4572 | /* |
| 4573 | ** Invert the RGB color so that it is appropriate for dark mode. |
| 4574 | */ |
| 4575 | static int pik_color_to_dark_mode(int x, int isBg){ |
| 4576 | int r, g, b; |
| 4577 | int mn, mx; |
| 4578 | x = 0xffffff - x; |
| @@ -4634,10 +4637,18 @@ | |
| 4634 | char buf[200]; |
| 4635 | snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); |
| 4636 | buf[sizeof(buf)-1] = 0; |
| 4637 | pik_append(p, buf, -1); |
| 4638 | } |
| 4639 | static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ |
| 4640 | char buf[200]; |
| 4641 | int x = (int)v; |
| 4642 | int r, g, b; |
| 4643 | if( x==0 && p->fgcolor>0 && !bg ){ |
| @@ -4668,25 +4679,39 @@ | |
| 4668 | pik_append(p, buf, -1); |
| 4669 | } |
| 4670 | |
| 4671 | /* Append a style="..." text. But, leave the quote unterminated, in case |
| 4672 | ** the caller wants to add some more. |
| 4673 | */ |
| 4674 | static void pik_append_style(Pik *p, PObj *pObj, int bFill){ |
| 4675 | pik_append(p, " style=\"", -1); |
| 4676 | if( pObj->fill>=0 && bFill ){ |
| 4677 | pik_append_clr(p, "fill:", pObj->fill, ";",1); |
| 4678 | }else{ |
| 4679 | pik_append(p,"fill:none;",-1); |
| 4680 | } |
| 4681 | if( pObj->sw>0.0 && pObj->color>=0.0 ){ |
| 4682 | PNum sw = pObj->sw; |
| 4683 | pik_append_dis(p, "stroke-width:", sw, ";"); |
| 4684 | if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ |
| 4685 | pik_append(p, "stroke-linejoin:round;", -1); |
| 4686 | } |
| 4687 | pik_append_clr(p, "stroke:",pObj->color,";",0); |
| 4688 | if( pObj->dotted>0.0 ){ |
| 4689 | PNum v = pObj->dotted; |
| 4690 | if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; |
| 4691 | pik_append_dis(p,"stroke-dasharray:",sw,""); |
| 4692 | pik_append_dis(p,",",v,";"); |
| @@ -4802,10 +4827,11 @@ | |
| 4802 | PNum ha2 = 0.0; /* Height of the top row of text */ |
| 4803 | PNum ha1 = 0.0; /* Height of the second "above" row */ |
| 4804 | PNum hc = 0.0; /* Height of the center row */ |
| 4805 | PNum hb1 = 0.0; /* Height of the first "below" row of text */ |
| 4806 | PNum hb2 = 0.0; /* Height of the second "below" row */ |
| 4807 | int n, i, nz; |
| 4808 | PNum x, y, orig_y, s; |
| 4809 | const char *z; |
| 4810 | PToken *aTxt; |
| 4811 | unsigned allMask = 0; |
| @@ -4815,11 +4841,15 @@ | |
| 4815 | aTxt = pObj->aTxt; |
| 4816 | n = pObj->nTxt; |
| 4817 | pik_txt_vertical_layout(pObj); |
| 4818 | x = pObj->ptAt.x; |
| 4819 | for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode; |
| 4820 | if( pObj->type->isLine ) hc = pObj->sw*1.5; |
| 4821 | if( allMask & TP_CENTER ){ |
| 4822 | for(i=0; i<n; i++){ |
| 4823 | if( pObj->aTxt[i].eCode & TP_CENTER ){ |
| 4824 | s = pik_font_scale(pObj->aTxt+i); |
| 4825 | if( hc<s*p->charHeight ) hc = s*p->charHeight; |
| @@ -4866,11 +4896,11 @@ | |
| 4866 | for(i=0; i<n; i++){ |
| 4867 | PToken *t = &aTxt[i]; |
| 4868 | PNum xtraFontScale = pik_font_scale(t); |
| 4869 | PNum nx = 0; |
| 4870 | orig_y = pObj->ptAt.y; |
| 4871 | y = 0; |
| 4872 | if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; |
| 4873 | if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; |
| 4874 | if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; |
| 4875 | if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; |
| 4876 | if( t->eCode & TP_LJUST ) nx -= jw; |
| @@ -7852,6 +7882,6 @@ | |
| 7852 | |
| 7853 | |
| 7854 | #endif /* PIKCHR_TCL */ |
| 7855 | |
| 7856 | |
| 7857 | #line 7882 "pikchr.c" |
| 7858 |
| --- src/pikchr.c | |
| +++ src/pikchr.c | |
| @@ -3756,11 +3756,11 @@ | |
| 3756 | pik_append_arc(p, rad, rad, x0, y2); |
| 3757 | if( y2>y1 ) pik_append_xy(p, "L", x0, y1); |
| 3758 | pik_append_arc(p, rad, rad, x1, y0); |
| 3759 | pik_append(p,"Z\" ",-1); |
| 3760 | } |
| 3761 | pik_append_style(p,pObj,3); |
| 3762 | pik_append(p,"\" />\n", -1); |
| 3763 | } |
| 3764 | pik_append_txt(p, pObj, 0); |
| 3765 | } |
| 3766 | |
| @@ -3818,11 +3818,11 @@ | |
| 3818 | PPoint pt = pObj->ptAt; |
| 3819 | if( pObj->sw>0.0 ){ |
| 3820 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3821 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3822 | pik_append_dis(p," r=\"", r, "\" "); |
| 3823 | pik_append_style(p,pObj,3); |
| 3824 | pik_append(p,"\" />\n", -1); |
| 3825 | } |
| 3826 | pik_append_txt(p, pObj, 0); |
| 3827 | } |
| 3828 | |
| @@ -3832,11 +3832,11 @@ | |
| 3832 | pObj->h = pik_value(p, "cylht",5,0); |
| 3833 | pObj->rad = pik_value(p, "cylrad",6,0); /* Minor radius of ellipses */ |
| 3834 | } |
| 3835 | static void cylinderFit(Pik *p, PObj *pObj, PNum w, PNum h){ |
| 3836 | if( w>0 ) pObj->w = w; |
| 3837 | if( h>0 ) pObj->h = h + 0.25*pObj->rad + pObj->sw; |
| 3838 | UNUSED_PARAMETER(p); |
| 3839 | } |
| 3840 | static void cylinderRender(Pik *p, PObj *pObj){ |
| 3841 | PNum w2 = 0.5*pObj->w; |
| 3842 | PNum h2 = 0.5*pObj->h; |
| @@ -3848,11 +3848,11 @@ | |
| 3848 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y-h2+rad); |
| 3849 | pik_append_xy(p,"L", pt.x+w2,pt.y+h2-rad); |
| 3850 | pik_append_arc(p,w2,rad,pt.x-w2,pt.y+h2-rad); |
| 3851 | pik_append_arc(p,w2,rad,pt.x+w2,pt.y+h2-rad); |
| 3852 | pik_append(p,"\" ",-1); |
| 3853 | pik_append_style(p,pObj,3); |
| 3854 | pik_append(p,"\" />\n", -1); |
| 3855 | } |
| 3856 | pik_append_txt(p, pObj, 0); |
| 3857 | } |
| 3858 | static PPoint cylinderOffset(Pik *p, PObj *pObj, int cp){ |
| @@ -3910,11 +3910,11 @@ | |
| 3910 | PPoint pt = pObj->ptAt; |
| 3911 | if( pObj->sw>0.0 ){ |
| 3912 | pik_append_x(p,"<circle cx=\"", pt.x, "\""); |
| 3913 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3914 | pik_append_dis(p," r=\"", r, "\""); |
| 3915 | pik_append_style(p,pObj,2); |
| 3916 | pik_append(p,"\" />\n", -1); |
| 3917 | } |
| 3918 | pik_append_txt(p, pObj, 0); |
| 3919 | } |
| 3920 | |
| @@ -3969,11 +3969,11 @@ | |
| 3969 | if( pObj->sw>0.0 ){ |
| 3970 | pik_append_x(p,"<ellipse cx=\"", pt.x, "\""); |
| 3971 | pik_append_y(p," cy=\"", pt.y, "\""); |
| 3972 | pik_append_dis(p," rx=\"", w/2.0, "\""); |
| 3973 | pik_append_dis(p," ry=\"", h/2.0, "\" "); |
| 3974 | pik_append_style(p,pObj,3); |
| 3975 | pik_append(p,"\" />\n", -1); |
| 3976 | } |
| 3977 | pik_append_txt(p, pObj, 0); |
| 3978 | } |
| 3979 | |
| @@ -4081,11 +4081,11 @@ | |
| 4081 | pik_append(p,"Z",1); |
| 4082 | }else{ |
| 4083 | pObj->fill = -1.0; |
| 4084 | } |
| 4085 | pik_append(p,"\" ",-1); |
| 4086 | pik_append_style(p,pObj,pObj->bClose?3:0); |
| 4087 | pik_append(p,"\" />\n", -1); |
| 4088 | } |
| 4089 | pik_append_txt(p, pObj, 0); |
| 4090 | } |
| 4091 | |
| @@ -4181,11 +4181,11 @@ | |
| 4181 | pik_append(p,"Z",1); |
| 4182 | }else{ |
| 4183 | pObj->fill = -1.0; |
| 4184 | } |
| 4185 | pik_append(p,"\" ",-1); |
| 4186 | pik_append_style(p,pObj,pObj->bClose?3:0); |
| 4187 | pik_append(p,"\" />\n", -1); |
| 4188 | } |
| 4189 | static void splineRender(Pik *p, PObj *pObj){ |
| 4190 | if( pObj->sw>0.0 ){ |
| 4191 | int n = pObj->nPath; |
| @@ -4569,10 +4569,13 @@ | |
| 4569 | pik_append(p, buf, -1); |
| 4570 | } |
| 4571 | |
| 4572 | /* |
| 4573 | ** Invert the RGB color so that it is appropriate for dark mode. |
| 4574 | ** Variable x hold the initial color. The color is intended for use |
| 4575 | ** as a background color if isBg is true, and as a foreground color |
| 4576 | ** if isBg is false. |
| 4577 | */ |
| 4578 | static int pik_color_to_dark_mode(int x, int isBg){ |
| 4579 | int r, g, b; |
| 4580 | int mn, mx; |
| 4581 | x = 0xffffff - x; |
| @@ -4634,10 +4637,18 @@ | |
| 4637 | char buf[200]; |
| 4638 | snprintf(buf, sizeof(buf)-1, "%s%g%s", z1, p->rScale*v, z2); |
| 4639 | buf[sizeof(buf)-1] = 0; |
| 4640 | pik_append(p, buf, -1); |
| 4641 | } |
| 4642 | |
| 4643 | /* Append a color specification to the output. |
| 4644 | ** |
| 4645 | ** In PIKCHR_DARK_MODE, the color is inverted. The "bg" flags indicates that |
| 4646 | ** the color is intended for use as a background color if true, or as a |
| 4647 | ** foreground color if false. The distinction only matters for color |
| 4648 | ** inversions in PIKCHR_DARK_MODE. |
| 4649 | */ |
| 4650 | static void pik_append_clr(Pik *p,const char *z1,PNum v,const char *z2,int bg){ |
| 4651 | char buf[200]; |
| 4652 | int x = (int)v; |
| 4653 | int r, g, b; |
| 4654 | if( x==0 && p->fgcolor>0 && !bg ){ |
| @@ -4668,25 +4679,39 @@ | |
| 4679 | pik_append(p, buf, -1); |
| 4680 | } |
| 4681 | |
| 4682 | /* Append a style="..." text. But, leave the quote unterminated, in case |
| 4683 | ** the caller wants to add some more. |
| 4684 | ** |
| 4685 | ** eFill is non-zero to fill in the background, or 0 if no fill should |
| 4686 | ** occur. Non-zero values of eFill determine the "bg" flag to pik_append_clr() |
| 4687 | ** for cases when pObj->fill==pObj->color |
| 4688 | ** |
| 4689 | ** 1 fill is background, and color is foreground. |
| 4690 | ** 2 fill and color are both foreground. (Used by "dot" objects) |
| 4691 | ** 3 fill and color are both background. (Used by most other objs) |
| 4692 | */ |
| 4693 | static void pik_append_style(Pik *p, PObj *pObj, int eFill){ |
| 4694 | int clrIsBg = 0; |
| 4695 | pik_append(p, " style=\"", -1); |
| 4696 | if( pObj->fill>=0 && eFill ){ |
| 4697 | int fillIsBg = 1; |
| 4698 | if( pObj->fill==pObj->color ){ |
| 4699 | if( eFill==2 ) fillIsBg = 0; |
| 4700 | if( eFill==3 ) clrIsBg = 1; |
| 4701 | } |
| 4702 | pik_append_clr(p, "fill:", pObj->fill, ";", fillIsBg); |
| 4703 | }else{ |
| 4704 | pik_append(p,"fill:none;",-1); |
| 4705 | } |
| 4706 | if( pObj->sw>0.0 && pObj->color>=0.0 ){ |
| 4707 | PNum sw = pObj->sw; |
| 4708 | pik_append_dis(p, "stroke-width:", sw, ";"); |
| 4709 | if( pObj->nPath>2 && pObj->rad<=pObj->sw ){ |
| 4710 | pik_append(p, "stroke-linejoin:round;", -1); |
| 4711 | } |
| 4712 | pik_append_clr(p, "stroke:",pObj->color,";",clrIsBg); |
| 4713 | if( pObj->dotted>0.0 ){ |
| 4714 | PNum v = pObj->dotted; |
| 4715 | if( sw<2.1/p->rScale ) sw = 2.1/p->rScale; |
| 4716 | pik_append_dis(p,"stroke-dasharray:",sw,""); |
| 4717 | pik_append_dis(p,",",v,";"); |
| @@ -4802,10 +4827,11 @@ | |
| 4827 | PNum ha2 = 0.0; /* Height of the top row of text */ |
| 4828 | PNum ha1 = 0.0; /* Height of the second "above" row */ |
| 4829 | PNum hc = 0.0; /* Height of the center row */ |
| 4830 | PNum hb1 = 0.0; /* Height of the first "below" row of text */ |
| 4831 | PNum hb2 = 0.0; /* Height of the second "below" row */ |
| 4832 | PNum yBase = 0.0; |
| 4833 | int n, i, nz; |
| 4834 | PNum x, y, orig_y, s; |
| 4835 | const char *z; |
| 4836 | PToken *aTxt; |
| 4837 | unsigned allMask = 0; |
| @@ -4815,11 +4841,15 @@ | |
| 4841 | aTxt = pObj->aTxt; |
| 4842 | n = pObj->nTxt; |
| 4843 | pik_txt_vertical_layout(pObj); |
| 4844 | x = pObj->ptAt.x; |
| 4845 | for(i=0; i<n; i++) allMask |= pObj->aTxt[i].eCode; |
| 4846 | if( pObj->type->isLine ){ |
| 4847 | hc = pObj->sw*1.5; |
| 4848 | }else if( pObj->rad>0.0 && pObj->type->xInit==cylinderInit ){ |
| 4849 | yBase = -0.75*pObj->rad; |
| 4850 | } |
| 4851 | if( allMask & TP_CENTER ){ |
| 4852 | for(i=0; i<n; i++){ |
| 4853 | if( pObj->aTxt[i].eCode & TP_CENTER ){ |
| 4854 | s = pik_font_scale(pObj->aTxt+i); |
| 4855 | if( hc<s*p->charHeight ) hc = s*p->charHeight; |
| @@ -4866,11 +4896,11 @@ | |
| 4896 | for(i=0; i<n; i++){ |
| 4897 | PToken *t = &aTxt[i]; |
| 4898 | PNum xtraFontScale = pik_font_scale(t); |
| 4899 | PNum nx = 0; |
| 4900 | orig_y = pObj->ptAt.y; |
| 4901 | y = yBase; |
| 4902 | if( t->eCode & TP_ABOVE2 ) y += 0.5*hc + ha1 + 0.5*ha2; |
| 4903 | if( t->eCode & TP_ABOVE ) y += 0.5*hc + 0.5*ha1; |
| 4904 | if( t->eCode & TP_BELOW ) y -= 0.5*hc + 0.5*hb1; |
| 4905 | if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2; |
| 4906 | if( t->eCode & TP_LJUST ) nx -= jw; |
| @@ -7852,6 +7882,6 @@ | |
| 7882 | |
| 7883 | |
| 7884 | #endif /* PIKCHR_TCL */ |
| 7885 | |
| 7886 | |
| 7887 | #line 7912 "pikchr.c" |
| 7888 |
+2
-2
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -335,11 +335,11 @@ | ||
| 335 | 335 | CX("body.pikchrshow .v-align-middle{" |
| 336 | 336 | "vertical-align: middle" |
| 337 | 337 | "}\n"); |
| 338 | 338 | CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n"); |
| 339 | 339 | } CX("</style>"); |
| 340 | - CX("<div>Input pikchr code and tap Preview or Ctrl-Enter) to render " | |
| 340 | + CX("<div>Input pikchr code and tap Preview (or Ctrl-Enter) to render " | |
| 341 | 341 | "it:</div>"); |
| 342 | 342 | CX("<div id='sbs-wrapper'>"); { |
| 343 | 343 | CX("<div id='pikchrshow-form'>"); { |
| 344 | 344 | CX("<textarea id='content' name='content' rows='15'>" |
| 345 | 345 | "%s</textarea>",zContent/*safe-for-%s*/); |
| @@ -371,11 +371,11 @@ | ||
| 371 | 371 | blob_reset(&out); |
| 372 | 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | - "storage", "pikchr", 0); | |
| 376 | + "storage", "pikchr", NULL); | |
| 377 | 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | 378 | builtin_fulfill_js_requests(); |
| 379 | 379 | style_finish_page("pikchrshow"); |
| 380 | 380 | } |
| 381 | 381 | |
| 382 | 382 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -335,11 +335,11 @@ | |
| 335 | CX("body.pikchrshow .v-align-middle{" |
| 336 | "vertical-align: middle" |
| 337 | "}\n"); |
| 338 | CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n"); |
| 339 | } CX("</style>"); |
| 340 | CX("<div>Input pikchr code and tap Preview or Ctrl-Enter) to render " |
| 341 | "it:</div>"); |
| 342 | CX("<div id='sbs-wrapper'>"); { |
| 343 | CX("<div id='pikchrshow-form'>"); { |
| 344 | CX("<textarea id='content' name='content' rows='15'>" |
| 345 | "%s</textarea>",zContent/*safe-for-%s*/); |
| @@ -371,11 +371,11 @@ | |
| 371 | blob_reset(&out); |
| 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | "storage", "pikchr", 0); |
| 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | builtin_fulfill_js_requests(); |
| 379 | style_finish_page("pikchrshow"); |
| 380 | } |
| 381 | |
| 382 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -335,11 +335,11 @@ | |
| 335 | CX("body.pikchrshow .v-align-middle{" |
| 336 | "vertical-align: middle" |
| 337 | "}\n"); |
| 338 | CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n"); |
| 339 | } CX("</style>"); |
| 340 | CX("<div>Input pikchr code and tap Preview (or Ctrl-Enter) to render " |
| 341 | "it:</div>"); |
| 342 | CX("<div id='sbs-wrapper'>"); { |
| 343 | CX("<div id='pikchrshow-form'>"); { |
| 344 | CX("<textarea id='content' name='content' rows='15'>" |
| 345 | "%s</textarea>",zContent/*safe-for-%s*/); |
| @@ -371,11 +371,11 @@ | |
| 371 | blob_reset(&out); |
| 372 | } CX("</div>"/*#pikchrshow-output*/); |
| 373 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 374 | } CX("</div>"/*sbs-wrapper*/); |
| 375 | builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget", |
| 376 | "storage", "pikchr", NULL); |
| 377 | builtin_request_js("fossil.page.pikchrshow.js"); |
| 378 | builtin_fulfill_js_requests(); |
| 379 | style_finish_page("pikchrshow"); |
| 380 | } |
| 381 | |
| 382 |
+33
-14
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -134,13 +134,17 @@ | ||
| 134 | 134 | |
| 135 | 135 | |
| 136 | 136 | /* |
| 137 | 137 | ** The following table is searched linearly, so it is good to put the |
| 138 | 138 | ** most frequently used conversion types first. |
| 139 | +** | |
| 140 | +** NB: When modifying this table is it vital that you also update the fmtchr[] | |
| 141 | +** variable to match!!! | |
| 139 | 142 | */ |
| 140 | 143 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 141 | 144 | static const char aPrefix[] = "-x0\000X0"; |
| 145 | +static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; | |
| 142 | 146 | static const et_info fmtinfo[] = { |
| 143 | 147 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 144 | 148 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 145 | 149 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 146 | 150 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -170,12 +174,26 @@ | ||
| 170 | 174 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 171 | 175 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 172 | 176 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 173 | 177 | { '/', 0, 0, etPATH, 0, 0 }, |
| 174 | 178 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 179 | + { etERROR, 0,0,0,0,0} /* Must be last */ | |
| 175 | 180 | }; |
| 176 | 181 | #define etNINFO count(fmtinfo) |
| 182 | + | |
| 183 | +/* | |
| 184 | +** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement. | |
| 185 | +** | |
| 186 | +** This routine is a defense against programming errors. | |
| 187 | +*/ | |
| 188 | +void fossil_printf_selfcheck(void){ | |
| 189 | + int i; | |
| 190 | + for(i=0; fmtchr[i]; i++){ | |
| 191 | + assert( fmtchr[i]==fmtinfo[i].fmttype ); | |
| 192 | + } | |
| 193 | +} | |
| 194 | + | |
| 177 | 195 | |
| 178 | 196 | /* |
| 179 | 197 | ** "*val" is a double such that 0.1 <= *val < 10.0 |
| 180 | 198 | ** Return the ascii code for the leading digit of *val, then |
| 181 | 199 | ** multiply "*val" by 10.0 to renormalize. |
| @@ -306,22 +324,24 @@ | ||
| 306 | 324 | double rounder; /* Used for rounding floating point values */ |
| 307 | 325 | etByte flag_dp; /* True if decimal point should be shown */ |
| 308 | 326 | etByte flag_rtz; /* True if trailing zeros should be removed */ |
| 309 | 327 | etByte flag_exp; /* True to force display of the exponent */ |
| 310 | 328 | int nsd; /* Number of significant digits returned */ |
| 329 | + char *zFmtLookup; | |
| 311 | 330 | |
| 312 | 331 | count = length = 0; |
| 313 | 332 | bufpt = 0; |
| 314 | 333 | for(; (c=(*fmt))!=0; ++fmt){ |
| 315 | 334 | if( c!='%' ){ |
| 316 | - int amt; | |
| 317 | 335 | bufpt = (char *)fmt; |
| 318 | - amt = 1; | |
| 319 | - while( (c=(*++fmt))!='%' && c!=0 ) amt++; | |
| 320 | - blob_append(pBlob,bufpt,amt); | |
| 321 | - count += amt; | |
| 322 | - if( c==0 ) break; | |
| 336 | +#if HAVE_STRCHRNUL | |
| 337 | + fmt = strchrnul(fmt, '%'); | |
| 338 | +#else | |
| 339 | + do{ fmt++; }while( *fmt && *fmt != '%' ); | |
| 340 | +#endif | |
| 341 | + blob_append(pBlob, bufpt, (int)(fmt - bufpt)); | |
| 342 | + if( *fmt==0 ) break; | |
| 323 | 343 | } |
| 324 | 344 | if( (c=(*++fmt))==0 ){ |
| 325 | 345 | errorflag = 1; |
| 326 | 346 | blob_append(pBlob,"%",1); |
| 327 | 347 | count++; |
| @@ -390,18 +410,17 @@ | ||
| 390 | 410 | } |
| 391 | 411 | }else{ |
| 392 | 412 | flag_long = flag_longlong = 0; |
| 393 | 413 | } |
| 394 | 414 | /* Fetch the info entry for the field */ |
| 395 | - infop = 0; | |
| 396 | - xtype = etERROR; | |
| 397 | - for(idx=0; idx<etNINFO; idx++){ | |
| 398 | - if( c==fmtinfo[idx].fmttype ){ | |
| 399 | - infop = &fmtinfo[idx]; | |
| 400 | - xtype = infop->type; | |
| 401 | - break; | |
| 402 | - } | |
| 415 | + zFmtLookup = strchr(fmtchr,c); | |
| 416 | + if( zFmtLookup ){ | |
| 417 | + infop = &fmtinfo[zFmtLookup-fmtchr]; | |
| 418 | + xtype = infop->type; | |
| 419 | + }else{ | |
| 420 | + infop = 0; | |
| 421 | + xtype = etERROR; | |
| 403 | 422 | } |
| 404 | 423 | zExtra = 0; |
| 405 | 424 | |
| 406 | 425 | /* Limit the precision to prevent overflowing buf[] during conversion */ |
| 407 | 426 | if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ |
| 408 | 427 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -134,13 +134,17 @@ | |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | ** The following table is searched linearly, so it is good to put the |
| 138 | ** most frequently used conversion types first. |
| 139 | */ |
| 140 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 141 | static const char aPrefix[] = "-x0\000X0"; |
| 142 | static const et_info fmtinfo[] = { |
| 143 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 144 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 145 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 146 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -170,12 +174,26 @@ | |
| 170 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 171 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 172 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 173 | { '/', 0, 0, etPATH, 0, 0 }, |
| 174 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 175 | }; |
| 176 | #define etNINFO count(fmtinfo) |
| 177 | |
| 178 | /* |
| 179 | ** "*val" is a double such that 0.1 <= *val < 10.0 |
| 180 | ** Return the ascii code for the leading digit of *val, then |
| 181 | ** multiply "*val" by 10.0 to renormalize. |
| @@ -306,22 +324,24 @@ | |
| 306 | double rounder; /* Used for rounding floating point values */ |
| 307 | etByte flag_dp; /* True if decimal point should be shown */ |
| 308 | etByte flag_rtz; /* True if trailing zeros should be removed */ |
| 309 | etByte flag_exp; /* True to force display of the exponent */ |
| 310 | int nsd; /* Number of significant digits returned */ |
| 311 | |
| 312 | count = length = 0; |
| 313 | bufpt = 0; |
| 314 | for(; (c=(*fmt))!=0; ++fmt){ |
| 315 | if( c!='%' ){ |
| 316 | int amt; |
| 317 | bufpt = (char *)fmt; |
| 318 | amt = 1; |
| 319 | while( (c=(*++fmt))!='%' && c!=0 ) amt++; |
| 320 | blob_append(pBlob,bufpt,amt); |
| 321 | count += amt; |
| 322 | if( c==0 ) break; |
| 323 | } |
| 324 | if( (c=(*++fmt))==0 ){ |
| 325 | errorflag = 1; |
| 326 | blob_append(pBlob,"%",1); |
| 327 | count++; |
| @@ -390,18 +410,17 @@ | |
| 390 | } |
| 391 | }else{ |
| 392 | flag_long = flag_longlong = 0; |
| 393 | } |
| 394 | /* Fetch the info entry for the field */ |
| 395 | infop = 0; |
| 396 | xtype = etERROR; |
| 397 | for(idx=0; idx<etNINFO; idx++){ |
| 398 | if( c==fmtinfo[idx].fmttype ){ |
| 399 | infop = &fmtinfo[idx]; |
| 400 | xtype = infop->type; |
| 401 | break; |
| 402 | } |
| 403 | } |
| 404 | zExtra = 0; |
| 405 | |
| 406 | /* Limit the precision to prevent overflowing buf[] during conversion */ |
| 407 | if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ |
| 408 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -134,13 +134,17 @@ | |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | ** The following table is searched linearly, so it is good to put the |
| 138 | ** most frequently used conversion types first. |
| 139 | ** |
| 140 | ** NB: When modifying this table is it vital that you also update the fmtchr[] |
| 141 | ** variable to match!!! |
| 142 | */ |
| 143 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 144 | static const char aPrefix[] = "-x0\000X0"; |
| 145 | static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; |
| 146 | static const et_info fmtinfo[] = { |
| 147 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 148 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 149 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 150 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -170,12 +174,26 @@ | |
| 174 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 175 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 176 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 177 | { '/', 0, 0, etPATH, 0, 0 }, |
| 178 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 179 | { etERROR, 0,0,0,0,0} /* Must be last */ |
| 180 | }; |
| 181 | #define etNINFO count(fmtinfo) |
| 182 | |
| 183 | /* |
| 184 | ** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement. |
| 185 | ** |
| 186 | ** This routine is a defense against programming errors. |
| 187 | */ |
| 188 | void fossil_printf_selfcheck(void){ |
| 189 | int i; |
| 190 | for(i=0; fmtchr[i]; i++){ |
| 191 | assert( fmtchr[i]==fmtinfo[i].fmttype ); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | |
| 196 | /* |
| 197 | ** "*val" is a double such that 0.1 <= *val < 10.0 |
| 198 | ** Return the ascii code for the leading digit of *val, then |
| 199 | ** multiply "*val" by 10.0 to renormalize. |
| @@ -306,22 +324,24 @@ | |
| 324 | double rounder; /* Used for rounding floating point values */ |
| 325 | etByte flag_dp; /* True if decimal point should be shown */ |
| 326 | etByte flag_rtz; /* True if trailing zeros should be removed */ |
| 327 | etByte flag_exp; /* True to force display of the exponent */ |
| 328 | int nsd; /* Number of significant digits returned */ |
| 329 | char *zFmtLookup; |
| 330 | |
| 331 | count = length = 0; |
| 332 | bufpt = 0; |
| 333 | for(; (c=(*fmt))!=0; ++fmt){ |
| 334 | if( c!='%' ){ |
| 335 | bufpt = (char *)fmt; |
| 336 | #if HAVE_STRCHRNUL |
| 337 | fmt = strchrnul(fmt, '%'); |
| 338 | #else |
| 339 | do{ fmt++; }while( *fmt && *fmt != '%' ); |
| 340 | #endif |
| 341 | blob_append(pBlob, bufpt, (int)(fmt - bufpt)); |
| 342 | if( *fmt==0 ) break; |
| 343 | } |
| 344 | if( (c=(*++fmt))==0 ){ |
| 345 | errorflag = 1; |
| 346 | blob_append(pBlob,"%",1); |
| 347 | count++; |
| @@ -390,18 +410,17 @@ | |
| 410 | } |
| 411 | }else{ |
| 412 | flag_long = flag_longlong = 0; |
| 413 | } |
| 414 | /* Fetch the info entry for the field */ |
| 415 | zFmtLookup = strchr(fmtchr,c); |
| 416 | if( zFmtLookup ){ |
| 417 | infop = &fmtinfo[zFmtLookup-fmtchr]; |
| 418 | xtype = infop->type; |
| 419 | }else{ |
| 420 | infop = 0; |
| 421 | xtype = etERROR; |
| 422 | } |
| 423 | zExtra = 0; |
| 424 | |
| 425 | /* Limit the precision to prevent overflowing buf[] during conversion */ |
| 426 | if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ |
| 427 |
+39
-16
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -5420,14 +5420,14 @@ | ||
| 5420 | 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | 5421 | ** result set of queries against generate_series will look like. |
| 5422 | 5422 | */ |
| 5423 | 5423 | static int seriesConnect( |
| 5424 | 5424 | sqlite3 *db, |
| 5425 | - void *pAux, | |
| 5426 | - int argc, const char *const*argv, | |
| 5425 | + void *pUnused, | |
| 5426 | + int argcUnused, const char *const*argvUnused, | |
| 5427 | 5427 | sqlite3_vtab **ppVtab, |
| 5428 | - char **pzErr | |
| 5428 | + char **pzErrUnused | |
| 5429 | 5429 | ){ |
| 5430 | 5430 | sqlite3_vtab *pNew; |
| 5431 | 5431 | int rc; |
| 5432 | 5432 | |
| 5433 | 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | ||
| 5434 | 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | 5438 | |
| 5439 | + (void)pUnused; | |
| 5440 | + (void)argcUnused; | |
| 5441 | + (void)argvUnused; | |
| 5442 | + (void)pzErrUnused; | |
| 5439 | 5443 | rc = sqlite3_declare_vtab(db, |
| 5440 | 5444 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5441 | 5445 | if( rc==SQLITE_OK ){ |
| 5442 | 5446 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5443 | 5447 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | ||
| 5456 | 5460 | } |
| 5457 | 5461 | |
| 5458 | 5462 | /* |
| 5459 | 5463 | ** Constructor for a new series_cursor object. |
| 5460 | 5464 | */ |
| 5461 | -static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ | |
| 5465 | +static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){ | |
| 5462 | 5466 | series_cursor *pCur; |
| 5467 | + (void)pUnused; | |
| 5463 | 5468 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5464 | 5469 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5465 | 5470 | memset(pCur, 0, sizeof(*pCur)); |
| 5466 | 5471 | *ppCursor = &pCur->base; |
| 5467 | 5472 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | ||
| 5564 | 5569 | ** is pointing at the first row, or pointing off the end of the table |
| 5565 | 5570 | ** (so that seriesEof() will return true) if the table is empty. |
| 5566 | 5571 | */ |
| 5567 | 5572 | static int seriesFilter( |
| 5568 | 5573 | sqlite3_vtab_cursor *pVtabCursor, |
| 5569 | - int idxNum, const char *idxStr, | |
| 5574 | + int idxNum, const char *idxStrUnused, | |
| 5570 | 5575 | int argc, sqlite3_value **argv |
| 5571 | 5576 | ){ |
| 5572 | 5577 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5573 | 5578 | int i = 0; |
| 5579 | + (void)idxStrUnused; | |
| 5574 | 5580 | if( idxNum & 1 ){ |
| 5575 | 5581 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5576 | 5582 | }else{ |
| 5577 | 5583 | pCur->mnValue = 0; |
| 5578 | 5584 | } |
| @@ -5625,11 +5631,11 @@ | ||
| 5625 | 5631 | ** (2) stop = $value -- constraint exists |
| 5626 | 5632 | ** (4) step = $value -- constraint exists |
| 5627 | 5633 | ** (8) output in descending order |
| 5628 | 5634 | */ |
| 5629 | 5635 | static int seriesBestIndex( |
| 5630 | - sqlite3_vtab *tab, | |
| 5636 | + sqlite3_vtab *tabUnused, | |
| 5631 | 5637 | sqlite3_index_info *pIdxInfo |
| 5632 | 5638 | ){ |
| 5633 | 5639 | int i, j; /* Loop over constraints */ |
| 5634 | 5640 | int idxNum = 0; /* The query plan bitmask */ |
| 5635 | 5641 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | ||
| 5639 | 5645 | |
| 5640 | 5646 | /* This implementation assumes that the start, stop, and step columns |
| 5641 | 5647 | ** are the last three columns in the virtual table. */ |
| 5642 | 5648 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5643 | 5649 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5650 | + (void)tabUnused; | |
| 5644 | 5651 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5645 | 5652 | pConstraint = pIdxInfo->aConstraint; |
| 5646 | 5653 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5647 | 5654 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5648 | 5655 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | ||
| 5712 | 5719 | 0, /* xSync */ |
| 5713 | 5720 | 0, /* xCommit */ |
| 5714 | 5721 | 0, /* xRollback */ |
| 5715 | 5722 | 0, /* xFindMethod */ |
| 5716 | 5723 | 0, /* xRename */ |
| 5724 | + 0, /* xSavepoint */ | |
| 5725 | + 0, /* xRelease */ | |
| 5726 | + 0, /* xRollbackTo */ | |
| 5727 | + 0 /* xShadowName */ | |
| 5717 | 5728 | }; |
| 5718 | 5729 | |
| 5719 | 5730 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5720 | 5731 | |
| 5721 | 5732 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | ||
| 14455 | 14466 | /* |
| 14456 | 14467 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14457 | 14468 | */ |
| 14458 | 14469 | static void shellUSleepFunc( |
| 14459 | 14470 | sqlite3_context *context, |
| 14460 | - int argc, | |
| 14471 | + int argcUnused, | |
| 14461 | 14472 | sqlite3_value **argv |
| 14462 | 14473 | ){ |
| 14463 | 14474 | int sleep = sqlite3_value_int(argv[0]); |
| 14475 | + (void)argcUnused; | |
| 14464 | 14476 | sqlite3_sleep(sleep/1000); |
| 14465 | 14477 | sqlite3_result_int(context, sleep); |
| 14466 | 14478 | } |
| 14467 | 14479 | |
| 14468 | 14480 | /* |
| @@ -20657,12 +20669,15 @@ | ||
| 20657 | 20669 | p->in = fopen(sqliterc,"rb"); |
| 20658 | 20670 | if( p->in ){ |
| 20659 | 20671 | if( stdin_is_interactive ){ |
| 20660 | 20672 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20661 | 20673 | } |
| 20662 | - process_input(p); | |
| 20674 | + if( process_input(p) && bail_on_error ) exit(1); | |
| 20663 | 20675 | fclose(p->in); |
| 20676 | + }else if( sqliterc_override!=0 ){ | |
| 20677 | + utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); | |
| 20678 | + if( bail_on_error ) exit(1); | |
| 20664 | 20679 | } |
| 20665 | 20680 | p->in = inSaved; |
| 20666 | 20681 | p->lineno = savedLineno; |
| 20667 | 20682 | sqlite3_free(zBuf); |
| 20668 | 20683 | } |
| @@ -21044,10 +21059,12 @@ | ||
| 21044 | 21059 | ** command, so ignore them */ |
| 21045 | 21060 | break; |
| 21046 | 21061 | #endif |
| 21047 | 21062 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21048 | 21063 | sqlite3MemTraceActivate(stderr); |
| 21064 | + }else if( strcmp(z,"-bail")==0 ){ | |
| 21065 | + bail_on_error = 1; | |
| 21049 | 21066 | } |
| 21050 | 21067 | } |
| 21051 | 21068 | verify_uninitialized(); |
| 21052 | 21069 | |
| 21053 | 21070 | |
| @@ -21190,11 +21207,11 @@ | ||
| 21190 | 21207 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21191 | 21208 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21192 | 21209 | */ |
| 21193 | 21210 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21194 | 21211 | }else if( strcmp(z,"-bail")==0 ){ |
| 21195 | - bail_on_error = 1; | |
| 21212 | + /* No-op. The bail_on_error flag should already be set. */ | |
| 21196 | 21213 | }else if( strcmp(z,"-version")==0 ){ |
| 21197 | 21214 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21198 | 21215 | return 0; |
| 21199 | 21216 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21200 | 21217 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | ||
| 21278 | 21295 | ** the database filename. |
| 21279 | 21296 | */ |
| 21280 | 21297 | for(i=0; i<nCmd; i++){ |
| 21281 | 21298 | if( azCmd[i][0]=='.' ){ |
| 21282 | 21299 | rc = do_meta_command(azCmd[i], &data); |
| 21283 | - if( rc ) return rc==2 ? 0 : rc; | |
| 21300 | + if( rc ){ | |
| 21301 | + free(azCmd); | |
| 21302 | + return rc==2 ? 0 : rc; | |
| 21303 | + } | |
| 21284 | 21304 | }else{ |
| 21285 | 21305 | open_db(&data, 0); |
| 21286 | 21306 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21287 | - if( zErrMsg!=0 ){ | |
| 21288 | - utf8_printf(stderr,"Error: %s\n", zErrMsg); | |
| 21307 | + if( zErrMsg || rc ){ | |
| 21308 | + if( zErrMsg!=0 ){ | |
| 21309 | + utf8_printf(stderr,"Error: %s\n", zErrMsg); | |
| 21310 | + }else{ | |
| 21311 | + utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); | |
| 21312 | + } | |
| 21313 | + sqlite3_free(zErrMsg); | |
| 21314 | + free(azCmd); | |
| 21289 | 21315 | return rc!=0 ? rc : 1; |
| 21290 | - }else if( rc!=0 ){ | |
| 21291 | - utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); | |
| 21292 | - return rc; | |
| 21293 | 21316 | } |
| 21294 | 21317 | } |
| 21295 | 21318 | } |
| 21296 | - free(azCmd); | |
| 21297 | 21319 | }else{ |
| 21298 | 21320 | /* Run commands received from standard input |
| 21299 | 21321 | */ |
| 21300 | 21322 | if( stdin_is_interactive ){ |
| 21301 | 21323 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | ||
| 21337 | 21359 | }else{ |
| 21338 | 21360 | data.in = stdin; |
| 21339 | 21361 | rc = process_input(&data); |
| 21340 | 21362 | } |
| 21341 | 21363 | } |
| 21364 | + free(azCmd); | |
| 21342 | 21365 | set_table_name(&data, 0); |
| 21343 | 21366 | if( data.db ){ |
| 21344 | 21367 | session_close_all(&data); |
| 21345 | 21368 | close_db(data.db); |
| 21346 | 21369 | } |
| 21347 | 21370 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -5420,14 +5420,14 @@ | |
| 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | ** result set of queries against generate_series will look like. |
| 5422 | */ |
| 5423 | static int seriesConnect( |
| 5424 | sqlite3 *db, |
| 5425 | void *pAux, |
| 5426 | int argc, const char *const*argv, |
| 5427 | sqlite3_vtab **ppVtab, |
| 5428 | char **pzErr |
| 5429 | ){ |
| 5430 | sqlite3_vtab *pNew; |
| 5431 | int rc; |
| 5432 | |
| 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | |
| 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | |
| 5439 | rc = sqlite3_declare_vtab(db, |
| 5440 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5441 | if( rc==SQLITE_OK ){ |
| 5442 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5443 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | |
| 5456 | } |
| 5457 | |
| 5458 | /* |
| 5459 | ** Constructor for a new series_cursor object. |
| 5460 | */ |
| 5461 | static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| 5462 | series_cursor *pCur; |
| 5463 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5464 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5465 | memset(pCur, 0, sizeof(*pCur)); |
| 5466 | *ppCursor = &pCur->base; |
| 5467 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | |
| 5564 | ** is pointing at the first row, or pointing off the end of the table |
| 5565 | ** (so that seriesEof() will return true) if the table is empty. |
| 5566 | */ |
| 5567 | static int seriesFilter( |
| 5568 | sqlite3_vtab_cursor *pVtabCursor, |
| 5569 | int idxNum, const char *idxStr, |
| 5570 | int argc, sqlite3_value **argv |
| 5571 | ){ |
| 5572 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5573 | int i = 0; |
| 5574 | if( idxNum & 1 ){ |
| 5575 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5576 | }else{ |
| 5577 | pCur->mnValue = 0; |
| 5578 | } |
| @@ -5625,11 +5631,11 @@ | |
| 5625 | ** (2) stop = $value -- constraint exists |
| 5626 | ** (4) step = $value -- constraint exists |
| 5627 | ** (8) output in descending order |
| 5628 | */ |
| 5629 | static int seriesBestIndex( |
| 5630 | sqlite3_vtab *tab, |
| 5631 | sqlite3_index_info *pIdxInfo |
| 5632 | ){ |
| 5633 | int i, j; /* Loop over constraints */ |
| 5634 | int idxNum = 0; /* The query plan bitmask */ |
| 5635 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | |
| 5639 | |
| 5640 | /* This implementation assumes that the start, stop, and step columns |
| 5641 | ** are the last three columns in the virtual table. */ |
| 5642 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5643 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5644 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5645 | pConstraint = pIdxInfo->aConstraint; |
| 5646 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5647 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5648 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | |
| 5712 | 0, /* xSync */ |
| 5713 | 0, /* xCommit */ |
| 5714 | 0, /* xRollback */ |
| 5715 | 0, /* xFindMethod */ |
| 5716 | 0, /* xRename */ |
| 5717 | }; |
| 5718 | |
| 5719 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5720 | |
| 5721 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | |
| 14455 | /* |
| 14456 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14457 | */ |
| 14458 | static void shellUSleepFunc( |
| 14459 | sqlite3_context *context, |
| 14460 | int argc, |
| 14461 | sqlite3_value **argv |
| 14462 | ){ |
| 14463 | int sleep = sqlite3_value_int(argv[0]); |
| 14464 | sqlite3_sleep(sleep/1000); |
| 14465 | sqlite3_result_int(context, sleep); |
| 14466 | } |
| 14467 | |
| 14468 | /* |
| @@ -20657,12 +20669,15 @@ | |
| 20657 | p->in = fopen(sqliterc,"rb"); |
| 20658 | if( p->in ){ |
| 20659 | if( stdin_is_interactive ){ |
| 20660 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20661 | } |
| 20662 | process_input(p); |
| 20663 | fclose(p->in); |
| 20664 | } |
| 20665 | p->in = inSaved; |
| 20666 | p->lineno = savedLineno; |
| 20667 | sqlite3_free(zBuf); |
| 20668 | } |
| @@ -21044,10 +21059,12 @@ | |
| 21044 | ** command, so ignore them */ |
| 21045 | break; |
| 21046 | #endif |
| 21047 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21048 | sqlite3MemTraceActivate(stderr); |
| 21049 | } |
| 21050 | } |
| 21051 | verify_uninitialized(); |
| 21052 | |
| 21053 | |
| @@ -21190,11 +21207,11 @@ | |
| 21190 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21191 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21192 | */ |
| 21193 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21194 | }else if( strcmp(z,"-bail")==0 ){ |
| 21195 | bail_on_error = 1; |
| 21196 | }else if( strcmp(z,"-version")==0 ){ |
| 21197 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21198 | return 0; |
| 21199 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21200 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | |
| 21278 | ** the database filename. |
| 21279 | */ |
| 21280 | for(i=0; i<nCmd; i++){ |
| 21281 | if( azCmd[i][0]=='.' ){ |
| 21282 | rc = do_meta_command(azCmd[i], &data); |
| 21283 | if( rc ) return rc==2 ? 0 : rc; |
| 21284 | }else{ |
| 21285 | open_db(&data, 0); |
| 21286 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21287 | if( zErrMsg!=0 ){ |
| 21288 | utf8_printf(stderr,"Error: %s\n", zErrMsg); |
| 21289 | return rc!=0 ? rc : 1; |
| 21290 | }else if( rc!=0 ){ |
| 21291 | utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); |
| 21292 | return rc; |
| 21293 | } |
| 21294 | } |
| 21295 | } |
| 21296 | free(azCmd); |
| 21297 | }else{ |
| 21298 | /* Run commands received from standard input |
| 21299 | */ |
| 21300 | if( stdin_is_interactive ){ |
| 21301 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | |
| 21337 | }else{ |
| 21338 | data.in = stdin; |
| 21339 | rc = process_input(&data); |
| 21340 | } |
| 21341 | } |
| 21342 | set_table_name(&data, 0); |
| 21343 | if( data.db ){ |
| 21344 | session_close_all(&data); |
| 21345 | close_db(data.db); |
| 21346 | } |
| 21347 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -5420,14 +5420,14 @@ | |
| 5420 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 5421 | ** result set of queries against generate_series will look like. |
| 5422 | */ |
| 5423 | static int seriesConnect( |
| 5424 | sqlite3 *db, |
| 5425 | void *pUnused, |
| 5426 | int argcUnused, const char *const*argvUnused, |
| 5427 | sqlite3_vtab **ppVtab, |
| 5428 | char **pzErrUnused |
| 5429 | ){ |
| 5430 | sqlite3_vtab *pNew; |
| 5431 | int rc; |
| 5432 | |
| 5433 | /* Column numbers */ |
| @@ -5434,10 +5434,14 @@ | |
| 5434 | #define SERIES_COLUMN_VALUE 0 |
| 5435 | #define SERIES_COLUMN_START 1 |
| 5436 | #define SERIES_COLUMN_STOP 2 |
| 5437 | #define SERIES_COLUMN_STEP 3 |
| 5438 | |
| 5439 | (void)pUnused; |
| 5440 | (void)argcUnused; |
| 5441 | (void)argvUnused; |
| 5442 | (void)pzErrUnused; |
| 5443 | rc = sqlite3_declare_vtab(db, |
| 5444 | "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); |
| 5445 | if( rc==SQLITE_OK ){ |
| 5446 | pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
| 5447 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -5456,12 +5460,13 @@ | |
| 5460 | } |
| 5461 | |
| 5462 | /* |
| 5463 | ** Constructor for a new series_cursor object. |
| 5464 | */ |
| 5465 | static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){ |
| 5466 | series_cursor *pCur; |
| 5467 | (void)pUnused; |
| 5468 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 5469 | if( pCur==0 ) return SQLITE_NOMEM; |
| 5470 | memset(pCur, 0, sizeof(*pCur)); |
| 5471 | *ppCursor = &pCur->base; |
| 5472 | return SQLITE_OK; |
| @@ -5564,15 +5569,16 @@ | |
| 5569 | ** is pointing at the first row, or pointing off the end of the table |
| 5570 | ** (so that seriesEof() will return true) if the table is empty. |
| 5571 | */ |
| 5572 | static int seriesFilter( |
| 5573 | sqlite3_vtab_cursor *pVtabCursor, |
| 5574 | int idxNum, const char *idxStrUnused, |
| 5575 | int argc, sqlite3_value **argv |
| 5576 | ){ |
| 5577 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 5578 | int i = 0; |
| 5579 | (void)idxStrUnused; |
| 5580 | if( idxNum & 1 ){ |
| 5581 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 5582 | }else{ |
| 5583 | pCur->mnValue = 0; |
| 5584 | } |
| @@ -5625,11 +5631,11 @@ | |
| 5631 | ** (2) stop = $value -- constraint exists |
| 5632 | ** (4) step = $value -- constraint exists |
| 5633 | ** (8) output in descending order |
| 5634 | */ |
| 5635 | static int seriesBestIndex( |
| 5636 | sqlite3_vtab *tabUnused, |
| 5637 | sqlite3_index_info *pIdxInfo |
| 5638 | ){ |
| 5639 | int i, j; /* Loop over constraints */ |
| 5640 | int idxNum = 0; /* The query plan bitmask */ |
| 5641 | int unusableMask = 0; /* Mask of unusable constraints */ |
| @@ -5639,10 +5645,11 @@ | |
| 5645 | |
| 5646 | /* This implementation assumes that the start, stop, and step columns |
| 5647 | ** are the last three columns in the virtual table. */ |
| 5648 | assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); |
| 5649 | assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); |
| 5650 | (void)tabUnused; |
| 5651 | aIdx[0] = aIdx[1] = aIdx[2] = -1; |
| 5652 | pConstraint = pIdxInfo->aConstraint; |
| 5653 | for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
| 5654 | int iCol; /* 0 for start, 1 for stop, 2 for step */ |
| 5655 | int iMask; /* bitmask for those column */ |
| @@ -5712,10 +5719,14 @@ | |
| 5719 | 0, /* xSync */ |
| 5720 | 0, /* xCommit */ |
| 5721 | 0, /* xRollback */ |
| 5722 | 0, /* xFindMethod */ |
| 5723 | 0, /* xRename */ |
| 5724 | 0, /* xSavepoint */ |
| 5725 | 0, /* xRelease */ |
| 5726 | 0, /* xRollbackTo */ |
| 5727 | 0 /* xShadowName */ |
| 5728 | }; |
| 5729 | |
| 5730 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5731 | |
| 5732 | #ifdef _WIN32 |
| @@ -14455,14 +14466,15 @@ | |
| 14466 | /* |
| 14467 | ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. |
| 14468 | */ |
| 14469 | static void shellUSleepFunc( |
| 14470 | sqlite3_context *context, |
| 14471 | int argcUnused, |
| 14472 | sqlite3_value **argv |
| 14473 | ){ |
| 14474 | int sleep = sqlite3_value_int(argv[0]); |
| 14475 | (void)argcUnused; |
| 14476 | sqlite3_sleep(sleep/1000); |
| 14477 | sqlite3_result_int(context, sleep); |
| 14478 | } |
| 14479 | |
| 14480 | /* |
| @@ -20657,12 +20669,15 @@ | |
| 20669 | p->in = fopen(sqliterc,"rb"); |
| 20670 | if( p->in ){ |
| 20671 | if( stdin_is_interactive ){ |
| 20672 | utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
| 20673 | } |
| 20674 | if( process_input(p) && bail_on_error ) exit(1); |
| 20675 | fclose(p->in); |
| 20676 | }else if( sqliterc_override!=0 ){ |
| 20677 | utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 20678 | if( bail_on_error ) exit(1); |
| 20679 | } |
| 20680 | p->in = inSaved; |
| 20681 | p->lineno = savedLineno; |
| 20682 | sqlite3_free(zBuf); |
| 20683 | } |
| @@ -21044,10 +21059,12 @@ | |
| 21059 | ** command, so ignore them */ |
| 21060 | break; |
| 21061 | #endif |
| 21062 | }else if( strcmp(z, "-memtrace")==0 ){ |
| 21063 | sqlite3MemTraceActivate(stderr); |
| 21064 | }else if( strcmp(z,"-bail")==0 ){ |
| 21065 | bail_on_error = 1; |
| 21066 | } |
| 21067 | } |
| 21068 | verify_uninitialized(); |
| 21069 | |
| 21070 | |
| @@ -21190,11 +21207,11 @@ | |
| 21207 | ** prior to sending the SQL into SQLite. Useful for injecting |
| 21208 | ** crazy bytes in the middle of SQL statements for testing and debugging. |
| 21209 | */ |
| 21210 | ShellSetFlag(&data, SHFLG_Backslash); |
| 21211 | }else if( strcmp(z,"-bail")==0 ){ |
| 21212 | /* No-op. The bail_on_error flag should already be set. */ |
| 21213 | }else if( strcmp(z,"-version")==0 ){ |
| 21214 | printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); |
| 21215 | return 0; |
| 21216 | }else if( strcmp(z,"-interactive")==0 ){ |
| 21217 | stdin_is_interactive = 1; |
| @@ -21278,24 +21295,29 @@ | |
| 21295 | ** the database filename. |
| 21296 | */ |
| 21297 | for(i=0; i<nCmd; i++){ |
| 21298 | if( azCmd[i][0]=='.' ){ |
| 21299 | rc = do_meta_command(azCmd[i], &data); |
| 21300 | if( rc ){ |
| 21301 | free(azCmd); |
| 21302 | return rc==2 ? 0 : rc; |
| 21303 | } |
| 21304 | }else{ |
| 21305 | open_db(&data, 0); |
| 21306 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 21307 | if( zErrMsg || rc ){ |
| 21308 | if( zErrMsg!=0 ){ |
| 21309 | utf8_printf(stderr,"Error: %s\n", zErrMsg); |
| 21310 | }else{ |
| 21311 | utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); |
| 21312 | } |
| 21313 | sqlite3_free(zErrMsg); |
| 21314 | free(azCmd); |
| 21315 | return rc!=0 ? rc : 1; |
| 21316 | } |
| 21317 | } |
| 21318 | } |
| 21319 | }else{ |
| 21320 | /* Run commands received from standard input |
| 21321 | */ |
| 21322 | if( stdin_is_interactive ){ |
| 21323 | char *zHome; |
| @@ -21337,10 +21359,11 @@ | |
| 21359 | }else{ |
| 21360 | data.in = stdin; |
| 21361 | rc = process_input(&data); |
| 21362 | } |
| 21363 | } |
| 21364 | free(azCmd); |
| 21365 | set_table_name(&data, 0); |
| 21366 | if( data.db ){ |
| 21367 | session_close_all(&data); |
| 21368 | close_db(data.db); |
| 21369 | } |
| 21370 |
+93
-13
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -100,14 +100,11 @@ | ||
| 100 | 100 | } |
| 101 | 101 | if( g.perm.Read ){ |
| 102 | 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | 103 | @ <ul> |
| 104 | 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | - @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li> | |
| 106 | - @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li> | |
| 107 | - @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 | |
| 108 | - @ check-ins</a></li> | |
| 105 | + @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li> | |
| 109 | 106 | @ </ul> |
| 110 | 107 | @ </li> |
| 111 | 108 | } |
| 112 | 109 | if( g.perm.Read ){ |
| 113 | 110 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | ||
| 192 | 189 | if( g.perm.Admin ){ |
| 193 | 190 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 194 | 191 | @ this repository</a></li> |
| 195 | 192 | } |
| 196 | 193 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 197 | - @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li> | |
| 198 | 194 | @ </ul> |
| 199 | 195 | @ </li> |
| 200 | 196 | } |
| 201 | 197 | @ <li>%z(href("%R/help"))Help</a> |
| 202 | 198 | @ <ul> |
| @@ -219,22 +215,106 @@ | ||
| 219 | 215 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 220 | 216 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 221 | 217 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 222 | 218 | @ </ul></li> |
| 223 | 219 | } |
| 224 | - @ <li>Test Pages | |
| 225 | - @ <ul> | |
| 220 | + @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> | |
| 221 | + if( isPopup ){ | |
| 222 | + @ <li>%z(href("%R/sitemap"))Site Map</a></li> | |
| 223 | + } | |
| 224 | + @ </ul> | |
| 225 | + if( !isPopup ){ | |
| 226 | + style_finish_page("sitemap"); | |
| 227 | + } | |
| 228 | +} | |
| 229 | + | |
| 230 | +/* | |
| 231 | +** WEBPAGE: sitemap-test | |
| 232 | +** | |
| 233 | +** List some of the web pages offered by the Fossil web engine for testing | |
| 234 | +** purposes. This is similar to /sitemap, but is focused only on showing | |
| 235 | +** pages associated with testing. | |
| 236 | +*/ | |
| 237 | +void sitemap_test_page(void){ | |
| 238 | + int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ | |
| 239 | + | |
| 240 | + login_check_credentials(); | |
| 241 | + if( P("popup")!=0 && cgi_csrf_safe(0) ){ | |
| 242 | + /* If this is a POST from the same origin with the popup=1 parameter, | |
| 243 | + ** then disable anti-robot defenses */ | |
| 244 | + isPopup = 1; | |
| 245 | + g.perm.Hyperlink = 1; | |
| 246 | + g.javascriptHyperlink = 0; | |
| 247 | + } | |
| 248 | + if( !isPopup ){ | |
| 249 | + style_header("Test Page Map"); | |
| 250 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 251 | + } | |
| 252 | + @ <ul id="sitemap" class="columns" style="column-width:20em"> | |
| 226 | 253 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 227 | - @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> | |
| 254 | + @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> | |
| 228 | 255 | } |
| 229 | 256 | if( g.perm.Read ){ |
| 230 | - @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> | |
| 257 | + @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> | |
| 258 | + } | |
| 259 | + @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> | |
| 260 | + @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> | |
| 261 | + @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic | |
| 262 | + @ colors assigned to branch names</a> | |
| 263 | + if( g.perm.Admin ){ | |
| 264 | + @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> | |
| 265 | + @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> | |
| 266 | + @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> | |
| 267 | + @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> | |
| 268 | + @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a> | |
| 269 | + @ <li>%z(href("%R/repo_schema"))Repository schema</a></li> | |
| 270 | + } | |
| 271 | + if( g.perm.Read && g.perm.Hyperlink ){ | |
| 272 | + @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li> | |
| 273 | + } | |
| 274 | + @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li> | |
| 275 | + @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> | |
| 276 | + @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li> | |
| 277 | + if( !isPopup ){ | |
| 278 | + style_finish_page("sitemap"); | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/* | |
| 283 | +** WEBPAGE: sitemap-timeline | |
| 284 | +** | |
| 285 | +** Generate a list of hyperlinks to various (obscure) variations on | |
| 286 | +** the /timeline page. | |
| 287 | +*/ | |
| 288 | +void sitemap_timeline_page(void){ | |
| 289 | + int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ | |
| 290 | + | |
| 291 | + login_check_credentials(); | |
| 292 | + if( P("popup")!=0 && cgi_csrf_safe(0) ){ | |
| 293 | + /* If this is a POST from the same origin with the popup=1 parameter, | |
| 294 | + ** then disable anti-robot defenses */ | |
| 295 | + isPopup = 1; | |
| 296 | + g.perm.Hyperlink = 1; | |
| 297 | + g.javascriptHyperlink = 0; | |
| 298 | + } | |
| 299 | + if( !isPopup ){ | |
| 300 | + style_header("Timeline Examples"); | |
| 301 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 231 | 302 | } |
| 232 | - @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic | |
| 233 | - @ colors assigned to branch names</a> | |
| 234 | - @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> | |
| 235 | - @ </ul></li> | |
| 303 | + @ <ul id="sitemap" class="columns" style="column-width:20em"> | |
| 304 | + @ <li>%z(href("%R/timeline?ymd"))Current day</a></li> | |
| 305 | + @ <li>%z(href("%R/timeline?yw"))Current week</a></li> | |
| 306 | + @ <li>%z(href("%R/timeline?ym"))Current month</a></li> | |
| 307 | + @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li> | |
| 308 | + @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 | |
| 309 | + @ check-ins</a></li> | |
| 310 | + @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li> | |
| 311 | + @ <li>%z(href("%R/timeline?forks"))Forks</a></li> | |
| 312 | + @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li> | |
| 313 | + @ <li>%z(href("%R/timewarps"))Timewarps</a></li> | |
| 314 | + @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li> | |
| 315 | + @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li> | |
| 236 | 316 | @ </ul> |
| 237 | 317 | if( !isPopup ){ |
| 238 | 318 | style_finish_page("sitemap"); |
| 239 | 319 | } |
| 240 | 320 | } |
| 241 | 321 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -100,14 +100,11 @@ | |
| 100 | } |
| 101 | if( g.perm.Read ){ |
| 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | @ <ul> |
| 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | @ <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li> |
| 106 | @ <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li> |
| 107 | @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 |
| 108 | @ check-ins</a></li> |
| 109 | @ </ul> |
| 110 | @ </li> |
| 111 | } |
| 112 | if( g.perm.Read ){ |
| 113 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | |
| 192 | if( g.perm.Admin ){ |
| 193 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 194 | @ this repository</a></li> |
| 195 | } |
| 196 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 197 | @ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li> |
| 198 | @ </ul> |
| 199 | @ </li> |
| 200 | } |
| 201 | @ <li>%z(href("%R/help"))Help</a> |
| 202 | @ <ul> |
| @@ -219,22 +215,106 @@ | |
| 219 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 220 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 221 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 222 | @ </ul></li> |
| 223 | } |
| 224 | @ <li>Test Pages |
| 225 | @ <ul> |
| 226 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 227 | @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> |
| 228 | } |
| 229 | if( g.perm.Read ){ |
| 230 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 231 | } |
| 232 | @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic |
| 233 | @ colors assigned to branch names</a> |
| 234 | @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> |
| 235 | @ </ul></li> |
| 236 | @ </ul> |
| 237 | if( !isPopup ){ |
| 238 | style_finish_page("sitemap"); |
| 239 | } |
| 240 | } |
| 241 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -100,14 +100,11 @@ | |
| 100 | } |
| 101 | if( g.perm.Read ){ |
| 102 | @ <li>%z(href("%R/timeline"))Project Timeline</a> |
| 103 | @ <ul> |
| 104 | @ <li>%z(href("%R/reports"))Activity Reports</a></li> |
| 105 | @ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li> |
| 106 | @ </ul> |
| 107 | @ </li> |
| 108 | } |
| 109 | if( g.perm.Read ){ |
| 110 | @ <li>%z(href("%R/brlist"))Branches</a> |
| @@ -192,11 +189,10 @@ | |
| 189 | if( g.perm.Admin ){ |
| 190 | @ <li>%z(href("%R/urllist"))List of URLs used to access |
| 191 | @ this repository</a></li> |
| 192 | } |
| 193 | @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> |
| 194 | @ </ul> |
| 195 | @ </li> |
| 196 | } |
| 197 | @ <li>%z(href("%R/help"))Help</a> |
| 198 | @ <ul> |
| @@ -219,22 +215,106 @@ | |
| 215 | @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> |
| 216 | @ <li>%z(href("%R/admin_log"))Admin log</a></li> |
| 217 | @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> |
| 218 | @ </ul></li> |
| 219 | } |
| 220 | @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> |
| 221 | if( isPopup ){ |
| 222 | @ <li>%z(href("%R/sitemap"))Site Map</a></li> |
| 223 | } |
| 224 | @ </ul> |
| 225 | if( !isPopup ){ |
| 226 | style_finish_page("sitemap"); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** WEBPAGE: sitemap-test |
| 232 | ** |
| 233 | ** List some of the web pages offered by the Fossil web engine for testing |
| 234 | ** purposes. This is similar to /sitemap, but is focused only on showing |
| 235 | ** pages associated with testing. |
| 236 | */ |
| 237 | void sitemap_test_page(void){ |
| 238 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 239 | |
| 240 | login_check_credentials(); |
| 241 | if( P("popup")!=0 && cgi_csrf_safe(0) ){ |
| 242 | /* If this is a POST from the same origin with the popup=1 parameter, |
| 243 | ** then disable anti-robot defenses */ |
| 244 | isPopup = 1; |
| 245 | g.perm.Hyperlink = 1; |
| 246 | g.javascriptHyperlink = 0; |
| 247 | } |
| 248 | if( !isPopup ){ |
| 249 | style_header("Test Page Map"); |
| 250 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 251 | } |
| 252 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 253 | if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){ |
| 254 | @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> |
| 255 | } |
| 256 | if( g.perm.Read ){ |
| 257 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 258 | } |
| 259 | @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> |
| 260 | @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> |
| 261 | @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic |
| 262 | @ colors assigned to branch names</a> |
| 263 | if( g.perm.Admin ){ |
| 264 | @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> |
| 265 | @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> |
| 266 | @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> |
| 267 | @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> |
| 268 | @ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a> |
| 269 | @ <li>%z(href("%R/repo_schema"))Repository schema</a></li> |
| 270 | } |
| 271 | if( g.perm.Read && g.perm.Hyperlink ){ |
| 272 | @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li> |
| 273 | } |
| 274 | @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li> |
| 275 | @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li> |
| 276 | @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li> |
| 277 | if( !isPopup ){ |
| 278 | style_finish_page("sitemap"); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** WEBPAGE: sitemap-timeline |
| 284 | ** |
| 285 | ** Generate a list of hyperlinks to various (obscure) variations on |
| 286 | ** the /timeline page. |
| 287 | */ |
| 288 | void sitemap_timeline_page(void){ |
| 289 | int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */ |
| 290 | |
| 291 | login_check_credentials(); |
| 292 | if( P("popup")!=0 && cgi_csrf_safe(0) ){ |
| 293 | /* If this is a POST from the same origin with the popup=1 parameter, |
| 294 | ** then disable anti-robot defenses */ |
| 295 | isPopup = 1; |
| 296 | g.perm.Hyperlink = 1; |
| 297 | g.javascriptHyperlink = 0; |
| 298 | } |
| 299 | if( !isPopup ){ |
| 300 | style_header("Timeline Examples"); |
| 301 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 302 | } |
| 303 | @ <ul id="sitemap" class="columns" style="column-width:20em"> |
| 304 | @ <li>%z(href("%R/timeline?ymd"))Current day</a></li> |
| 305 | @ <li>%z(href("%R/timeline?yw"))Current week</a></li> |
| 306 | @ <li>%z(href("%R/timeline?ym"))Current month</a></li> |
| 307 | @ <li>%z(href("%R/thisdayinhistory"))Today in history</a></li> |
| 308 | @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 |
| 309 | @ check-ins</a></li> |
| 310 | @ <li>%z(href("%R/timeline?namechng"))File name changes</a></li> |
| 311 | @ <li>%z(href("%R/timeline?forks"))Forks</a></li> |
| 312 | @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li> |
| 313 | @ <li>%z(href("%R/timewarps"))Timewarps</a></li> |
| 314 | @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li> |
| 315 | @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li> |
| 316 | @ </ul> |
| 317 | if( !isPopup ){ |
| 318 | style_finish_page("sitemap"); |
| 319 | } |
| 320 | } |
| 321 |
+2
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -85,11 +85,13 @@ | ||
| 85 | 85 | */ |
| 86 | 86 | static struct SkinDetail { |
| 87 | 87 | const char *zName; /* Name of the detail */ |
| 88 | 88 | const char *zValue; /* Value of the detail */ |
| 89 | 89 | } aSkinDetail[] = { |
| 90 | + { "pikchr-fontscale", "" }, | |
| 90 | 91 | { "pikchr-foreground", "" }, |
| 92 | + { "pikchr-scale", "" }, | |
| 91 | 93 | { "timeline-arrowheads", "1" }, |
| 92 | 94 | { "timeline-circle-nodes", "0" }, |
| 93 | 95 | { "timeline-color-graph-lines", "0" }, |
| 94 | 96 | { "white-foreground", "0" }, |
| 95 | 97 | }; |
| 96 | 98 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -85,11 +85,13 @@ | |
| 85 | */ |
| 86 | static struct SkinDetail { |
| 87 | const char *zName; /* Name of the detail */ |
| 88 | const char *zValue; /* Value of the detail */ |
| 89 | } aSkinDetail[] = { |
| 90 | { "pikchr-foreground", "" }, |
| 91 | { "timeline-arrowheads", "1" }, |
| 92 | { "timeline-circle-nodes", "0" }, |
| 93 | { "timeline-color-graph-lines", "0" }, |
| 94 | { "white-foreground", "0" }, |
| 95 | }; |
| 96 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -85,11 +85,13 @@ | |
| 85 | */ |
| 86 | static struct SkinDetail { |
| 87 | const char *zName; /* Name of the detail */ |
| 88 | const char *zValue; /* Value of the detail */ |
| 89 | } aSkinDetail[] = { |
| 90 | { "pikchr-fontscale", "" }, |
| 91 | { "pikchr-foreground", "" }, |
| 92 | { "pikchr-scale", "" }, |
| 93 | { "timeline-arrowheads", "1" }, |
| 94 | { "timeline-circle-nodes", "0" }, |
| 95 | { "timeline-color-graph-lines", "0" }, |
| 96 | { "white-foreground", "0" }, |
| 97 | }; |
| 98 |
+37
-13
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -32,10 +32,15 @@ | ||
| 32 | 32 | |
| 33 | 33 | #ifndef _WIN32 |
| 34 | 34 | # include "linenoise.h" |
| 35 | 35 | #endif |
| 36 | 36 | |
| 37 | +/* | |
| 38 | +** True if the "fossil sql" command has the --test flag. False otherwise. | |
| 39 | +*/ | |
| 40 | +static int local_bSqlCmdTest = 0; | |
| 41 | + | |
| 37 | 42 | /* |
| 38 | 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | 44 | ** content of artifact identified by X as a blob. |
| 40 | 45 | */ |
| 41 | 46 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | ||
| 163 | 168 | ** Undocumented test SQL functions: |
| 164 | 169 | ** |
| 165 | 170 | ** db_protect(X) |
| 166 | 171 | ** db_protect_pop(X) |
| 167 | 172 | ** |
| 168 | -** These invoke the corresponding C routines. Misuse may result in | |
| 169 | -** an assertion fault. | |
| 173 | +** These invoke the corresponding C routines. | |
| 174 | +** | |
| 175 | +** WARNING: | |
| 176 | +** Do not instantiate these functions for any Fossil webpage or command | |
| 177 | +** method of than the "fossil sql" command. If an attacker gains access | |
| 178 | +** to these functions, he will be able to disable other defense mechanisms. | |
| 179 | +** | |
| 180 | +** This routines are for interactiving testing only. They are experimental | |
| 181 | +** and undocumented (apart from this comments) and might go away or change | |
| 182 | +** in future releases. | |
| 183 | +** | |
| 184 | +** 2020-11-29: This functions are now only available if the "fossil sql" | |
| 185 | +** command is started with the --test option. | |
| 170 | 186 | */ |
| 171 | 187 | static void sqlcmd_db_protect( |
| 172 | 188 | sqlite3_context *context, |
| 173 | 189 | int argc, |
| 174 | 190 | sqlite3_value **argv |
| 175 | 191 | ){ |
| 176 | 192 | unsigned mask = 0; |
| 177 | 193 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | - if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 179 | - if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 180 | - if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 181 | - if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 182 | - if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 183 | - db_protect(mask); | |
| 194 | + if( z!=0 && local_bSqlCmdTest ){ | |
| 195 | + if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 196 | + if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 197 | + if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 198 | + if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 199 | + if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 200 | + db_protect(mask); | |
| 201 | + } | |
| 184 | 202 | } |
| 185 | 203 | static void sqlcmd_db_protect_pop( |
| 186 | 204 | sqlite3_context *context, |
| 187 | 205 | int argc, |
| 188 | 206 | sqlite3_value **argv |
| 189 | 207 | ){ |
| 190 | - db_protect_pop(); | |
| 208 | + if( !local_bSqlCmdTest ) db_protect_pop(); | |
| 191 | 209 | } |
| 192 | 210 | |
| 193 | 211 | |
| 194 | 212 | |
| 195 | 213 | |
| @@ -232,14 +250,16 @@ | ||
| 232 | 250 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | 251 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | 252 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | 253 | db_protect_only(PROTECT_NONE); |
| 236 | 254 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | - sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 238 | - sqlcmd_db_protect, 0, 0); | |
| 239 | - sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 240 | - sqlcmd_db_protect_pop, 0, 0); | |
| 255 | + if( local_bSqlCmdTest ){ | |
| 256 | + sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 257 | + sqlcmd_db_protect, 0, 0); | |
| 258 | + sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 259 | + sqlcmd_db_protect_pop, 0, 0); | |
| 260 | + } | |
| 241 | 261 | return SQLITE_OK; |
| 242 | 262 | } |
| 243 | 263 | |
| 244 | 264 | /* |
| 245 | 265 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | ||
| 329 | 349 | ** --readonly Open the repository read-only. No changes |
| 330 | 350 | ** are allowed. This is a recommended safety |
| 331 | 351 | ** precaution to prevent repository damage. |
| 332 | 352 | ** |
| 333 | 353 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 354 | +** | |
| 355 | +** --test Enable some testing and analysis features | |
| 356 | +** that are normally disabled. | |
| 334 | 357 | ** |
| 335 | 358 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | 359 | ** work. |
| 337 | 360 | ** |
| 338 | 361 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | ||
| 397 | 420 | extern int sqlite3_shell(int, char**); |
| 398 | 421 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | 422 | g.fNoThHook = 1; |
| 400 | 423 | #endif |
| 401 | 424 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 425 | + local_bSqlCmdTest = find_option("test",0,0)!=0; | |
| 402 | 426 | if( !noRepository ){ |
| 403 | 427 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | 428 | } |
| 405 | 429 | db_open_config(1,0); |
| 406 | 430 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 | 431 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 39 | ** content of artifact identified by X as a blob. |
| 40 | */ |
| 41 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | |
| 163 | ** Undocumented test SQL functions: |
| 164 | ** |
| 165 | ** db_protect(X) |
| 166 | ** db_protect_pop(X) |
| 167 | ** |
| 168 | ** These invoke the corresponding C routines. Misuse may result in |
| 169 | ** an assertion fault. |
| 170 | */ |
| 171 | static void sqlcmd_db_protect( |
| 172 | sqlite3_context *context, |
| 173 | int argc, |
| 174 | sqlite3_value **argv |
| 175 | ){ |
| 176 | unsigned mask = 0; |
| 177 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 178 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 179 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 180 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 181 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 182 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 183 | db_protect(mask); |
| 184 | } |
| 185 | static void sqlcmd_db_protect_pop( |
| 186 | sqlite3_context *context, |
| 187 | int argc, |
| 188 | sqlite3_value **argv |
| 189 | ){ |
| 190 | db_protect_pop(); |
| 191 | } |
| 192 | |
| 193 | |
| 194 | |
| 195 | |
| @@ -232,14 +250,16 @@ | |
| 232 | ** will get cleaned up when the shell closes the database connection */ |
| 233 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 234 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 235 | db_protect_only(PROTECT_NONE); |
| 236 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 237 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 238 | sqlcmd_db_protect, 0, 0); |
| 239 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 240 | sqlcmd_db_protect_pop, 0, 0); |
| 241 | return SQLITE_OK; |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | |
| 329 | ** --readonly Open the repository read-only. No changes |
| 330 | ** are allowed. This is a recommended safety |
| 331 | ** precaution to prevent repository damage. |
| 332 | ** |
| 333 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 334 | ** |
| 335 | ** All of the standard sqlite3 command-line shell options should also |
| 336 | ** work. |
| 337 | ** |
| 338 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | |
| 397 | extern int sqlite3_shell(int, char**); |
| 398 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 399 | g.fNoThHook = 1; |
| 400 | #endif |
| 401 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 402 | if( !noRepository ){ |
| 403 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 404 | } |
| 405 | db_open_config(1,0); |
| 406 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 407 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -32,10 +32,15 @@ | |
| 32 | |
| 33 | #ifndef _WIN32 |
| 34 | # include "linenoise.h" |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** True if the "fossil sql" command has the --test flag. False otherwise. |
| 39 | */ |
| 40 | static int local_bSqlCmdTest = 0; |
| 41 | |
| 42 | /* |
| 43 | ** Implementation of the "content(X)" SQL function. Return the complete |
| 44 | ** content of artifact identified by X as a blob. |
| 45 | */ |
| 46 | static void sqlcmd_content( |
| @@ -163,33 +168,46 @@ | |
| 168 | ** Undocumented test SQL functions: |
| 169 | ** |
| 170 | ** db_protect(X) |
| 171 | ** db_protect_pop(X) |
| 172 | ** |
| 173 | ** These invoke the corresponding C routines. |
| 174 | ** |
| 175 | ** WARNING: |
| 176 | ** Do not instantiate these functions for any Fossil webpage or command |
| 177 | ** method of than the "fossil sql" command. If an attacker gains access |
| 178 | ** to these functions, he will be able to disable other defense mechanisms. |
| 179 | ** |
| 180 | ** This routines are for interactiving testing only. They are experimental |
| 181 | ** and undocumented (apart from this comments) and might go away or change |
| 182 | ** in future releases. |
| 183 | ** |
| 184 | ** 2020-11-29: This functions are now only available if the "fossil sql" |
| 185 | ** command is started with the --test option. |
| 186 | */ |
| 187 | static void sqlcmd_db_protect( |
| 188 | sqlite3_context *context, |
| 189 | int argc, |
| 190 | sqlite3_value **argv |
| 191 | ){ |
| 192 | unsigned mask = 0; |
| 193 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 194 | if( z!=0 && local_bSqlCmdTest ){ |
| 195 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 196 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 197 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 198 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 199 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 200 | db_protect(mask); |
| 201 | } |
| 202 | } |
| 203 | static void sqlcmd_db_protect_pop( |
| 204 | sqlite3_context *context, |
| 205 | int argc, |
| 206 | sqlite3_value **argv |
| 207 | ){ |
| 208 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 209 | } |
| 210 | |
| 211 | |
| 212 | |
| 213 | |
| @@ -232,14 +250,16 @@ | |
| 250 | ** will get cleaned up when the shell closes the database connection */ |
| 251 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 252 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 253 | db_protect_only(PROTECT_NONE); |
| 254 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 255 | if( local_bSqlCmdTest ){ |
| 256 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 257 | sqlcmd_db_protect, 0, 0); |
| 258 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 259 | sqlcmd_db_protect_pop, 0, 0); |
| 260 | } |
| 261 | return SQLITE_OK; |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | ** atexit() handler that cleans up global state modified by this module. |
| @@ -329,10 +349,13 @@ | |
| 349 | ** --readonly Open the repository read-only. No changes |
| 350 | ** are allowed. This is a recommended safety |
| 351 | ** precaution to prevent repository damage. |
| 352 | ** |
| 353 | ** -R REPOSITORY Use REPOSITORY as the repository database |
| 354 | ** |
| 355 | ** --test Enable some testing and analysis features |
| 356 | ** that are normally disabled. |
| 357 | ** |
| 358 | ** All of the standard sqlite3 command-line shell options should also |
| 359 | ** work. |
| 360 | ** |
| 361 | ** The following SQL extensions are provided with this Fossil-enhanced |
| @@ -397,10 +420,11 @@ | |
| 420 | extern int sqlite3_shell(int, char**); |
| 421 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 422 | g.fNoThHook = 1; |
| 423 | #endif |
| 424 | noRepository = find_option("no-repository", 0, 0)!=0; |
| 425 | local_bSqlCmdTest = find_option("test",0,0)!=0; |
| 426 | if( !noRepository ){ |
| 427 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 428 | } |
| 429 | db_open_config(1,0); |
| 430 | zConfigDb = fossil_strdup(g.zConfigDbName); |
| 431 |
+215
-74
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1171,11 +1171,11 @@ | ||
| 1171 | 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | 1173 | */ |
| 1174 | 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" | |
| 1176 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" | |
| 1177 | 1177 | |
| 1178 | 1178 | /* |
| 1179 | 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | 1181 | ** |
| @@ -1550,10 +1550,11 @@ | ||
| 1550 | 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) | |
| 1555 | 1556 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1556 | 1557 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1557 | 1558 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1558 | 1559 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1559 | 1560 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | ||
| 7238 | 7239 | ** CAPI3REF: Determine the transaction state of a database |
| 7239 | 7240 | ** METHOD: sqlite3 |
| 7240 | 7241 | ** |
| 7241 | 7242 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7242 | 7243 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7243 | -** then the highest transaction state of any schema on databse connection D | |
| 7244 | +** then the highest transaction state of any schema on database connection D | |
| 7244 | 7245 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7245 | 7246 | ** <ol> |
| 7246 | 7247 | ** <li value="0"> SQLITE_TXN_NONE |
| 7247 | 7248 | ** <li value="1"> SQLITE_TXN_READ |
| 7248 | 7249 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | ||
| 8785 | 8786 | */ |
| 8786 | 8787 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8787 | 8788 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8788 | 8789 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8789 | 8790 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8790 | -#define SQLITE_TESTCTRL_SEEK_COUNT 7 | |
| 8791 | 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | ||
| 8810 | 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | -#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ | |
| 8815 | +#define SQLITE_TESTCTRL_SEEK_COUNT 30 | |
| 8816 | +#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ | |
| 8816 | 8817 | |
| 8817 | 8818 | /* |
| 8818 | 8819 | ** CAPI3REF: SQL Keyword Checking |
| 8819 | 8820 | ** |
| 8820 | 8821 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | ||
| 28182 | 28183 | EnableLookaside; |
| 28183 | 28184 | } |
| 28184 | 28185 | } |
| 28185 | 28186 | |
| 28186 | 28187 | /* |
| 28187 | -** Take actions at the end of an API call to indicate an OOM error | |
| 28188 | +** Take actions at the end of an API call to deal with error codes. | |
| 28188 | 28189 | */ |
| 28189 | -static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ | |
| 28190 | - sqlite3OomClear(db); | |
| 28191 | - sqlite3Error(db, SQLITE_NOMEM); | |
| 28192 | - return SQLITE_NOMEM_BKPT; | |
| 28190 | +static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ | |
| 28191 | + if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ | |
| 28192 | + sqlite3OomClear(db); | |
| 28193 | + sqlite3Error(db, SQLITE_NOMEM); | |
| 28194 | + return SQLITE_NOMEM_BKPT; | |
| 28195 | + } | |
| 28196 | + return rc & db->errMask; | |
| 28193 | 28197 | } |
| 28194 | 28198 | |
| 28195 | 28199 | /* |
| 28196 | 28200 | ** This function must be called before exiting any API function (i.e. |
| 28197 | 28201 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | ||
| 28209 | 28213 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28210 | 28214 | ** is unsafe, as is the call to sqlite3Error(). |
| 28211 | 28215 | */ |
| 28212 | 28216 | assert( db!=0 ); |
| 28213 | 28217 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28214 | - if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ | |
| 28215 | - return apiOomError(db); | |
| 28218 | + if( db->mallocFailed || rc ){ | |
| 28219 | + return apiHandleError(db, rc); | |
| 28216 | 28220 | } |
| 28217 | 28221 | return rc & db->errMask; |
| 28218 | 28222 | } |
| 28219 | 28223 | |
| 28220 | 28224 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | ||
| 37003 | 37007 | |
| 37004 | 37008 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37005 | 37009 | if( got==amt ){ |
| 37006 | 37010 | return SQLITE_OK; |
| 37007 | 37011 | }else if( got<0 ){ |
| 37008 | - /* lastErrno set by seekAndRead */ | |
| 37012 | + /* pFile->lastErrno has been set by seekAndRead(). | |
| 37013 | + ** Usually we return SQLITE_IOERR_READ here, though for some | |
| 37014 | + ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The | |
| 37015 | + ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT | |
| 37016 | + ** prior to returning to the application by the sqlite3ApiExit() | |
| 37017 | + ** routine. | |
| 37018 | + */ | |
| 37019 | + switch( pFile->lastErrno ){ | |
| 37020 | + case ERANGE: | |
| 37021 | + case EIO: | |
| 37022 | +#ifdef ENXIO | |
| 37023 | + case ENXIO: | |
| 37024 | +#endif | |
| 37025 | +#ifdef EDEVERR | |
| 37026 | + case EDEVERR: | |
| 37027 | +#endif | |
| 37028 | + return SQLITE_IOERR_CORRUPTFS; | |
| 37029 | + } | |
| 37009 | 37030 | return SQLITE_IOERR_READ; |
| 37010 | 37031 | }else{ |
| 37011 | 37032 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37012 | 37033 | /* Unread parts of the buffer must be zero-filled */ |
| 37013 | 37034 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | ||
| 38535 | 38556 | if( bUnlock ){ |
| 38536 | 38557 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38537 | 38558 | if( rc==SQLITE_OK ){ |
| 38538 | 38559 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38539 | 38560 | } |
| 38540 | - }else if( p->sharedMask & (1<<ofst) ){ | |
| 38561 | + }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){ | |
| 38541 | 38562 | assert( n==1 && aLock[ofst]>1 ); |
| 38542 | 38563 | aLock[ofst]--; |
| 38543 | 38564 | } |
| 38544 | 38565 | |
| 38545 | 38566 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | ||
| 38568 | 38589 | /* Make sure no sibling connections hold locks that will block this |
| 38569 | 38590 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38570 | 38591 | int ii; |
| 38571 | 38592 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38572 | 38593 | assert( (p->sharedMask & mask)==0 ); |
| 38573 | - if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){ | |
| 38594 | + if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){ | |
| 38574 | 38595 | rc = SQLITE_BUSY; |
| 38575 | 38596 | break; |
| 38576 | 38597 | } |
| 38577 | 38598 | } |
| 38578 | 38599 | |
| @@ -39964,19 +39985,39 @@ | ||
| 39964 | 39985 | } |
| 39965 | 39986 | return SQLITE_OK; |
| 39966 | 39987 | } |
| 39967 | 39988 | |
| 39968 | 39989 | /* |
| 39990 | +** If the last component of the pathname in z[0]..z[j-1] is something | |
| 39991 | +** other than ".." then back it out and return true. If the last | |
| 39992 | +** component is empty or if it is ".." then return false. | |
| 39993 | +*/ | |
| 39994 | +static int unixBackupDir(const char *z, int *pJ){ | |
| 39995 | + int j = *pJ; | |
| 39996 | + int i; | |
| 39997 | + if( j<=0 ) return 0; | |
| 39998 | + for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} | |
| 39999 | + if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; | |
| 40000 | + *pJ = i-1; | |
| 40001 | + return 1; | |
| 40002 | +} | |
| 40003 | + | |
| 40004 | +/* | |
| 40005 | +** Convert a relative pathname into a full pathname. Also | |
| 40006 | +** simplify the pathname as follows: | |
| 39969 | 40007 | ** |
| 40008 | +** Remove all instances of /./ | |
| 40009 | +** Remove all isntances of /X/../ for any X | |
| 39970 | 40010 | */ |
| 39971 | 40011 | static int mkFullPathname( |
| 39972 | 40012 | const char *zPath, /* Input path */ |
| 39973 | 40013 | char *zOut, /* Output buffer */ |
| 39974 | 40014 | int nOut /* Allocated size of buffer zOut */ |
| 39975 | 40015 | ){ |
| 39976 | 40016 | int nPath = sqlite3Strlen30(zPath); |
| 39977 | 40017 | int iOff = 0; |
| 40018 | + int i, j; | |
| 39978 | 40019 | if( zPath[0]!='/' ){ |
| 39979 | 40020 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 39980 | 40021 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 39981 | 40022 | } |
| 39982 | 40023 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | ||
| 39987 | 40028 | ** even if it returns an error. */ |
| 39988 | 40029 | zOut[iOff] = '\0'; |
| 39989 | 40030 | return SQLITE_CANTOPEN_BKPT; |
| 39990 | 40031 | } |
| 39991 | 40032 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 40033 | + | |
| 40034 | + /* Remove duplicate '/' characters. Except, two // at the beginning | |
| 40035 | + ** of a pathname is allowed since this is important on windows. */ | |
| 40036 | + for(i=j=1; zOut[i]; i++){ | |
| 40037 | + zOut[j++] = zOut[i]; | |
| 40038 | + while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; | |
| 40039 | + } | |
| 40040 | + zOut[j] = 0; | |
| 40041 | + | |
| 40042 | + assert( zOut[0]=='/' ); | |
| 40043 | + for(i=j=0; zOut[i]; i++){ | |
| 40044 | + if( zOut[i]=='/' ){ | |
| 40045 | + /* Skip over internal "/." directory components */ | |
| 40046 | + if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ | |
| 40047 | + i += 1; | |
| 40048 | + continue; | |
| 40049 | + } | |
| 40050 | + | |
| 40051 | + /* If this is a "/.." directory component then back out the | |
| 40052 | + ** previous term of the directory if it is something other than "..". | |
| 40053 | + */ | |
| 40054 | + if( zOut[i+1]=='.' | |
| 40055 | + && zOut[i+2]=='.' | |
| 40056 | + && zOut[i+3]=='/' | |
| 40057 | + && unixBackupDir(zOut, &j) | |
| 40058 | + ){ | |
| 40059 | + i += 2; | |
| 40060 | + continue; | |
| 40061 | + } | |
| 40062 | + } | |
| 40063 | + if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; | |
| 40064 | + j++; | |
| 40065 | + } | |
| 40066 | + if( NEVER(j==0) ) zOut[j++] = '/'; | |
| 40067 | + zOut[j] = 0; | |
| 39992 | 40068 | return SQLITE_OK; |
| 39993 | 40069 | } |
| 39994 | 40070 | |
| 39995 | 40071 | /* |
| 39996 | 40072 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | ||
| 54326 | 54402 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54327 | 54403 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54328 | 54404 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54329 | 54405 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54330 | 54406 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54407 | + char *zFree = 0; /* Free this buffer */ | |
| 54331 | 54408 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54332 | 54409 | |
| 54333 | 54410 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54334 | 54411 | ** If successful, open the super-journal file for reading. |
| 54335 | 54412 | */ |
| @@ -54350,15 +54427,17 @@ | ||
| 54350 | 54427 | ** files extracted from regular rollback-journals. |
| 54351 | 54428 | */ |
| 54352 | 54429 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54353 | 54430 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54354 | 54431 | nSuperPtr = pVfs->mxPathname+1; |
| 54355 | - zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); | |
| 54356 | - if( !zSuperJournal ){ | |
| 54432 | + zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); | |
| 54433 | + if( !zFree ){ | |
| 54357 | 54434 | rc = SQLITE_NOMEM_BKPT; |
| 54358 | 54435 | goto delsuper_out; |
| 54359 | 54436 | } |
| 54437 | + zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; | |
| 54438 | + zSuperJournal = &zFree[4]; | |
| 54360 | 54439 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54361 | 54440 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54362 | 54441 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54363 | 54442 | zSuperJournal[nSuperJournal] = 0; |
| 54364 | 54443 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | ||
| 54402 | 54481 | |
| 54403 | 54482 | sqlite3OsClose(pSuper); |
| 54404 | 54483 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54405 | 54484 | |
| 54406 | 54485 | delsuper_out: |
| 54407 | - sqlite3_free(zSuperJournal); | |
| 54486 | + sqlite3_free(zFree); | |
| 54408 | 54487 | if( pSuper ){ |
| 54409 | 54488 | sqlite3OsClose(pSuper); |
| 54410 | 54489 | assert( !isOpen(pJournal) ); |
| 54411 | 54490 | sqlite3_free(pSuper); |
| 54412 | 54491 | } |
| @@ -54740,11 +54819,15 @@ | ||
| 54740 | 54819 | ** in case this has happened, clear the changeCountDone flag now. |
| 54741 | 54820 | */ |
| 54742 | 54821 | pPager->changeCountDone = pPager->tempFile; |
| 54743 | 54822 | |
| 54744 | 54823 | if( rc==SQLITE_OK ){ |
| 54745 | - zSuper = pPager->pTmpSpace; | |
| 54824 | + /* Leave 4 bytes of space before the super-journal filename in memory. | |
| 54825 | + ** This is because it may end up being passed to sqlite3OsOpen(), in | |
| 54826 | + ** which case it requires 4 0x00 bytes in memory immediately before | |
| 54827 | + ** the filename. */ | |
| 54828 | + zSuper = &pPager->pTmpSpace[4]; | |
| 54746 | 54829 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54747 | 54830 | testcase( rc!=SQLITE_OK ); |
| 54748 | 54831 | } |
| 54749 | 54832 | if( rc==SQLITE_OK |
| 54750 | 54833 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | ||
| 54757 | 54840 | } |
| 54758 | 54841 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54759 | 54842 | /* If there was a super-journal and this routine will return success, |
| 54760 | 54843 | ** see if it is possible to delete the super-journal. |
| 54761 | 54844 | */ |
| 54845 | + assert( zSuper==&pPager->pTmpSpace[4] ); | |
| 54846 | + memset(&zSuper[-4], 0, 4); | |
| 54762 | 54847 | rc = pager_delsuper(pPager, zSuper); |
| 54763 | 54848 | testcase( rc!=SQLITE_OK ); |
| 54764 | 54849 | } |
| 54765 | 54850 | if( isHot && nPlayback ){ |
| 54766 | 54851 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | ||
| 64746 | 64831 | #define hasReadConflicts(a, b) 0 |
| 64747 | 64832 | #endif |
| 64748 | 64833 | |
| 64749 | 64834 | #ifdef SQLITE_DEBUG |
| 64750 | 64835 | /* |
| 64751 | -** Return an reset the seek counter for a Btree object. | |
| 64836 | +** Return and reset the seek counter for a Btree object. | |
| 64752 | 64837 | */ |
| 64753 | 64838 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64754 | 64839 | u64 n = pBt->nSeek; |
| 64755 | 64840 | pBt->nSeek = 0; |
| 64756 | 64841 | return n; |
| @@ -82192,13 +82277,16 @@ | ||
| 82192 | 82277 | ** equal to, or greater than the second (double). |
| 82193 | 82278 | */ |
| 82194 | 82279 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82195 | 82280 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82196 | 82281 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82282 | + testcase( x<r ); | |
| 82283 | + testcase( x>r ); | |
| 82284 | + testcase( x==r ); | |
| 82197 | 82285 | if( x<r ) return -1; |
| 82198 | - if( x>r ) return +1; | |
| 82199 | - return 0; | |
| 82286 | + if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ | |
| 82287 | + return 0; /*NO_TEST*/ /* work around bugs in gcov */ | |
| 82200 | 82288 | }else{ |
| 82201 | 82289 | i64 y; |
| 82202 | 82290 | double s; |
| 82203 | 82291 | if( r<-9223372036854775808.0 ) return +1; |
| 82204 | 82292 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | ||
| 89388 | 89476 | assert( rc==SQLITE_OK ); |
| 89389 | 89477 | break; |
| 89390 | 89478 | } |
| 89391 | 89479 | |
| 89392 | 89480 | |
| 89393 | -/* Opcode: OpenEphemeral P1 P2 * P4 P5 | |
| 89481 | +/* Opcode: OpenEphemeral P1 P2 P3 P4 P5 | |
| 89394 | 89482 | ** Synopsis: nColumn=P2 |
| 89395 | 89483 | ** |
| 89396 | 89484 | ** Open a new cursor P1 to a transient table. |
| 89397 | 89485 | ** The cursor is always opened read/write even if |
| 89398 | 89486 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | ||
| 89408 | 89496 | ** |
| 89409 | 89497 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89410 | 89498 | ** in btree.h. These flags control aspects of the operation of |
| 89411 | 89499 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89412 | 89500 | ** added automatically. |
| 89501 | +** | |
| 89502 | +** If P3 is positive, then reg[P3] is modified slightly so that it | |
| 89503 | +** can be used as zero-length data for OP_Insert. This is an optimization | |
| 89504 | +** that avoids an extra OP_Blob opcode to initialize that register. | |
| 89413 | 89505 | */ |
| 89414 | 89506 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89415 | 89507 | ** Synopsis: nColumn=P2 |
| 89416 | 89508 | ** |
| 89417 | 89509 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | ||
| 89430 | 89522 | SQLITE_OPEN_EXCLUSIVE | |
| 89431 | 89523 | SQLITE_OPEN_DELETEONCLOSE | |
| 89432 | 89524 | SQLITE_OPEN_TRANSIENT_DB; |
| 89433 | 89525 | assert( pOp->p1>=0 ); |
| 89434 | 89526 | assert( pOp->p2>=0 ); |
| 89527 | + if( pOp->p3>0 ){ | |
| 89528 | + /* Make register reg[P3] into a value that can be used as the data | |
| 89529 | + ** form sqlite3BtreeInsert() where the length of the data is zero. */ | |
| 89530 | + assert( pOp->p2==0 ); /* Only used when number of columns is zero */ | |
| 89531 | + assert( pOp->opcode==OP_OpenEphemeral ); | |
| 89532 | + assert( aMem[pOp->p3].flags & MEM_Null ); | |
| 89533 | + aMem[pOp->p3].n = 0; | |
| 89534 | + aMem[pOp->p3].z = ""; | |
| 89535 | + } | |
| 89435 | 89536 | pCx = p->apCsr[pOp->p1]; |
| 89436 | 89537 | if( pCx && pCx->pBtx ){ |
| 89437 | 89538 | /* If the ephermeral table is already open, erase all existing content |
| 89438 | 89539 | ** so that the table is empty again, rather than creating a new table. */ |
| 89439 | 89540 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | ||
| 90589 | 90690 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90590 | 90691 | #endif |
| 90591 | 90692 | |
| 90592 | 90693 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90593 | 90694 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90594 | - assert( pData->flags & (MEM_Blob|MEM_Str) ); | |
| 90695 | + assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); | |
| 90595 | 90696 | x.pData = pData->z; |
| 90596 | 90697 | x.nData = pData->n; |
| 90597 | 90698 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90598 | 90699 | if( pData->flags & MEM_Zero ){ |
| 90599 | 90700 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | ||
| 93644 | 93745 | |
| 93645 | 93746 | /* If we reach this point, it means that execution is finished with |
| 93646 | 93747 | ** an error of some kind. |
| 93647 | 93748 | */ |
| 93648 | 93749 | abort_due_to_error: |
| 93649 | - if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; | |
| 93750 | + if( db->mallocFailed ){ | |
| 93751 | + rc = SQLITE_NOMEM_BKPT; | |
| 93752 | + }else if( rc==SQLITE_IOERR_CORRUPTFS ){ | |
| 93753 | + rc = SQLITE_CORRUPT_BKPT; | |
| 93754 | + } | |
| 93650 | 93755 | assert( rc ); |
| 93651 | 93756 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93652 | 93757 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93653 | 93758 | } |
| 93654 | 93759 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | ||
| 99381 | 99486 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99382 | 99487 | int iCol = -1; |
| 99383 | 99488 | Expr *pE, *pDup; |
| 99384 | 99489 | if( pItem->done ) continue; |
| 99385 | 99490 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99491 | + if( NEVER(pE==0) ) continue; | |
| 99386 | 99492 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99387 | 99493 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99388 | 99494 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99389 | 99495 | return 1; |
| 99390 | 99496 | } |
| @@ -99560,10 +99666,11 @@ | ||
| 99560 | 99666 | nResult = pSelect->pEList->nExpr; |
| 99561 | 99667 | pParse = pNC->pParse; |
| 99562 | 99668 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99563 | 99669 | Expr *pE = pItem->pExpr; |
| 99564 | 99670 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99671 | + if( NEVER(pE2==0) ) continue; | |
| 99565 | 99672 | if( zType[0]!='G' ){ |
| 99566 | 99673 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99567 | 99674 | if( iCol>0 ){ |
| 99568 | 99675 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99569 | 99676 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | ||
| 103679 | 103786 | ** register iReg. The caller must ensure that iReg already contains |
| 103680 | 103787 | ** the correct value for the expression. |
| 103681 | 103788 | */ |
| 103682 | 103789 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103683 | 103790 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103791 | + if( NEVER(p==0) ) return; | |
| 103684 | 103792 | p->op2 = p->op; |
| 103685 | 103793 | p->op = TK_REGISTER; |
| 103686 | 103794 | p->iTable = iReg; |
| 103687 | 103795 | ExprClearProperty(p, EP_Skip); |
| 103688 | 103796 | } |
| @@ -104666,10 +104774,11 @@ | ||
| 104666 | 104774 | */ |
| 104667 | 104775 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104668 | 104776 | int r2; |
| 104669 | 104777 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104670 | 104778 | if( ConstFactorOk(pParse) |
| 104779 | + && ALWAYS(pExpr!=0) | |
| 104671 | 104780 | && pExpr->op!=TK_REGISTER |
| 104672 | 104781 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104673 | 104782 | ){ |
| 104674 | 104783 | *pReg = 0; |
| 104675 | 104784 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | ||
| 119424 | 119533 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119425 | 119534 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119426 | 119535 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119427 | 119536 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119428 | 119537 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119538 | + FUNCTION(substring, 2, 0, 0, substrFunc ), | |
| 119539 | + FUNCTION(substring, 3, 0, 0, substrFunc ), | |
| 119429 | 119540 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119430 | 119541 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119431 | 119542 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119432 | 119543 | WAGGREGATE(count, 0,0,0, countStep, |
| 119433 | 119544 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | ||
| 124310 | 124421 | /* Version 3.32.0 and later */ |
| 124311 | 124422 | char *(*create_filename)(const char*,const char*,const char*, |
| 124312 | 124423 | int,const char**); |
| 124313 | 124424 | void (*free_filename)(char*); |
| 124314 | 124425 | sqlite3_file *(*database_file_object)(const char*); |
| 124426 | + /* Version 3.34.0 and later */ | |
| 124427 | + int (*txn_state)(sqlite3*,const char*); | |
| 124315 | 124428 | }; |
| 124316 | 124429 | |
| 124317 | 124430 | /* |
| 124318 | 124431 | ** This is the function signature used for all extension entry points. It |
| 124319 | 124432 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | ||
| 124614 | 124727 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124615 | 124728 | /* Version 3.32.0 and later */ |
| 124616 | 124729 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124617 | 124730 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124618 | 124731 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124732 | +/* Version 3.34.0 and later */ | |
| 124733 | +#define sqlite3_txn_state sqlite3_api->txn_state | |
| 124619 | 124734 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124620 | 124735 | |
| 124621 | 124736 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124622 | 124737 | /* This case when the file really is being compiled as a loadable |
| 124623 | 124738 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | ||
| 125096 | 125211 | sqlite3_filename_wal, |
| 125097 | 125212 | /* Version 3.32.0 and later */ |
| 125098 | 125213 | sqlite3_create_filename, |
| 125099 | 125214 | sqlite3_free_filename, |
| 125100 | 125215 | sqlite3_database_file_object, |
| 125216 | + /* Version 3.34.0 and later */ | |
| 125217 | + sqlite3_txn_state, | |
| 125101 | 125218 | }; |
| 125102 | 125219 | |
| 125103 | 125220 | /* True if x is the directory separator character |
| 125104 | 125221 | */ |
| 125105 | 125222 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | ||
| 131654 | 131771 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131655 | 131772 | int nCol; /* Number of columns in the result set */ |
| 131656 | 131773 | char *zName; /* Column name */ |
| 131657 | 131774 | int nName; /* Size of name in zName[] */ |
| 131658 | 131775 | Hash ht; /* Hash table of column names */ |
| 131776 | + Table *pTab; | |
| 131659 | 131777 | |
| 131660 | 131778 | sqlite3HashInit(&ht); |
| 131661 | 131779 | if( pEList ){ |
| 131662 | 131780 | nCol = pEList->nExpr; |
| 131663 | 131781 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | ||
| 131676 | 131794 | */ |
| 131677 | 131795 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131678 | 131796 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131679 | 131797 | }else{ |
| 131680 | 131798 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131681 | - while( pColExpr->op==TK_DOT ){ | |
| 131799 | + while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ | |
| 131682 | 131800 | pColExpr = pColExpr->pRight; |
| 131683 | 131801 | assert( pColExpr!=0 ); |
| 131684 | 131802 | } |
| 131685 | - if( pColExpr->op==TK_COLUMN ){ | |
| 131803 | + if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ | |
| 131686 | 131804 | /* For columns use the column name name */ |
| 131687 | 131805 | int iCol = pColExpr->iColumn; |
| 131688 | - Table *pTab = pColExpr->y.pTab; | |
| 131689 | - assert( pTab!=0 ); | |
| 131690 | 131806 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131691 | 131807 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131692 | 131808 | }else if( pColExpr->op==TK_ID ){ |
| 131693 | 131809 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131694 | 131810 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | ||
| 136959 | 137075 | goto trigger_cleanup; |
| 136960 | 137076 | } |
| 136961 | 137077 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 136962 | 137078 | if( !pTab ){ |
| 136963 | 137079 | /* The table does not exist. */ |
| 136964 | - if( db->init.iDb==1 ){ | |
| 136965 | - /* Ticket #3810. | |
| 136966 | - ** Normally, whenever a table is dropped, all associated triggers are | |
| 136967 | - ** dropped too. But if a TEMP trigger is created on a non-TEMP table | |
| 136968 | - ** and the table is dropped by a different database connection, the | |
| 136969 | - ** trigger is not visible to the database connection that does the | |
| 136970 | - ** drop so the trigger cannot be dropped. This results in an | |
| 136971 | - ** "orphaned trigger" - a trigger whose associated table is missing. | |
| 136972 | - */ | |
| 136973 | - db->init.orphanTrigger = 1; | |
| 136974 | - } | |
| 136975 | - goto trigger_cleanup; | |
| 137080 | + goto trigger_orphan_error; | |
| 136976 | 137081 | } |
| 136977 | 137082 | if( IsVirtual(pTab) ){ |
| 136978 | 137083 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 136979 | - goto trigger_cleanup; | |
| 137084 | + goto trigger_orphan_error; | |
| 136980 | 137085 | } |
| 136981 | 137086 | |
| 136982 | 137087 | /* Check that the trigger name is not reserved and that no trigger of the |
| 136983 | 137088 | ** specified name exists */ |
| 136984 | 137089 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | ||
| 137012 | 137117 | ** of triggers. |
| 137013 | 137118 | */ |
| 137014 | 137119 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137015 | 137120 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137016 | 137121 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137017 | - goto trigger_cleanup; | |
| 137122 | + goto trigger_orphan_error; | |
| 137018 | 137123 | } |
| 137019 | 137124 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137020 | 137125 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137021 | 137126 | " trigger on table: %S", pTableName, 0); |
| 137022 | - goto trigger_cleanup; | |
| 137127 | + goto trigger_orphan_error; | |
| 137023 | 137128 | } |
| 137024 | 137129 | |
| 137025 | 137130 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137026 | 137131 | if( !IN_RENAME_OBJECT ){ |
| 137027 | 137132 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | ||
| 137077 | 137182 | if( !pParse->pNewTrigger ){ |
| 137078 | 137183 | sqlite3DeleteTrigger(db, pTrigger); |
| 137079 | 137184 | }else{ |
| 137080 | 137185 | assert( pParse->pNewTrigger==pTrigger ); |
| 137081 | 137186 | } |
| 137187 | + return; | |
| 137188 | + | |
| 137189 | +trigger_orphan_error: | |
| 137190 | + if( db->init.iDb==1 ){ | |
| 137191 | + /* Ticket #3810. | |
| 137192 | + ** Normally, whenever a table is dropped, all associated triggers are | |
| 137193 | + ** dropped too. But if a TEMP trigger is created on a non-TEMP table | |
| 137194 | + ** and the table is dropped by a different database connection, the | |
| 137195 | + ** trigger is not visible to the database connection that does the | |
| 137196 | + ** drop so the trigger cannot be dropped. This results in an | |
| 137197 | + ** "orphaned trigger" - a trigger whose associated table is missing. | |
| 137198 | + ** | |
| 137199 | + ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df | |
| 137200 | + */ | |
| 137201 | + db->init.orphanTrigger = 1; | |
| 137202 | + } | |
| 137203 | + goto trigger_cleanup; | |
| 137082 | 137204 | } |
| 137083 | 137205 | |
| 137084 | 137206 | /* |
| 137085 | 137207 | ** This routine is called after all of the trigger actions have been parsed |
| 137086 | 137208 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | ||
| 138664 | 138786 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138665 | 138787 | } |
| 138666 | 138788 | |
| 138667 | 138789 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138668 | 138790 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138791 | + iEph = pParse->nTab++; | |
| 138792 | + addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); | |
| 138669 | 138793 | }else{ |
| 138670 | 138794 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138671 | 138795 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138672 | 138796 | iPk = pParse->nMem+1; |
| 138673 | 138797 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | ||
| 138755 | 138879 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138756 | 138880 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138757 | 138881 | ** leave it in register regOldRowid. */ |
| 138758 | 138882 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138759 | 138883 | if( eOnePass==ONEPASS_OFF ){ |
| 138760 | - /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ | |
| 138761 | 138884 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138762 | - sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); | |
| 138885 | + sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); | |
| 138886 | + }else{ | |
| 138887 | + if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); | |
| 138763 | 138888 | } |
| 138764 | 138889 | }else{ |
| 138765 | 138890 | /* Read the PK of the current row into an array of registers. In |
| 138766 | 138891 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138767 | 138892 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | ||
| 138845 | 138970 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138846 | 138971 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138847 | 138972 | VdbeCoverage(v); |
| 138848 | 138973 | } |
| 138849 | 138974 | }else{ |
| 138850 | - labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, | |
| 138851 | - regOldRowid); | |
| 138975 | + sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); | |
| 138976 | + labelContinue = sqlite3VdbeMakeLabel(pParse); | |
| 138977 | + addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); | |
| 138852 | 138978 | VdbeCoverage(v); |
| 138853 | 138979 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138854 | 138980 | VdbeCoverage(v); |
| 138855 | 138981 | } |
| 138856 | 138982 | } |
| @@ -139096,15 +139222,13 @@ | ||
| 139096 | 139222 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139097 | 139223 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139098 | 139224 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139099 | 139225 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139100 | 139226 | sqlite3WhereEnd(pWInfo); |
| 139101 | - }else if( pPk || nChangeFrom ){ | |
| 139227 | + }else{ | |
| 139102 | 139228 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139103 | 139229 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139104 | - }else{ | |
| 139105 | - sqlite3VdbeGoto(v, labelContinue); | |
| 139106 | 139230 | } |
| 139107 | 139231 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139108 | 139232 | |
| 139109 | 139233 | /* Update the sqlite_sequence table by storing the content of the |
| 139110 | 139234 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | ||
| 145894 | 146018 | ** all terms of the WHERE clause. |
| 145895 | 146019 | */ |
| 145896 | 146020 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 145897 | 146021 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 145898 | 146022 | pWC->op = op; |
| 146023 | + assert( pE2!=0 || pExpr==0 ); | |
| 145899 | 146024 | if( pE2==0 ) return; |
| 145900 | 146025 | if( pE2->op!=op ){ |
| 145901 | 146026 | whereClauseInsert(pWC, pExpr, 0); |
| 145902 | 146027 | }else{ |
| 145903 | 146028 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | ||
| 146292 | 146417 | */ |
| 146293 | 146418 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146294 | 146419 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146295 | 146420 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146296 | 146421 | } |
| 146422 | + | |
| 146423 | +/* | |
| 146424 | +** If the right-hand branch of the expression is a TK_COLUMN, then return | |
| 146425 | +** a pointer to the right-hand branch. Otherwise, return NULL. | |
| 146426 | +*/ | |
| 146427 | +static Expr *whereRightSubexprIsColumn(Expr *p){ | |
| 146428 | + p = sqlite3ExprSkipCollateAndLikely(p->pRight); | |
| 146429 | + if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; | |
| 146430 | + return 0; | |
| 146431 | +} | |
| 146297 | 146432 | |
| 146298 | 146433 | /* |
| 146299 | 146434 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146300 | 146435 | ** established when the pScan object was initialized by whereScanInit(). |
| 146301 | 146436 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | ||
| 146323 | 146458 | pScan->pIdxExpr,iCur)==0) |
| 146324 | 146459 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146325 | 146460 | ){ |
| 146326 | 146461 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146327 | 146462 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146328 | - && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op | |
| 146329 | - ==TK_COLUMN | |
| 146463 | + && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 | |
| 146330 | 146464 | ){ |
| 146331 | 146465 | int j; |
| 146332 | 146466 | for(j=0; j<pScan->nEquiv; j++){ |
| 146333 | 146467 | if( pScan->aiCur[j]==pX->iTable |
| 146334 | 146468 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | ||
| 146520 | 146654 | int i; |
| 146521 | 146655 | const char *zColl = pIdx->azColl[iCol]; |
| 146522 | 146656 | |
| 146523 | 146657 | for(i=0; i<pList->nExpr; i++){ |
| 146524 | 146658 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146525 | - if( p->op==TK_COLUMN | |
| 146659 | + if( ALWAYS(p!=0) | |
| 146660 | + && p->op==TK_COLUMN | |
| 146526 | 146661 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146527 | 146662 | && p->iTable==iBase |
| 146528 | 146663 | ){ |
| 146529 | 146664 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146530 | 146665 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | ||
| 146584 | 146719 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146585 | 146720 | ** current SELECT is a correlated sub-query. |
| 146586 | 146721 | */ |
| 146587 | 146722 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146588 | 146723 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146724 | + if( NEVER(p==0) ) continue; | |
| 146589 | 146725 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146590 | 146726 | } |
| 146591 | 146727 | |
| 146592 | 146728 | /* Loop through all indices on the table, checking each to see if it makes |
| 146593 | 146729 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | ||
| 148498 | 148634 | LogEst rLogSize; /* Logarithm of table size */ |
| 148499 | 148635 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148500 | 148636 | |
| 148501 | 148637 | pNew = pBuilder->pNew; |
| 148502 | 148638 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148503 | - WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", | |
| 148639 | + WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", | |
| 148504 | 148640 | pProbe->pTable->zName,pProbe->zName, |
| 148505 | - pNew->u.btree.nEq, pNew->nSkip)); | |
| 148641 | + pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); | |
| 148506 | 148642 | |
| 148507 | 148643 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148508 | 148644 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148509 | 148645 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148510 | 148646 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | ||
| 148869 | 149005 | |
| 148870 | 149006 | if( pIndex->bUnordered ) return 0; |
| 148871 | 149007 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 148872 | 149008 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 148873 | 149009 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 149010 | + if( NEVER(pExpr==0) ) continue; | |
| 148874 | 149011 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 148875 | 149012 | if( pExpr->iColumn<0 ) return 1; |
| 148876 | 149013 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 148877 | 149014 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 148878 | 149015 | } |
| @@ -149847,10 +149984,11 @@ | ||
| 149847 | 149984 | ** loops. |
| 149848 | 149985 | */ |
| 149849 | 149986 | for(i=0; i<nOrderBy; i++){ |
| 149850 | 149987 | if( MASKBIT(i) & obSat ) continue; |
| 149851 | 149988 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149989 | + if( NEVER(pOBExpr==0) ) continue; | |
| 149852 | 149990 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149853 | 149991 | if( pOBExpr->iTable!=iCur ) continue; |
| 149854 | 149992 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149855 | 149993 | ~ready, eqOpMask, 0); |
| 149856 | 149994 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | ||
| 149973 | 150111 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 149974 | 150112 | if( MASKBIT(i) & obSat ) continue; |
| 149975 | 150113 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149976 | 150114 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 149977 | 150115 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 150116 | + if( NEVER(pOBExpr==0) ) continue; | |
| 149978 | 150117 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 149979 | 150118 | if( iColumn>=XN_ROWID ){ |
| 149980 | 150119 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149981 | 150120 | if( pOBExpr->iTable!=iCur ) continue; |
| 149982 | 150121 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | ||
| 150136 | 150275 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150137 | 150276 | rSortCost = nRow + rScale + 16; |
| 150138 | 150277 | |
| 150139 | 150278 | /* Multiple by log(M) where M is the number of output rows. |
| 150140 | 150279 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150141 | - ** a DISTINT operator, M will be the number of distinct output | |
| 150280 | + ** a DISTINCT operator, M will be the number of distinct output | |
| 150142 | 150281 | ** rows, so fudge it downwards a bit. |
| 150143 | 150282 | */ |
| 150144 | 150283 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150145 | 150284 | nRow = pWInfo->iLimit; |
| 150146 | 150285 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | ||
| 194220 | 194359 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194221 | 194360 | p->nEvent = p->nSegment = 0; |
| 194222 | 194361 | geopolyAddSegments(p, p1, 1); |
| 194223 | 194362 | geopolyAddSegments(p, p2, 2); |
| 194224 | 194363 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194225 | - rX = pThisEvent->x==0.0 ? -1.0 : 0.0; | |
| 194364 | + rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; | |
| 194226 | 194365 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194227 | 194366 | while( pThisEvent ){ |
| 194228 | 194367 | if( pThisEvent->x!=rX ){ |
| 194229 | 194368 | GeoSegment *pPrev = 0; |
| 194230 | 194369 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | ||
| 212129 | 212268 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212130 | 212269 | ){ |
| 212131 | 212270 | int rc = SQLITE_OK; /* Return code */ |
| 212132 | 212271 | Fts5Bm25Data *p; /* Object to return */ |
| 212133 | 212272 | |
| 212134 | - p = pApi->xGetAuxdata(pFts, 0); | |
| 212273 | + p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); | |
| 212135 | 212274 | if( p==0 ){ |
| 212136 | 212275 | int nPhrase; /* Number of phrases in query */ |
| 212137 | 212276 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212138 | 212277 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212139 | 212278 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | ||
| 212203 | 212342 | int nVal, /* Number of values in apVal[] array */ |
| 212204 | 212343 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212205 | 212344 | ){ |
| 212206 | 212345 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212207 | 212346 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212208 | - int rc = SQLITE_OK; /* Error code */ | |
| 212347 | + int rc; /* Error code */ | |
| 212209 | 212348 | double score = 0.0; /* SQL function return value */ |
| 212210 | 212349 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212211 | 212350 | int i; /* Iterator variable */ |
| 212212 | 212351 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212213 | 212352 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | ||
| 212235 | 212374 | int nTok; |
| 212236 | 212375 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212237 | 212376 | D = (double)nTok; |
| 212238 | 212377 | } |
| 212239 | 212378 | |
| 212240 | - /* Determine the BM25 score for the current row. */ | |
| 212241 | - for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){ | |
| 212242 | - score += pData->aIDF[i] * ( | |
| 212243 | - ( aFreq[i] * (k1 + 1.0) ) / | |
| 212244 | - ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) | |
| 212245 | - ); | |
| 212246 | - } | |
| 212247 | - | |
| 212248 | - /* If no error has occurred, return the calculated score. Otherwise, | |
| 212249 | - ** throw an SQL exception. */ | |
| 212379 | + /* Determine and return the BM25 score for the current row. Or, if an | |
| 212380 | + ** error has occurred, throw an exception. */ | |
| 212250 | 212381 | if( rc==SQLITE_OK ){ |
| 212382 | + for(i=0; i<pData->nPhrase; i++){ | |
| 212383 | + score += pData->aIDF[i] * ( | |
| 212384 | + ( aFreq[i] * (k1 + 1.0) ) / | |
| 212385 | + ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) | |
| 212386 | + ); | |
| 212387 | + } | |
| 212251 | 212388 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212252 | 212389 | }else{ |
| 212253 | 212390 | sqlite3_result_error_code(pCtx, rc); |
| 212254 | 212391 | } |
| 212255 | 212392 | } |
| @@ -223384,10 +223521,11 @@ | ||
| 223384 | 223521 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223385 | 223522 | } |
| 223386 | 223523 | }else{ |
| 223387 | 223524 | poslist.n = 0; |
| 223388 | 223525 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223526 | + fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); | |
| 223389 | 223527 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223390 | 223528 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223391 | 223529 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223392 | 223530 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223393 | 223531 | } |
| @@ -226679,11 +226817,11 @@ | ||
| 226679 | 226817 | int nArg, /* Number of args */ |
| 226680 | 226818 | sqlite3_value **apUnused /* Function arguments */ |
| 226681 | 226819 | ){ |
| 226682 | 226820 | assert( nArg==0 ); |
| 226683 | 226821 | UNUSED_PARAM2(nArg, apUnused); |
| 226684 | - sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT); | |
| 226822 | + sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT); | |
| 226685 | 226823 | } |
| 226686 | 226824 | |
| 226687 | 226825 | /* |
| 226688 | 226826 | ** Return true if zName is the extension on one of the shadow tables used |
| 226689 | 226827 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | ||
| 229252 | 229390 | |
| 229253 | 229391 | /* |
| 229254 | 229392 | ** Allocate a trigram tokenizer. |
| 229255 | 229393 | */ |
| 229256 | 229394 | static int fts5TriCreate( |
| 229257 | - void *pCtx, | |
| 229395 | + void *pUnused, | |
| 229258 | 229396 | const char **azArg, |
| 229259 | 229397 | int nArg, |
| 229260 | 229398 | Fts5Tokenizer **ppOut |
| 229261 | 229399 | ){ |
| 229262 | 229400 | int rc = SQLITE_OK; |
| 229263 | 229401 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229402 | + UNUSED_PARAM(pUnused); | |
| 229264 | 229403 | if( pNew==0 ){ |
| 229265 | 229404 | rc = SQLITE_NOMEM; |
| 229266 | 229405 | }else{ |
| 229267 | 229406 | int i; |
| 229268 | 229407 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | ||
| 229291 | 229430 | ** Trigram tokenizer tokenize routine. |
| 229292 | 229431 | */ |
| 229293 | 229432 | static int fts5TriTokenize( |
| 229294 | 229433 | Fts5Tokenizer *pTok, |
| 229295 | 229434 | void *pCtx, |
| 229296 | - int flags, | |
| 229435 | + int unusedFlags, | |
| 229297 | 229436 | const char *pText, int nText, |
| 229298 | 229437 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229299 | 229438 | ){ |
| 229300 | 229439 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229301 | 229440 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | ||
| 229302 | 229441 | char aBuf[32]; |
| 229303 | 229442 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229304 | 229443 | const unsigned char *zEof = &zIn[nText]; |
| 229305 | 229444 | u32 iCode; |
| 229306 | 229445 | |
| 229446 | + UNUSED_PARAM(unusedFlags); | |
| 229307 | 229447 | while( 1 ){ |
| 229308 | 229448 | char *zOut = aBuf; |
| 229309 | 229449 | int iStart = zIn - (const unsigned char*)pText; |
| 229310 | 229450 | const unsigned char *zNext; |
| 229311 | 229451 | |
| @@ -230164,10 +230304,11 @@ | ||
| 230164 | 230304 | } |
| 230165 | 230305 | iTbl++; |
| 230166 | 230306 | } |
| 230167 | 230307 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230168 | 230308 | } |
| 230309 | + | |
| 230169 | 230310 | |
| 230170 | 230311 | /* |
| 230171 | 230312 | ** 2015 May 30 |
| 230172 | 230313 | ** |
| 230173 | 230314 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | ||
| 231602 | 231743 | } |
| 231603 | 231744 | #endif /* SQLITE_CORE */ |
| 231604 | 231745 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231605 | 231746 | |
| 231606 | 231747 | /************** End of stmt.c ************************************************/ |
| 231607 | -#if __LINE__!=231607 | |
| 231748 | +#if __LINE__!=231748 | |
| 231608 | 231749 | #undef SQLITE_SOURCE_ID |
| 231609 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2" | |
| 231750 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2" | |
| 231610 | 231751 | #endif |
| 231611 | 231752 | /* Return the source-id for this library */ |
| 231612 | 231753 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231613 | 231754 | /************************** End of sqlite3.c ******************************/ |
| 231614 | 231755 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1171,11 +1171,11 @@ | |
| 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | */ |
| 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" |
| 1177 | |
| 1178 | /* |
| 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | ** |
| @@ -1550,10 +1550,11 @@ | |
| 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1556 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1557 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1558 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1559 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | |
| 7238 | ** CAPI3REF: Determine the transaction state of a database |
| 7239 | ** METHOD: sqlite3 |
| 7240 | ** |
| 7241 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7242 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7243 | ** then the highest transaction state of any schema on databse connection D |
| 7244 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7245 | ** <ol> |
| 7246 | ** <li value="0"> SQLITE_TXN_NONE |
| 7247 | ** <li value="1"> SQLITE_TXN_READ |
| 7248 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | |
| 8785 | */ |
| 8786 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8787 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8788 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8789 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8790 | #define SQLITE_TESTCTRL_SEEK_COUNT 7 |
| 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | |
| 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ |
| 8816 | |
| 8817 | /* |
| 8818 | ** CAPI3REF: SQL Keyword Checking |
| 8819 | ** |
| 8820 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | |
| 28182 | EnableLookaside; |
| 28183 | } |
| 28184 | } |
| 28185 | |
| 28186 | /* |
| 28187 | ** Take actions at the end of an API call to indicate an OOM error |
| 28188 | */ |
| 28189 | static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ |
| 28190 | sqlite3OomClear(db); |
| 28191 | sqlite3Error(db, SQLITE_NOMEM); |
| 28192 | return SQLITE_NOMEM_BKPT; |
| 28193 | } |
| 28194 | |
| 28195 | /* |
| 28196 | ** This function must be called before exiting any API function (i.e. |
| 28197 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | |
| 28209 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28210 | ** is unsafe, as is the call to sqlite3Error(). |
| 28211 | */ |
| 28212 | assert( db!=0 ); |
| 28213 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28214 | if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ |
| 28215 | return apiOomError(db); |
| 28216 | } |
| 28217 | return rc & db->errMask; |
| 28218 | } |
| 28219 | |
| 28220 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | |
| 37003 | |
| 37004 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37005 | if( got==amt ){ |
| 37006 | return SQLITE_OK; |
| 37007 | }else if( got<0 ){ |
| 37008 | /* lastErrno set by seekAndRead */ |
| 37009 | return SQLITE_IOERR_READ; |
| 37010 | }else{ |
| 37011 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37012 | /* Unread parts of the buffer must be zero-filled */ |
| 37013 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | |
| 38535 | if( bUnlock ){ |
| 38536 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38537 | if( rc==SQLITE_OK ){ |
| 38538 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38539 | } |
| 38540 | }else if( p->sharedMask & (1<<ofst) ){ |
| 38541 | assert( n==1 && aLock[ofst]>1 ); |
| 38542 | aLock[ofst]--; |
| 38543 | } |
| 38544 | |
| 38545 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | |
| 38568 | /* Make sure no sibling connections hold locks that will block this |
| 38569 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38570 | int ii; |
| 38571 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38572 | assert( (p->sharedMask & mask)==0 ); |
| 38573 | if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){ |
| 38574 | rc = SQLITE_BUSY; |
| 38575 | break; |
| 38576 | } |
| 38577 | } |
| 38578 | |
| @@ -39964,19 +39985,39 @@ | |
| 39964 | } |
| 39965 | return SQLITE_OK; |
| 39966 | } |
| 39967 | |
| 39968 | /* |
| 39969 | ** |
| 39970 | */ |
| 39971 | static int mkFullPathname( |
| 39972 | const char *zPath, /* Input path */ |
| 39973 | char *zOut, /* Output buffer */ |
| 39974 | int nOut /* Allocated size of buffer zOut */ |
| 39975 | ){ |
| 39976 | int nPath = sqlite3Strlen30(zPath); |
| 39977 | int iOff = 0; |
| 39978 | if( zPath[0]!='/' ){ |
| 39979 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 39980 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 39981 | } |
| 39982 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | |
| 39987 | ** even if it returns an error. */ |
| 39988 | zOut[iOff] = '\0'; |
| 39989 | return SQLITE_CANTOPEN_BKPT; |
| 39990 | } |
| 39991 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 39992 | return SQLITE_OK; |
| 39993 | } |
| 39994 | |
| 39995 | /* |
| 39996 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | |
| 54326 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54327 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54328 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54329 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54330 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54331 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54332 | |
| 54333 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54334 | ** If successful, open the super-journal file for reading. |
| 54335 | */ |
| @@ -54350,15 +54427,17 @@ | |
| 54350 | ** files extracted from regular rollback-journals. |
| 54351 | */ |
| 54352 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54353 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54354 | nSuperPtr = pVfs->mxPathname+1; |
| 54355 | zSuperJournal = sqlite3Malloc(nSuperJournal + nSuperPtr + 2); |
| 54356 | if( !zSuperJournal ){ |
| 54357 | rc = SQLITE_NOMEM_BKPT; |
| 54358 | goto delsuper_out; |
| 54359 | } |
| 54360 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54361 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54362 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54363 | zSuperJournal[nSuperJournal] = 0; |
| 54364 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | |
| 54402 | |
| 54403 | sqlite3OsClose(pSuper); |
| 54404 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54405 | |
| 54406 | delsuper_out: |
| 54407 | sqlite3_free(zSuperJournal); |
| 54408 | if( pSuper ){ |
| 54409 | sqlite3OsClose(pSuper); |
| 54410 | assert( !isOpen(pJournal) ); |
| 54411 | sqlite3_free(pSuper); |
| 54412 | } |
| @@ -54740,11 +54819,15 @@ | |
| 54740 | ** in case this has happened, clear the changeCountDone flag now. |
| 54741 | */ |
| 54742 | pPager->changeCountDone = pPager->tempFile; |
| 54743 | |
| 54744 | if( rc==SQLITE_OK ){ |
| 54745 | zSuper = pPager->pTmpSpace; |
| 54746 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54747 | testcase( rc!=SQLITE_OK ); |
| 54748 | } |
| 54749 | if( rc==SQLITE_OK |
| 54750 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | |
| 54757 | } |
| 54758 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54759 | /* If there was a super-journal and this routine will return success, |
| 54760 | ** see if it is possible to delete the super-journal. |
| 54761 | */ |
| 54762 | rc = pager_delsuper(pPager, zSuper); |
| 54763 | testcase( rc!=SQLITE_OK ); |
| 54764 | } |
| 54765 | if( isHot && nPlayback ){ |
| 54766 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | |
| 64746 | #define hasReadConflicts(a, b) 0 |
| 64747 | #endif |
| 64748 | |
| 64749 | #ifdef SQLITE_DEBUG |
| 64750 | /* |
| 64751 | ** Return an reset the seek counter for a Btree object. |
| 64752 | */ |
| 64753 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64754 | u64 n = pBt->nSeek; |
| 64755 | pBt->nSeek = 0; |
| 64756 | return n; |
| @@ -82192,13 +82277,16 @@ | |
| 82192 | ** equal to, or greater than the second (double). |
| 82193 | */ |
| 82194 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82195 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82196 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82197 | if( x<r ) return -1; |
| 82198 | if( x>r ) return +1; |
| 82199 | return 0; |
| 82200 | }else{ |
| 82201 | i64 y; |
| 82202 | double s; |
| 82203 | if( r<-9223372036854775808.0 ) return +1; |
| 82204 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | |
| 89388 | assert( rc==SQLITE_OK ); |
| 89389 | break; |
| 89390 | } |
| 89391 | |
| 89392 | |
| 89393 | /* Opcode: OpenEphemeral P1 P2 * P4 P5 |
| 89394 | ** Synopsis: nColumn=P2 |
| 89395 | ** |
| 89396 | ** Open a new cursor P1 to a transient table. |
| 89397 | ** The cursor is always opened read/write even if |
| 89398 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | |
| 89408 | ** |
| 89409 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89410 | ** in btree.h. These flags control aspects of the operation of |
| 89411 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89412 | ** added automatically. |
| 89413 | */ |
| 89414 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89415 | ** Synopsis: nColumn=P2 |
| 89416 | ** |
| 89417 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | |
| 89430 | SQLITE_OPEN_EXCLUSIVE | |
| 89431 | SQLITE_OPEN_DELETEONCLOSE | |
| 89432 | SQLITE_OPEN_TRANSIENT_DB; |
| 89433 | assert( pOp->p1>=0 ); |
| 89434 | assert( pOp->p2>=0 ); |
| 89435 | pCx = p->apCsr[pOp->p1]; |
| 89436 | if( pCx && pCx->pBtx ){ |
| 89437 | /* If the ephermeral table is already open, erase all existing content |
| 89438 | ** so that the table is empty again, rather than creating a new table. */ |
| 89439 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | |
| 90589 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90590 | #endif |
| 90591 | |
| 90592 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90593 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90594 | assert( pData->flags & (MEM_Blob|MEM_Str) ); |
| 90595 | x.pData = pData->z; |
| 90596 | x.nData = pData->n; |
| 90597 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90598 | if( pData->flags & MEM_Zero ){ |
| 90599 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | |
| 93644 | |
| 93645 | /* If we reach this point, it means that execution is finished with |
| 93646 | ** an error of some kind. |
| 93647 | */ |
| 93648 | abort_due_to_error: |
| 93649 | if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT; |
| 93650 | assert( rc ); |
| 93651 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93652 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93653 | } |
| 93654 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | |
| 99381 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99382 | int iCol = -1; |
| 99383 | Expr *pE, *pDup; |
| 99384 | if( pItem->done ) continue; |
| 99385 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99386 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99387 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99388 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99389 | return 1; |
| 99390 | } |
| @@ -99560,10 +99666,11 @@ | |
| 99560 | nResult = pSelect->pEList->nExpr; |
| 99561 | pParse = pNC->pParse; |
| 99562 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99563 | Expr *pE = pItem->pExpr; |
| 99564 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99565 | if( zType[0]!='G' ){ |
| 99566 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99567 | if( iCol>0 ){ |
| 99568 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99569 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | |
| 103679 | ** register iReg. The caller must ensure that iReg already contains |
| 103680 | ** the correct value for the expression. |
| 103681 | */ |
| 103682 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103683 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103684 | p->op2 = p->op; |
| 103685 | p->op = TK_REGISTER; |
| 103686 | p->iTable = iReg; |
| 103687 | ExprClearProperty(p, EP_Skip); |
| 103688 | } |
| @@ -104666,10 +104774,11 @@ | |
| 104666 | */ |
| 104667 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104668 | int r2; |
| 104669 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104670 | if( ConstFactorOk(pParse) |
| 104671 | && pExpr->op!=TK_REGISTER |
| 104672 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104673 | ){ |
| 104674 | *pReg = 0; |
| 104675 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | |
| 119424 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119425 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119426 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119427 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119428 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119429 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119430 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119431 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119432 | WAGGREGATE(count, 0,0,0, countStep, |
| 119433 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | |
| 124310 | /* Version 3.32.0 and later */ |
| 124311 | char *(*create_filename)(const char*,const char*,const char*, |
| 124312 | int,const char**); |
| 124313 | void (*free_filename)(char*); |
| 124314 | sqlite3_file *(*database_file_object)(const char*); |
| 124315 | }; |
| 124316 | |
| 124317 | /* |
| 124318 | ** This is the function signature used for all extension entry points. It |
| 124319 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | |
| 124614 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124615 | /* Version 3.32.0 and later */ |
| 124616 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124617 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124618 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124619 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124620 | |
| 124621 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124622 | /* This case when the file really is being compiled as a loadable |
| 124623 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | |
| 125096 | sqlite3_filename_wal, |
| 125097 | /* Version 3.32.0 and later */ |
| 125098 | sqlite3_create_filename, |
| 125099 | sqlite3_free_filename, |
| 125100 | sqlite3_database_file_object, |
| 125101 | }; |
| 125102 | |
| 125103 | /* True if x is the directory separator character |
| 125104 | */ |
| 125105 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | |
| 131654 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131655 | int nCol; /* Number of columns in the result set */ |
| 131656 | char *zName; /* Column name */ |
| 131657 | int nName; /* Size of name in zName[] */ |
| 131658 | Hash ht; /* Hash table of column names */ |
| 131659 | |
| 131660 | sqlite3HashInit(&ht); |
| 131661 | if( pEList ){ |
| 131662 | nCol = pEList->nExpr; |
| 131663 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | |
| 131676 | */ |
| 131677 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131678 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131679 | }else{ |
| 131680 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131681 | while( pColExpr->op==TK_DOT ){ |
| 131682 | pColExpr = pColExpr->pRight; |
| 131683 | assert( pColExpr!=0 ); |
| 131684 | } |
| 131685 | if( pColExpr->op==TK_COLUMN ){ |
| 131686 | /* For columns use the column name name */ |
| 131687 | int iCol = pColExpr->iColumn; |
| 131688 | Table *pTab = pColExpr->y.pTab; |
| 131689 | assert( pTab!=0 ); |
| 131690 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131691 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131692 | }else if( pColExpr->op==TK_ID ){ |
| 131693 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131694 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | |
| 136959 | goto trigger_cleanup; |
| 136960 | } |
| 136961 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 136962 | if( !pTab ){ |
| 136963 | /* The table does not exist. */ |
| 136964 | if( db->init.iDb==1 ){ |
| 136965 | /* Ticket #3810. |
| 136966 | ** Normally, whenever a table is dropped, all associated triggers are |
| 136967 | ** dropped too. But if a TEMP trigger is created on a non-TEMP table |
| 136968 | ** and the table is dropped by a different database connection, the |
| 136969 | ** trigger is not visible to the database connection that does the |
| 136970 | ** drop so the trigger cannot be dropped. This results in an |
| 136971 | ** "orphaned trigger" - a trigger whose associated table is missing. |
| 136972 | */ |
| 136973 | db->init.orphanTrigger = 1; |
| 136974 | } |
| 136975 | goto trigger_cleanup; |
| 136976 | } |
| 136977 | if( IsVirtual(pTab) ){ |
| 136978 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 136979 | goto trigger_cleanup; |
| 136980 | } |
| 136981 | |
| 136982 | /* Check that the trigger name is not reserved and that no trigger of the |
| 136983 | ** specified name exists */ |
| 136984 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | |
| 137012 | ** of triggers. |
| 137013 | */ |
| 137014 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137015 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137016 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137017 | goto trigger_cleanup; |
| 137018 | } |
| 137019 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137020 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137021 | " trigger on table: %S", pTableName, 0); |
| 137022 | goto trigger_cleanup; |
| 137023 | } |
| 137024 | |
| 137025 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137026 | if( !IN_RENAME_OBJECT ){ |
| 137027 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | |
| 137077 | if( !pParse->pNewTrigger ){ |
| 137078 | sqlite3DeleteTrigger(db, pTrigger); |
| 137079 | }else{ |
| 137080 | assert( pParse->pNewTrigger==pTrigger ); |
| 137081 | } |
| 137082 | } |
| 137083 | |
| 137084 | /* |
| 137085 | ** This routine is called after all of the trigger actions have been parsed |
| 137086 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | |
| 138664 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138665 | } |
| 138666 | |
| 138667 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138668 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138669 | }else{ |
| 138670 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138671 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138672 | iPk = pParse->nMem+1; |
| 138673 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | |
| 138755 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138756 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138757 | ** leave it in register regOldRowid. */ |
| 138758 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138759 | if( eOnePass==ONEPASS_OFF ){ |
| 138760 | /* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */ |
| 138761 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138762 | sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); |
| 138763 | } |
| 138764 | }else{ |
| 138765 | /* Read the PK of the current row into an array of registers. In |
| 138766 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138767 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | |
| 138845 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138846 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138847 | VdbeCoverage(v); |
| 138848 | } |
| 138849 | }else{ |
| 138850 | labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak, |
| 138851 | regOldRowid); |
| 138852 | VdbeCoverage(v); |
| 138853 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138854 | VdbeCoverage(v); |
| 138855 | } |
| 138856 | } |
| @@ -139096,15 +139222,13 @@ | |
| 139096 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139097 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139098 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139099 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139100 | sqlite3WhereEnd(pWInfo); |
| 139101 | }else if( pPk || nChangeFrom ){ |
| 139102 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139103 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139104 | }else{ |
| 139105 | sqlite3VdbeGoto(v, labelContinue); |
| 139106 | } |
| 139107 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139108 | |
| 139109 | /* Update the sqlite_sequence table by storing the content of the |
| 139110 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | |
| 145894 | ** all terms of the WHERE clause. |
| 145895 | */ |
| 145896 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 145897 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 145898 | pWC->op = op; |
| 145899 | if( pE2==0 ) return; |
| 145900 | if( pE2->op!=op ){ |
| 145901 | whereClauseInsert(pWC, pExpr, 0); |
| 145902 | }else{ |
| 145903 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | |
| 146292 | */ |
| 146293 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146294 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146295 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146296 | } |
| 146297 | |
| 146298 | /* |
| 146299 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146300 | ** established when the pScan object was initialized by whereScanInit(). |
| 146301 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | |
| 146323 | pScan->pIdxExpr,iCur)==0) |
| 146324 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146325 | ){ |
| 146326 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146327 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146328 | && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op |
| 146329 | ==TK_COLUMN |
| 146330 | ){ |
| 146331 | int j; |
| 146332 | for(j=0; j<pScan->nEquiv; j++){ |
| 146333 | if( pScan->aiCur[j]==pX->iTable |
| 146334 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | |
| 146520 | int i; |
| 146521 | const char *zColl = pIdx->azColl[iCol]; |
| 146522 | |
| 146523 | for(i=0; i<pList->nExpr; i++){ |
| 146524 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146525 | if( p->op==TK_COLUMN |
| 146526 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146527 | && p->iTable==iBase |
| 146528 | ){ |
| 146529 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146530 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | |
| 146584 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146585 | ** current SELECT is a correlated sub-query. |
| 146586 | */ |
| 146587 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146588 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146589 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146590 | } |
| 146591 | |
| 146592 | /* Loop through all indices on the table, checking each to see if it makes |
| 146593 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | |
| 148498 | LogEst rLogSize; /* Logarithm of table size */ |
| 148499 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148500 | |
| 148501 | pNew = pBuilder->pNew; |
| 148502 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148503 | WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d\n", |
| 148504 | pProbe->pTable->zName,pProbe->zName, |
| 148505 | pNew->u.btree.nEq, pNew->nSkip)); |
| 148506 | |
| 148507 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148508 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148509 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148510 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | |
| 148869 | |
| 148870 | if( pIndex->bUnordered ) return 0; |
| 148871 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 148872 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 148873 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 148874 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 148875 | if( pExpr->iColumn<0 ) return 1; |
| 148876 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 148877 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 148878 | } |
| @@ -149847,10 +149984,11 @@ | |
| 149847 | ** loops. |
| 149848 | */ |
| 149849 | for(i=0; i<nOrderBy; i++){ |
| 149850 | if( MASKBIT(i) & obSat ) continue; |
| 149851 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149852 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149853 | if( pOBExpr->iTable!=iCur ) continue; |
| 149854 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149855 | ~ready, eqOpMask, 0); |
| 149856 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | |
| 149973 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 149974 | if( MASKBIT(i) & obSat ) continue; |
| 149975 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149976 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 149977 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 149978 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 149979 | if( iColumn>=XN_ROWID ){ |
| 149980 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149981 | if( pOBExpr->iTable!=iCur ) continue; |
| 149982 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | |
| 150136 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150137 | rSortCost = nRow + rScale + 16; |
| 150138 | |
| 150139 | /* Multiple by log(M) where M is the number of output rows. |
| 150140 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150141 | ** a DISTINT operator, M will be the number of distinct output |
| 150142 | ** rows, so fudge it downwards a bit. |
| 150143 | */ |
| 150144 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150145 | nRow = pWInfo->iLimit; |
| 150146 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | |
| 194220 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194221 | p->nEvent = p->nSegment = 0; |
| 194222 | geopolyAddSegments(p, p1, 1); |
| 194223 | geopolyAddSegments(p, p2, 2); |
| 194224 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194225 | rX = pThisEvent->x==0.0 ? -1.0 : 0.0; |
| 194226 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194227 | while( pThisEvent ){ |
| 194228 | if( pThisEvent->x!=rX ){ |
| 194229 | GeoSegment *pPrev = 0; |
| 194230 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | |
| 212129 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212130 | ){ |
| 212131 | int rc = SQLITE_OK; /* Return code */ |
| 212132 | Fts5Bm25Data *p; /* Object to return */ |
| 212133 | |
| 212134 | p = pApi->xGetAuxdata(pFts, 0); |
| 212135 | if( p==0 ){ |
| 212136 | int nPhrase; /* Number of phrases in query */ |
| 212137 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212138 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212139 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | |
| 212203 | int nVal, /* Number of values in apVal[] array */ |
| 212204 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212205 | ){ |
| 212206 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212207 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212208 | int rc = SQLITE_OK; /* Error code */ |
| 212209 | double score = 0.0; /* SQL function return value */ |
| 212210 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212211 | int i; /* Iterator variable */ |
| 212212 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212213 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | |
| 212235 | int nTok; |
| 212236 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212237 | D = (double)nTok; |
| 212238 | } |
| 212239 | |
| 212240 | /* Determine the BM25 score for the current row. */ |
| 212241 | for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){ |
| 212242 | score += pData->aIDF[i] * ( |
| 212243 | ( aFreq[i] * (k1 + 1.0) ) / |
| 212244 | ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) |
| 212245 | ); |
| 212246 | } |
| 212247 | |
| 212248 | /* If no error has occurred, return the calculated score. Otherwise, |
| 212249 | ** throw an SQL exception. */ |
| 212250 | if( rc==SQLITE_OK ){ |
| 212251 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212252 | }else{ |
| 212253 | sqlite3_result_error_code(pCtx, rc); |
| 212254 | } |
| 212255 | } |
| @@ -223384,10 +223521,11 @@ | |
| 223384 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223385 | } |
| 223386 | }else{ |
| 223387 | poslist.n = 0; |
| 223388 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223389 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223390 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223391 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223392 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223393 | } |
| @@ -226679,11 +226817,11 @@ | |
| 226679 | int nArg, /* Number of args */ |
| 226680 | sqlite3_value **apUnused /* Function arguments */ |
| 226681 | ){ |
| 226682 | assert( nArg==0 ); |
| 226683 | UNUSED_PARAM2(nArg, apUnused); |
| 226684 | sqlite3_result_text(pCtx, "fts5: 2020-10-26 16:22:31 80eba105d6d1b49ba8ca2ad4e14ddec2de0bdc2f6686c2f8a1c1d24fc1fe846f", -1, SQLITE_TRANSIENT); |
| 226685 | } |
| 226686 | |
| 226687 | /* |
| 226688 | ** Return true if zName is the extension on one of the shadow tables used |
| 226689 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | |
| 229252 | |
| 229253 | /* |
| 229254 | ** Allocate a trigram tokenizer. |
| 229255 | */ |
| 229256 | static int fts5TriCreate( |
| 229257 | void *pCtx, |
| 229258 | const char **azArg, |
| 229259 | int nArg, |
| 229260 | Fts5Tokenizer **ppOut |
| 229261 | ){ |
| 229262 | int rc = SQLITE_OK; |
| 229263 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229264 | if( pNew==0 ){ |
| 229265 | rc = SQLITE_NOMEM; |
| 229266 | }else{ |
| 229267 | int i; |
| 229268 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | |
| 229291 | ** Trigram tokenizer tokenize routine. |
| 229292 | */ |
| 229293 | static int fts5TriTokenize( |
| 229294 | Fts5Tokenizer *pTok, |
| 229295 | void *pCtx, |
| 229296 | int flags, |
| 229297 | const char *pText, int nText, |
| 229298 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229299 | ){ |
| 229300 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229301 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | |
| 229302 | char aBuf[32]; |
| 229303 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229304 | const unsigned char *zEof = &zIn[nText]; |
| 229305 | u32 iCode; |
| 229306 | |
| 229307 | while( 1 ){ |
| 229308 | char *zOut = aBuf; |
| 229309 | int iStart = zIn - (const unsigned char*)pText; |
| 229310 | const unsigned char *zNext; |
| 229311 | |
| @@ -230164,10 +230304,11 @@ | |
| 230164 | } |
| 230165 | iTbl++; |
| 230166 | } |
| 230167 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230168 | } |
| 230169 | |
| 230170 | /* |
| 230171 | ** 2015 May 30 |
| 230172 | ** |
| 230173 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | |
| 231602 | } |
| 231603 | #endif /* SQLITE_CORE */ |
| 231604 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231605 | |
| 231606 | /************** End of stmt.c ************************************************/ |
| 231607 | #if __LINE__!=231607 |
| 231608 | #undef SQLITE_SOURCE_ID |
| 231609 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692alt2" |
| 231610 | #endif |
| 231611 | /* Return the source-id for this library */ |
| 231612 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231613 | /************************** End of sqlite3.c ******************************/ |
| 231614 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1171,11 +1171,11 @@ | |
| 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | */ |
| 1174 | #define SQLITE_VERSION "3.34.0" |
| 1175 | #define SQLITE_VERSION_NUMBER 3034000 |
| 1176 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" |
| 1177 | |
| 1178 | /* |
| 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | ** |
| @@ -1550,10 +1550,11 @@ | |
| 1550 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 1551 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 1552 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 1553 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 1554 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 1555 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 1556 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 1557 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 1558 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 1559 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 1560 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -7238,11 +7239,11 @@ | |
| 7239 | ** CAPI3REF: Determine the transaction state of a database |
| 7240 | ** METHOD: sqlite3 |
| 7241 | ** |
| 7242 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 7243 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 7244 | ** then the highest transaction state of any schema on database connection D |
| 7245 | ** is returned. Transaction states are (in order of lowest to highest): |
| 7246 | ** <ol> |
| 7247 | ** <li value="0"> SQLITE_TXN_NONE |
| 7248 | ** <li value="1"> SQLITE_TXN_READ |
| 7249 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -8785,11 +8786,10 @@ | |
| 8786 | */ |
| 8787 | #define SQLITE_TESTCTRL_FIRST 5 |
| 8788 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 8789 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 8790 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 8791 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 8792 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 8793 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 8794 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 8795 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -8810,11 +8810,12 @@ | |
| 8810 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 8811 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 8812 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 8813 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 8814 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 8815 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 8816 | #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ |
| 8817 | |
| 8818 | /* |
| 8819 | ** CAPI3REF: SQL Keyword Checking |
| 8820 | ** |
| 8821 | ** These routines provide access to the set of SQL language keywords |
| @@ -28182,16 +28183,19 @@ | |
| 28183 | EnableLookaside; |
| 28184 | } |
| 28185 | } |
| 28186 | |
| 28187 | /* |
| 28188 | ** Take actions at the end of an API call to deal with error codes. |
| 28189 | */ |
| 28190 | static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){ |
| 28191 | if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ |
| 28192 | sqlite3OomClear(db); |
| 28193 | sqlite3Error(db, SQLITE_NOMEM); |
| 28194 | return SQLITE_NOMEM_BKPT; |
| 28195 | } |
| 28196 | return rc & db->errMask; |
| 28197 | } |
| 28198 | |
| 28199 | /* |
| 28200 | ** This function must be called before exiting any API function (i.e. |
| 28201 | ** returning control to the user) that has called sqlite3_malloc or |
| @@ -28209,12 +28213,12 @@ | |
| 28213 | ** Otherwise the read (and possible write) of db->mallocFailed |
| 28214 | ** is unsafe, as is the call to sqlite3Error(). |
| 28215 | */ |
| 28216 | assert( db!=0 ); |
| 28217 | assert( sqlite3_mutex_held(db->mutex) ); |
| 28218 | if( db->mallocFailed || rc ){ |
| 28219 | return apiHandleError(db, rc); |
| 28220 | } |
| 28221 | return rc & db->errMask; |
| 28222 | } |
| 28223 | |
| 28224 | /************** End of malloc.c **********************************************/ |
| @@ -37003,11 +37007,28 @@ | |
| 37007 | |
| 37008 | got = seekAndRead(pFile, offset, pBuf, amt); |
| 37009 | if( got==amt ){ |
| 37010 | return SQLITE_OK; |
| 37011 | }else if( got<0 ){ |
| 37012 | /* pFile->lastErrno has been set by seekAndRead(). |
| 37013 | ** Usually we return SQLITE_IOERR_READ here, though for some |
| 37014 | ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The |
| 37015 | ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT |
| 37016 | ** prior to returning to the application by the sqlite3ApiExit() |
| 37017 | ** routine. |
| 37018 | */ |
| 37019 | switch( pFile->lastErrno ){ |
| 37020 | case ERANGE: |
| 37021 | case EIO: |
| 37022 | #ifdef ENXIO |
| 37023 | case ENXIO: |
| 37024 | #endif |
| 37025 | #ifdef EDEVERR |
| 37026 | case EDEVERR: |
| 37027 | #endif |
| 37028 | return SQLITE_IOERR_CORRUPTFS; |
| 37029 | } |
| 37030 | return SQLITE_IOERR_READ; |
| 37031 | }else{ |
| 37032 | storeLastErrno(pFile, 0); /* not a system error */ |
| 37033 | /* Unread parts of the buffer must be zero-filled */ |
| 37034 | memset(&((char*)pBuf)[got], 0, amt-got); |
| @@ -38535,11 +38556,11 @@ | |
| 38556 | if( bUnlock ){ |
| 38557 | rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
| 38558 | if( rc==SQLITE_OK ){ |
| 38559 | memset(&aLock[ofst], 0, sizeof(int)*n); |
| 38560 | } |
| 38561 | }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){ |
| 38562 | assert( n==1 && aLock[ofst]>1 ); |
| 38563 | aLock[ofst]--; |
| 38564 | } |
| 38565 | |
| 38566 | /* Undo the local locks */ |
| @@ -38568,11 +38589,11 @@ | |
| 38589 | /* Make sure no sibling connections hold locks that will block this |
| 38590 | ** lock. If any do, return SQLITE_BUSY right away. */ |
| 38591 | int ii; |
| 38592 | for(ii=ofst; ii<ofst+n; ii++){ |
| 38593 | assert( (p->sharedMask & mask)==0 ); |
| 38594 | if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){ |
| 38595 | rc = SQLITE_BUSY; |
| 38596 | break; |
| 38597 | } |
| 38598 | } |
| 38599 | |
| @@ -39964,19 +39985,39 @@ | |
| 39985 | } |
| 39986 | return SQLITE_OK; |
| 39987 | } |
| 39988 | |
| 39989 | /* |
| 39990 | ** If the last component of the pathname in z[0]..z[j-1] is something |
| 39991 | ** other than ".." then back it out and return true. If the last |
| 39992 | ** component is empty or if it is ".." then return false. |
| 39993 | */ |
| 39994 | static int unixBackupDir(const char *z, int *pJ){ |
| 39995 | int j = *pJ; |
| 39996 | int i; |
| 39997 | if( j<=0 ) return 0; |
| 39998 | for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){} |
| 39999 | if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; |
| 40000 | *pJ = i-1; |
| 40001 | return 1; |
| 40002 | } |
| 40003 | |
| 40004 | /* |
| 40005 | ** Convert a relative pathname into a full pathname. Also |
| 40006 | ** simplify the pathname as follows: |
| 40007 | ** |
| 40008 | ** Remove all instances of /./ |
| 40009 | ** Remove all isntances of /X/../ for any X |
| 40010 | */ |
| 40011 | static int mkFullPathname( |
| 40012 | const char *zPath, /* Input path */ |
| 40013 | char *zOut, /* Output buffer */ |
| 40014 | int nOut /* Allocated size of buffer zOut */ |
| 40015 | ){ |
| 40016 | int nPath = sqlite3Strlen30(zPath); |
| 40017 | int iOff = 0; |
| 40018 | int i, j; |
| 40019 | if( zPath[0]!='/' ){ |
| 40020 | if( osGetcwd(zOut, nOut-2)==0 ){ |
| 40021 | return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
| 40022 | } |
| 40023 | iOff = sqlite3Strlen30(zOut); |
| @@ -39987,10 +40028,45 @@ | |
| 40028 | ** even if it returns an error. */ |
| 40029 | zOut[iOff] = '\0'; |
| 40030 | return SQLITE_CANTOPEN_BKPT; |
| 40031 | } |
| 40032 | sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); |
| 40033 | |
| 40034 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 40035 | ** of a pathname is allowed since this is important on windows. */ |
| 40036 | for(i=j=1; zOut[i]; i++){ |
| 40037 | zOut[j++] = zOut[i]; |
| 40038 | while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; |
| 40039 | } |
| 40040 | zOut[j] = 0; |
| 40041 | |
| 40042 | assert( zOut[0]=='/' ); |
| 40043 | for(i=j=0; zOut[i]; i++){ |
| 40044 | if( zOut[i]=='/' ){ |
| 40045 | /* Skip over internal "/." directory components */ |
| 40046 | if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ |
| 40047 | i += 1; |
| 40048 | continue; |
| 40049 | } |
| 40050 | |
| 40051 | /* If this is a "/.." directory component then back out the |
| 40052 | ** previous term of the directory if it is something other than "..". |
| 40053 | */ |
| 40054 | if( zOut[i+1]=='.' |
| 40055 | && zOut[i+2]=='.' |
| 40056 | && zOut[i+3]=='/' |
| 40057 | && unixBackupDir(zOut, &j) |
| 40058 | ){ |
| 40059 | i += 2; |
| 40060 | continue; |
| 40061 | } |
| 40062 | } |
| 40063 | if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; |
| 40064 | j++; |
| 40065 | } |
| 40066 | if( NEVER(j==0) ) zOut[j++] = '/'; |
| 40067 | zOut[j] = 0; |
| 40068 | return SQLITE_OK; |
| 40069 | } |
| 40070 | |
| 40071 | /* |
| 40072 | ** Turn a relative pathname into a full pathname. The relative path |
| @@ -54326,10 +54402,11 @@ | |
| 54402 | sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */ |
| 54403 | char *zSuperJournal = 0; /* Contents of super-journal file */ |
| 54404 | i64 nSuperJournal; /* Size of super-journal file */ |
| 54405 | char *zJournal; /* Pointer to one journal within MJ file */ |
| 54406 | char *zSuperPtr; /* Space to hold super-journal filename */ |
| 54407 | char *zFree = 0; /* Free this buffer */ |
| 54408 | int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */ |
| 54409 | |
| 54410 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54411 | ** If successful, open the super-journal file for reading. |
| 54412 | */ |
| @@ -54350,15 +54427,17 @@ | |
| 54427 | ** files extracted from regular rollback-journals. |
| 54428 | */ |
| 54429 | rc = sqlite3OsFileSize(pSuper, &nSuperJournal); |
| 54430 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54431 | nSuperPtr = pVfs->mxPathname+1; |
| 54432 | zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); |
| 54433 | if( !zFree ){ |
| 54434 | rc = SQLITE_NOMEM_BKPT; |
| 54435 | goto delsuper_out; |
| 54436 | } |
| 54437 | zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; |
| 54438 | zSuperJournal = &zFree[4]; |
| 54439 | zSuperPtr = &zSuperJournal[nSuperJournal+2]; |
| 54440 | rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); |
| 54441 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54442 | zSuperJournal[nSuperJournal] = 0; |
| 54443 | zSuperJournal[nSuperJournal+1] = 0; |
| @@ -54402,11 +54481,11 @@ | |
| 54481 | |
| 54482 | sqlite3OsClose(pSuper); |
| 54483 | rc = sqlite3OsDelete(pVfs, zSuper, 0); |
| 54484 | |
| 54485 | delsuper_out: |
| 54486 | sqlite3_free(zFree); |
| 54487 | if( pSuper ){ |
| 54488 | sqlite3OsClose(pSuper); |
| 54489 | assert( !isOpen(pJournal) ); |
| 54490 | sqlite3_free(pSuper); |
| 54491 | } |
| @@ -54740,11 +54819,15 @@ | |
| 54819 | ** in case this has happened, clear the changeCountDone flag now. |
| 54820 | */ |
| 54821 | pPager->changeCountDone = pPager->tempFile; |
| 54822 | |
| 54823 | if( rc==SQLITE_OK ){ |
| 54824 | /* Leave 4 bytes of space before the super-journal filename in memory. |
| 54825 | ** This is because it may end up being passed to sqlite3OsOpen(), in |
| 54826 | ** which case it requires 4 0x00 bytes in memory immediately before |
| 54827 | ** the filename. */ |
| 54828 | zSuper = &pPager->pTmpSpace[4]; |
| 54829 | rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1); |
| 54830 | testcase( rc!=SQLITE_OK ); |
| 54831 | } |
| 54832 | if( rc==SQLITE_OK |
| 54833 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| @@ -54757,10 +54840,12 @@ | |
| 54840 | } |
| 54841 | if( rc==SQLITE_OK && zSuper[0] && res ){ |
| 54842 | /* If there was a super-journal and this routine will return success, |
| 54843 | ** see if it is possible to delete the super-journal. |
| 54844 | */ |
| 54845 | assert( zSuper==&pPager->pTmpSpace[4] ); |
| 54846 | memset(&zSuper[-4], 0, 4); |
| 54847 | rc = pager_delsuper(pPager, zSuper); |
| 54848 | testcase( rc!=SQLITE_OK ); |
| 54849 | } |
| 54850 | if( isHot && nPlayback ){ |
| 54851 | sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", |
| @@ -64746,11 +64831,11 @@ | |
| 64831 | #define hasReadConflicts(a, b) 0 |
| 64832 | #endif |
| 64833 | |
| 64834 | #ifdef SQLITE_DEBUG |
| 64835 | /* |
| 64836 | ** Return and reset the seek counter for a Btree object. |
| 64837 | */ |
| 64838 | SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ |
| 64839 | u64 n = pBt->nSeek; |
| 64840 | pBt->nSeek = 0; |
| 64841 | return n; |
| @@ -82192,13 +82277,16 @@ | |
| 82277 | ** equal to, or greater than the second (double). |
| 82278 | */ |
| 82279 | static int sqlite3IntFloatCompare(i64 i, double r){ |
| 82280 | if( sizeof(LONGDOUBLE_TYPE)>8 ){ |
| 82281 | LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; |
| 82282 | testcase( x<r ); |
| 82283 | testcase( x>r ); |
| 82284 | testcase( x==r ); |
| 82285 | if( x<r ) return -1; |
| 82286 | if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ |
| 82287 | return 0; /*NO_TEST*/ /* work around bugs in gcov */ |
| 82288 | }else{ |
| 82289 | i64 y; |
| 82290 | double s; |
| 82291 | if( r<-9223372036854775808.0 ) return +1; |
| 82292 | if( r>=9223372036854775808.0 ) return -1; |
| @@ -89388,11 +89476,11 @@ | |
| 89476 | assert( rc==SQLITE_OK ); |
| 89477 | break; |
| 89478 | } |
| 89479 | |
| 89480 | |
| 89481 | /* Opcode: OpenEphemeral P1 P2 P3 P4 P5 |
| 89482 | ** Synopsis: nColumn=P2 |
| 89483 | ** |
| 89484 | ** Open a new cursor P1 to a transient table. |
| 89485 | ** The cursor is always opened read/write even if |
| 89486 | ** the main database is read-only. The ephemeral |
| @@ -89408,10 +89496,14 @@ | |
| 89496 | ** |
| 89497 | ** The P5 parameter can be a mask of the BTREE_* flags defined |
| 89498 | ** in btree.h. These flags control aspects of the operation of |
| 89499 | ** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are |
| 89500 | ** added automatically. |
| 89501 | ** |
| 89502 | ** If P3 is positive, then reg[P3] is modified slightly so that it |
| 89503 | ** can be used as zero-length data for OP_Insert. This is an optimization |
| 89504 | ** that avoids an extra OP_Blob opcode to initialize that register. |
| 89505 | */ |
| 89506 | /* Opcode: OpenAutoindex P1 P2 * P4 * |
| 89507 | ** Synopsis: nColumn=P2 |
| 89508 | ** |
| 89509 | ** This opcode works the same as OP_OpenEphemeral. It has a |
| @@ -89430,10 +89522,19 @@ | |
| 89522 | SQLITE_OPEN_EXCLUSIVE | |
| 89523 | SQLITE_OPEN_DELETEONCLOSE | |
| 89524 | SQLITE_OPEN_TRANSIENT_DB; |
| 89525 | assert( pOp->p1>=0 ); |
| 89526 | assert( pOp->p2>=0 ); |
| 89527 | if( pOp->p3>0 ){ |
| 89528 | /* Make register reg[P3] into a value that can be used as the data |
| 89529 | ** form sqlite3BtreeInsert() where the length of the data is zero. */ |
| 89530 | assert( pOp->p2==0 ); /* Only used when number of columns is zero */ |
| 89531 | assert( pOp->opcode==OP_OpenEphemeral ); |
| 89532 | assert( aMem[pOp->p3].flags & MEM_Null ); |
| 89533 | aMem[pOp->p3].n = 0; |
| 89534 | aMem[pOp->p3].z = ""; |
| 89535 | } |
| 89536 | pCx = p->apCsr[pOp->p1]; |
| 89537 | if( pCx && pCx->pBtx ){ |
| 89538 | /* If the ephermeral table is already open, erase all existing content |
| 89539 | ** so that the table is empty again, rather than creating a new table. */ |
| 89540 | assert( pCx->isEphemeral ); |
| @@ -90589,11 +90690,11 @@ | |
| 90690 | if( pOp->p5 & OPFLAG_ISNOOP ) break; |
| 90691 | #endif |
| 90692 | |
| 90693 | if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; |
| 90694 | if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey; |
| 90695 | assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 ); |
| 90696 | x.pData = pData->z; |
| 90697 | x.nData = pData->n; |
| 90698 | seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0); |
| 90699 | if( pData->flags & MEM_Zero ){ |
| 90700 | x.nZero = pData->u.nZero; |
| @@ -93644,11 +93745,15 @@ | |
| 93745 | |
| 93746 | /* If we reach this point, it means that execution is finished with |
| 93747 | ** an error of some kind. |
| 93748 | */ |
| 93749 | abort_due_to_error: |
| 93750 | if( db->mallocFailed ){ |
| 93751 | rc = SQLITE_NOMEM_BKPT; |
| 93752 | }else if( rc==SQLITE_IOERR_CORRUPTFS ){ |
| 93753 | rc = SQLITE_CORRUPT_BKPT; |
| 93754 | } |
| 93755 | assert( rc ); |
| 93756 | if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){ |
| 93757 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 93758 | } |
| 93759 | p->rc = rc; |
| @@ -99381,10 +99486,11 @@ | |
| 99486 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99487 | int iCol = -1; |
| 99488 | Expr *pE, *pDup; |
| 99489 | if( pItem->done ) continue; |
| 99490 | pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); |
| 99491 | if( NEVER(pE==0) ) continue; |
| 99492 | if( sqlite3ExprIsInteger(pE, &iCol) ){ |
| 99493 | if( iCol<=0 || iCol>pEList->nExpr ){ |
| 99494 | resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr); |
| 99495 | return 1; |
| 99496 | } |
| @@ -99560,10 +99666,11 @@ | |
| 99666 | nResult = pSelect->pEList->nExpr; |
| 99667 | pParse = pNC->pParse; |
| 99668 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 99669 | Expr *pE = pItem->pExpr; |
| 99670 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE); |
| 99671 | if( NEVER(pE2==0) ) continue; |
| 99672 | if( zType[0]!='G' ){ |
| 99673 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 99674 | if( iCol>0 ){ |
| 99675 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 99676 | ** a copy of the iCol-th result-set column. The subsequent call to |
| @@ -103679,10 +103786,11 @@ | |
| 103786 | ** register iReg. The caller must ensure that iReg already contains |
| 103787 | ** the correct value for the expression. |
| 103788 | */ |
| 103789 | static void exprToRegister(Expr *pExpr, int iReg){ |
| 103790 | Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 103791 | if( NEVER(p==0) ) return; |
| 103792 | p->op2 = p->op; |
| 103793 | p->op = TK_REGISTER; |
| 103794 | p->iTable = iReg; |
| 103795 | ExprClearProperty(p, EP_Skip); |
| 103796 | } |
| @@ -104666,10 +104774,11 @@ | |
| 104774 | */ |
| 104775 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ |
| 104776 | int r2; |
| 104777 | pExpr = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 104778 | if( ConstFactorOk(pParse) |
| 104779 | && ALWAYS(pExpr!=0) |
| 104780 | && pExpr->op!=TK_REGISTER |
| 104781 | && sqlite3ExprIsConstantNotJoin(pExpr) |
| 104782 | ){ |
| 104783 | *pReg = 0; |
| 104784 | r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); |
| @@ -119424,10 +119533,12 @@ | |
| 119533 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 119534 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 119535 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| 119536 | FUNCTION(substr, 2, 0, 0, substrFunc ), |
| 119537 | FUNCTION(substr, 3, 0, 0, substrFunc ), |
| 119538 | FUNCTION(substring, 2, 0, 0, substrFunc ), |
| 119539 | FUNCTION(substring, 3, 0, 0, substrFunc ), |
| 119540 | WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0), |
| 119541 | WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0), |
| 119542 | WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0), |
| 119543 | WAGGREGATE(count, 0,0,0, countStep, |
| 119544 | countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ), |
| @@ -124310,10 +124421,12 @@ | |
| 124421 | /* Version 3.32.0 and later */ |
| 124422 | char *(*create_filename)(const char*,const char*,const char*, |
| 124423 | int,const char**); |
| 124424 | void (*free_filename)(char*); |
| 124425 | sqlite3_file *(*database_file_object)(const char*); |
| 124426 | /* Version 3.34.0 and later */ |
| 124427 | int (*txn_state)(sqlite3*,const char*); |
| 124428 | }; |
| 124429 | |
| 124430 | /* |
| 124431 | ** This is the function signature used for all extension entry points. It |
| 124432 | ** is also defined in the file "loadext.c". |
| @@ -124614,10 +124727,12 @@ | |
| 124727 | #define sqlite3_filename_wal sqlite3_api->filename_wal |
| 124728 | /* Version 3.32.0 and later */ |
| 124729 | #define sqlite3_create_filename sqlite3_api->create_filename |
| 124730 | #define sqlite3_free_filename sqlite3_api->free_filename |
| 124731 | #define sqlite3_database_file_object sqlite3_api->database_file_object |
| 124732 | /* Version 3.34.0 and later */ |
| 124733 | #define sqlite3_txn_state sqlite3_api->txn_state |
| 124734 | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ |
| 124735 | |
| 124736 | #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| 124737 | /* This case when the file really is being compiled as a loadable |
| 124738 | ** extension */ |
| @@ -125096,10 +125211,12 @@ | |
| 125211 | sqlite3_filename_wal, |
| 125212 | /* Version 3.32.0 and later */ |
| 125213 | sqlite3_create_filename, |
| 125214 | sqlite3_free_filename, |
| 125215 | sqlite3_database_file_object, |
| 125216 | /* Version 3.34.0 and later */ |
| 125217 | sqlite3_txn_state, |
| 125218 | }; |
| 125219 | |
| 125220 | /* True if x is the directory separator character |
| 125221 | */ |
| 125222 | #if SQLITE_OS_WIN |
| @@ -131654,10 +131771,11 @@ | |
| 131771 | Column *aCol, *pCol; /* For looping over result columns */ |
| 131772 | int nCol; /* Number of columns in the result set */ |
| 131773 | char *zName; /* Column name */ |
| 131774 | int nName; /* Size of name in zName[] */ |
| 131775 | Hash ht; /* Hash table of column names */ |
| 131776 | Table *pTab; |
| 131777 | |
| 131778 | sqlite3HashInit(&ht); |
| 131779 | if( pEList ){ |
| 131780 | nCol = pEList->nExpr; |
| 131781 | aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); |
| @@ -131676,19 +131794,17 @@ | |
| 131794 | */ |
| 131795 | if( (zName = pEList->a[i].zEName)!=0 && pEList->a[i].eEName==ENAME_NAME ){ |
| 131796 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 131797 | }else{ |
| 131798 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr); |
| 131799 | while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ |
| 131800 | pColExpr = pColExpr->pRight; |
| 131801 | assert( pColExpr!=0 ); |
| 131802 | } |
| 131803 | if( pColExpr->op==TK_COLUMN && (pTab = pColExpr->y.pTab)!=0 ){ |
| 131804 | /* For columns use the column name name */ |
| 131805 | int iCol = pColExpr->iColumn; |
| 131806 | if( iCol<0 ) iCol = pTab->iPKey; |
| 131807 | zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; |
| 131808 | }else if( pColExpr->op==TK_ID ){ |
| 131809 | assert( !ExprHasProperty(pColExpr, EP_IntValue) ); |
| 131810 | zName = pColExpr->u.zToken; |
| @@ -136959,26 +137075,15 @@ | |
| 137075 | goto trigger_cleanup; |
| 137076 | } |
| 137077 | pTab = sqlite3SrcListLookup(pParse, pTableName); |
| 137078 | if( !pTab ){ |
| 137079 | /* The table does not exist. */ |
| 137080 | goto trigger_orphan_error; |
| 137081 | } |
| 137082 | if( IsVirtual(pTab) ){ |
| 137083 | sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); |
| 137084 | goto trigger_orphan_error; |
| 137085 | } |
| 137086 | |
| 137087 | /* Check that the trigger name is not reserved and that no trigger of the |
| 137088 | ** specified name exists */ |
| 137089 | zName = sqlite3NameFromToken(db, pName); |
| @@ -137012,16 +137117,16 @@ | |
| 137117 | ** of triggers. |
| 137118 | */ |
| 137119 | if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ |
| 137120 | sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", |
| 137121 | (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); |
| 137122 | goto trigger_orphan_error; |
| 137123 | } |
| 137124 | if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ |
| 137125 | sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" |
| 137126 | " trigger on table: %S", pTableName, 0); |
| 137127 | goto trigger_orphan_error; |
| 137128 | } |
| 137129 | |
| 137130 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 137131 | if( !IN_RENAME_OBJECT ){ |
| 137132 | int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| @@ -137077,10 +137182,27 @@ | |
| 137182 | if( !pParse->pNewTrigger ){ |
| 137183 | sqlite3DeleteTrigger(db, pTrigger); |
| 137184 | }else{ |
| 137185 | assert( pParse->pNewTrigger==pTrigger ); |
| 137186 | } |
| 137187 | return; |
| 137188 | |
| 137189 | trigger_orphan_error: |
| 137190 | if( db->init.iDb==1 ){ |
| 137191 | /* Ticket #3810. |
| 137192 | ** Normally, whenever a table is dropped, all associated triggers are |
| 137193 | ** dropped too. But if a TEMP trigger is created on a non-TEMP table |
| 137194 | ** and the table is dropped by a different database connection, the |
| 137195 | ** trigger is not visible to the database connection that does the |
| 137196 | ** drop so the trigger cannot be dropped. This results in an |
| 137197 | ** "orphaned trigger" - a trigger whose associated table is missing. |
| 137198 | ** |
| 137199 | ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df |
| 137200 | */ |
| 137201 | db->init.orphanTrigger = 1; |
| 137202 | } |
| 137203 | goto trigger_cleanup; |
| 137204 | } |
| 137205 | |
| 137206 | /* |
| 137207 | ** This routine is called after all of the trigger actions have been parsed |
| 137208 | ** in order to complete the process of building the trigger. |
| @@ -138664,10 +138786,12 @@ | |
| 138786 | sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); |
| 138787 | } |
| 138788 | |
| 138789 | if( nChangeFrom==0 && HasRowid(pTab) ){ |
| 138790 | sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); |
| 138791 | iEph = pParse->nTab++; |
| 138792 | addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet); |
| 138793 | }else{ |
| 138794 | assert( pPk!=0 || HasRowid(pTab) ); |
| 138795 | nPk = pPk ? pPk->nKeyCol : 0; |
| 138796 | iPk = pParse->nMem+1; |
| 138797 | pParse->nMem += nPk; |
| @@ -138755,13 +138879,14 @@ | |
| 138879 | /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF |
| 138880 | ** mode, write the rowid into the FIFO. In either of the one-pass modes, |
| 138881 | ** leave it in register regOldRowid. */ |
| 138882 | sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid); |
| 138883 | if( eOnePass==ONEPASS_OFF ){ |
| 138884 | aRegIdx[nAllIdx] = ++pParse->nMem; |
| 138885 | sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid); |
| 138886 | }else{ |
| 138887 | if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen); |
| 138888 | } |
| 138889 | }else{ |
| 138890 | /* Read the PK of the current row into an array of registers. In |
| 138891 | ** ONEPASS_OFF mode, serialize the array into a record and store it in |
| 138892 | ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change |
| @@ -138845,12 +138970,13 @@ | |
| 138970 | sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); |
| 138971 | sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0); |
| 138972 | VdbeCoverage(v); |
| 138973 | } |
| 138974 | }else{ |
| 138975 | sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); |
| 138976 | labelContinue = sqlite3VdbeMakeLabel(pParse); |
| 138977 | addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid); |
| 138978 | VdbeCoverage(v); |
| 138979 | sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); |
| 138980 | VdbeCoverage(v); |
| 138981 | } |
| 138982 | } |
| @@ -139096,15 +139222,13 @@ | |
| 139222 | if( eOnePass==ONEPASS_SINGLE ){ |
| 139223 | /* Nothing to do at end-of-loop for a single-pass */ |
| 139224 | }else if( eOnePass==ONEPASS_MULTI ){ |
| 139225 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139226 | sqlite3WhereEnd(pWInfo); |
| 139227 | }else{ |
| 139228 | sqlite3VdbeResolveLabel(v, labelContinue); |
| 139229 | sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); |
| 139230 | } |
| 139231 | sqlite3VdbeResolveLabel(v, labelBreak); |
| 139232 | |
| 139233 | /* Update the sqlite_sequence table by storing the content of the |
| 139234 | ** maximum rowid counter values recorded while inserting into |
| @@ -145894,10 +146018,11 @@ | |
| 146018 | ** all terms of the WHERE clause. |
| 146019 | */ |
| 146020 | SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ |
| 146021 | Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr); |
| 146022 | pWC->op = op; |
| 146023 | assert( pE2!=0 || pExpr==0 ); |
| 146024 | if( pE2==0 ) return; |
| 146025 | if( pE2->op!=op ){ |
| 146026 | whereClauseInsert(pWC, pExpr, 0); |
| 146027 | }else{ |
| 146028 | sqlite3WhereSplit(pWC, pE2->pLeft, op); |
| @@ -146292,10 +146417,20 @@ | |
| 146417 | */ |
| 146418 | static void createMask(WhereMaskSet *pMaskSet, int iCursor){ |
| 146419 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 146420 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 146421 | } |
| 146422 | |
| 146423 | /* |
| 146424 | ** If the right-hand branch of the expression is a TK_COLUMN, then return |
| 146425 | ** a pointer to the right-hand branch. Otherwise, return NULL. |
| 146426 | */ |
| 146427 | static Expr *whereRightSubexprIsColumn(Expr *p){ |
| 146428 | p = sqlite3ExprSkipCollateAndLikely(p->pRight); |
| 146429 | if( ALWAYS(p!=0) && p->op==TK_COLUMN ) return p; |
| 146430 | return 0; |
| 146431 | } |
| 146432 | |
| 146433 | /* |
| 146434 | ** Advance to the next WhereTerm that matches according to the criteria |
| 146435 | ** established when the pScan object was initialized by whereScanInit(). |
| 146436 | ** Return NULL if there are no more matching WhereTerms. |
| @@ -146323,12 +146458,11 @@ | |
| 146458 | pScan->pIdxExpr,iCur)==0) |
| 146459 | && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) |
| 146460 | ){ |
| 146461 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 146462 | && pScan->nEquiv<ArraySize(pScan->aiCur) |
| 146463 | && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 |
| 146464 | ){ |
| 146465 | int j; |
| 146466 | for(j=0; j<pScan->nEquiv; j++){ |
| 146467 | if( pScan->aiCur[j]==pX->iTable |
| 146468 | && pScan->aiColumn[j]==pX->iColumn ){ |
| @@ -146520,11 +146654,12 @@ | |
| 146654 | int i; |
| 146655 | const char *zColl = pIdx->azColl[iCol]; |
| 146656 | |
| 146657 | for(i=0; i<pList->nExpr; i++){ |
| 146658 | Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr); |
| 146659 | if( ALWAYS(p!=0) |
| 146660 | && p->op==TK_COLUMN |
| 146661 | && p->iColumn==pIdx->aiColumn[iCol] |
| 146662 | && p->iTable==iBase |
| 146663 | ){ |
| 146664 | CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); |
| 146665 | if( 0==sqlite3StrICmp(pColl->zName, zColl) ){ |
| @@ -146584,10 +146719,11 @@ | |
| 146719 | ** true. Note: The (p->iTable==iBase) part of this test may be false if the |
| 146720 | ** current SELECT is a correlated sub-query. |
| 146721 | */ |
| 146722 | for(i=0; i<pDistinct->nExpr; i++){ |
| 146723 | Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
| 146724 | if( NEVER(p==0) ) continue; |
| 146725 | if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1; |
| 146726 | } |
| 146727 | |
| 146728 | /* Loop through all indices on the table, checking each to see if it makes |
| 146729 | ** the DISTINCT qualifier redundant. It does so if: |
| @@ -148498,13 +148634,13 @@ | |
| 148634 | LogEst rLogSize; /* Logarithm of table size */ |
| 148635 | WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ |
| 148636 | |
| 148637 | pNew = pBuilder->pNew; |
| 148638 | if( db->mallocFailed ) return SQLITE_NOMEM_BKPT; |
| 148639 | WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n", |
| 148640 | pProbe->pTable->zName,pProbe->zName, |
| 148641 | pNew->u.btree.nEq, pNew->nSkip, pNew->rRun)); |
| 148642 | |
| 148643 | assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); |
| 148644 | assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); |
| 148645 | if( pNew->wsFlags & WHERE_BTM_LIMIT ){ |
| 148646 | opMask = WO_LT|WO_LE; |
| @@ -148869,10 +149005,11 @@ | |
| 149005 | |
| 149006 | if( pIndex->bUnordered ) return 0; |
| 149007 | if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; |
| 149008 | for(ii=0; ii<pOB->nExpr; ii++){ |
| 149009 | Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); |
| 149010 | if( NEVER(pExpr==0) ) continue; |
| 149011 | if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ |
| 149012 | if( pExpr->iColumn<0 ) return 1; |
| 149013 | for(jj=0; jj<pIndex->nKeyCol; jj++){ |
| 149014 | if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; |
| 149015 | } |
| @@ -149847,10 +149984,11 @@ | |
| 149984 | ** loops. |
| 149985 | */ |
| 149986 | for(i=0; i<nOrderBy; i++){ |
| 149987 | if( MASKBIT(i) & obSat ) continue; |
| 149988 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 149989 | if( NEVER(pOBExpr==0) ) continue; |
| 149990 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 149991 | if( pOBExpr->iTable!=iCur ) continue; |
| 149992 | pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, |
| 149993 | ~ready, eqOpMask, 0); |
| 149994 | if( pTerm==0 ) continue; |
| @@ -149973,10 +150111,11 @@ | |
| 150111 | for(i=0; bOnce && i<nOrderBy; i++){ |
| 150112 | if( MASKBIT(i) & obSat ) continue; |
| 150113 | pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr); |
| 150114 | testcase( wctrlFlags & WHERE_GROUPBY ); |
| 150115 | testcase( wctrlFlags & WHERE_DISTINCTBY ); |
| 150116 | if( NEVER(pOBExpr==0) ) continue; |
| 150117 | if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; |
| 150118 | if( iColumn>=XN_ROWID ){ |
| 150119 | if( pOBExpr->op!=TK_COLUMN ) continue; |
| 150120 | if( pOBExpr->iTable!=iCur ) continue; |
| 150121 | if( pOBExpr->iColumn!=iColumn ) continue; |
| @@ -150136,11 +150275,11 @@ | |
| 150275 | rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; |
| 150276 | rSortCost = nRow + rScale + 16; |
| 150277 | |
| 150278 | /* Multiple by log(M) where M is the number of output rows. |
| 150279 | ** Use the LIMIT for M if it is smaller. Or if this sort is for |
| 150280 | ** a DISTINCT operator, M will be the number of distinct output |
| 150281 | ** rows, so fudge it downwards a bit. |
| 150282 | */ |
| 150283 | if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){ |
| 150284 | nRow = pWInfo->iLimit; |
| 150285 | }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){ |
| @@ -194220,11 +194359,11 @@ | |
| 194359 | p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; |
| 194360 | p->nEvent = p->nSegment = 0; |
| 194361 | geopolyAddSegments(p, p1, 1); |
| 194362 | geopolyAddSegments(p, p2, 2); |
| 194363 | pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent); |
| 194364 | rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0; |
| 194365 | memset(aOverlap, 0, sizeof(aOverlap)); |
| 194366 | while( pThisEvent ){ |
| 194367 | if( pThisEvent->x!=rX ){ |
| 194368 | GeoSegment *pPrev = 0; |
| 194369 | int iMask = 0; |
| @@ -212129,11 +212268,11 @@ | |
| 212268 | Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ |
| 212269 | ){ |
| 212270 | int rc = SQLITE_OK; /* Return code */ |
| 212271 | Fts5Bm25Data *p; /* Object to return */ |
| 212272 | |
| 212273 | p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0); |
| 212274 | if( p==0 ){ |
| 212275 | int nPhrase; /* Number of phrases in query */ |
| 212276 | sqlite3_int64 nRow = 0; /* Number of rows in table */ |
| 212277 | sqlite3_int64 nToken = 0; /* Number of tokens in table */ |
| 212278 | sqlite3_int64 nByte; /* Bytes of space to allocate */ |
| @@ -212203,11 +212342,11 @@ | |
| 212342 | int nVal, /* Number of values in apVal[] array */ |
| 212343 | sqlite3_value **apVal /* Array of trailing arguments */ |
| 212344 | ){ |
| 212345 | const double k1 = 1.2; /* Constant "k1" from BM25 formula */ |
| 212346 | const double b = 0.75; /* Constant "b" from BM25 formula */ |
| 212347 | int rc; /* Error code */ |
| 212348 | double score = 0.0; /* SQL function return value */ |
| 212349 | Fts5Bm25Data *pData; /* Values allocated/calculated once only */ |
| 212350 | int i; /* Iterator variable */ |
| 212351 | int nInst = 0; /* Value returned by xInstCount() */ |
| 212352 | double D = 0.0; /* Total number of tokens in row */ |
| @@ -212235,21 +212374,19 @@ | |
| 212374 | int nTok; |
| 212375 | rc = pApi->xColumnSize(pFts, -1, &nTok); |
| 212376 | D = (double)nTok; |
| 212377 | } |
| 212378 | |
| 212379 | /* Determine and return the BM25 score for the current row. Or, if an |
| 212380 | ** error has occurred, throw an exception. */ |
| 212381 | if( rc==SQLITE_OK ){ |
| 212382 | for(i=0; i<pData->nPhrase; i++){ |
| 212383 | score += pData->aIDF[i] * ( |
| 212384 | ( aFreq[i] * (k1 + 1.0) ) / |
| 212385 | ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) |
| 212386 | ); |
| 212387 | } |
| 212388 | sqlite3_result_double(pCtx, -1.0 * score); |
| 212389 | }else{ |
| 212390 | sqlite3_result_error_code(pCtx, rc); |
| 212391 | } |
| 212392 | } |
| @@ -223384,10 +223521,11 @@ | |
| 223521 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 223522 | } |
| 223523 | }else{ |
| 223524 | poslist.n = 0; |
| 223525 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 223526 | fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0"); |
| 223527 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 223528 | int iCol = FTS5_POS2COLUMN(iPos); |
| 223529 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 223530 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 223531 | } |
| @@ -226679,11 +226817,11 @@ | |
| 226817 | int nArg, /* Number of args */ |
| 226818 | sqlite3_value **apUnused /* Function arguments */ |
| 226819 | ){ |
| 226820 | assert( nArg==0 ); |
| 226821 | UNUSED_PARAM2(nArg, apUnused); |
| 226822 | sqlite3_result_text(pCtx, "fts5: 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -1, SQLITE_TRANSIENT); |
| 226823 | } |
| 226824 | |
| 226825 | /* |
| 226826 | ** Return true if zName is the extension on one of the shadow tables used |
| 226827 | ** by this module. |
| @@ -229252,17 +229390,18 @@ | |
| 229390 | |
| 229391 | /* |
| 229392 | ** Allocate a trigram tokenizer. |
| 229393 | */ |
| 229394 | static int fts5TriCreate( |
| 229395 | void *pUnused, |
| 229396 | const char **azArg, |
| 229397 | int nArg, |
| 229398 | Fts5Tokenizer **ppOut |
| 229399 | ){ |
| 229400 | int rc = SQLITE_OK; |
| 229401 | TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); |
| 229402 | UNUSED_PARAM(pUnused); |
| 229403 | if( pNew==0 ){ |
| 229404 | rc = SQLITE_NOMEM; |
| 229405 | }else{ |
| 229406 | int i; |
| 229407 | pNew->bFold = 1; |
| @@ -229291,11 +229430,11 @@ | |
| 229430 | ** Trigram tokenizer tokenize routine. |
| 229431 | */ |
| 229432 | static int fts5TriTokenize( |
| 229433 | Fts5Tokenizer *pTok, |
| 229434 | void *pCtx, |
| 229435 | int unusedFlags, |
| 229436 | const char *pText, int nText, |
| 229437 | int (*xToken)(void*, int, const char*, int, int, int) |
| 229438 | ){ |
| 229439 | TrigramTokenizer *p = (TrigramTokenizer*)pTok; |
| 229440 | int rc = SQLITE_OK; |
| @@ -229302,10 +229441,11 @@ | |
| 229441 | char aBuf[32]; |
| 229442 | const unsigned char *zIn = (const unsigned char*)pText; |
| 229443 | const unsigned char *zEof = &zIn[nText]; |
| 229444 | u32 iCode; |
| 229445 | |
| 229446 | UNUSED_PARAM(unusedFlags); |
| 229447 | while( 1 ){ |
| 229448 | char *zOut = aBuf; |
| 229449 | int iStart = zIn - (const unsigned char*)pText; |
| 229450 | const unsigned char *zNext; |
| 229451 | |
| @@ -230164,10 +230304,11 @@ | |
| 230304 | } |
| 230305 | iTbl++; |
| 230306 | } |
| 230307 | aAscii[0] = 0; /* 0x00 is never a token character */ |
| 230308 | } |
| 230309 | |
| 230310 | |
| 230311 | /* |
| 230312 | ** 2015 May 30 |
| 230313 | ** |
| 230314 | ** The author disclaims copyright to this source code. In place of |
| @@ -231602,12 +231743,12 @@ | |
| 231743 | } |
| 231744 | #endif /* SQLITE_CORE */ |
| 231745 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 231746 | |
| 231747 | /************** End of stmt.c ************************************************/ |
| 231748 | #if __LINE__!=231748 |
| 231749 | #undef SQLITE_SOURCE_ID |
| 231750 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2" |
| 231751 | #endif |
| 231752 | /* Return the source-id for this library */ |
| 231753 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 231754 | /************************** End of sqlite3.c ******************************/ |
| 231755 |
+5
-4
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -123,11 +123,11 @@ | ||
| 123 | 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | 125 | */ |
| 126 | 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | -#define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" | |
| 128 | +#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| @@ -502,10 +502,11 @@ | ||
| 502 | 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) | |
| 507 | 508 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 508 | 509 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 509 | 510 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 510 | 511 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | 512 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | ||
| 6190 | 6191 | ** CAPI3REF: Determine the transaction state of a database |
| 6191 | 6192 | ** METHOD: sqlite3 |
| 6192 | 6193 | ** |
| 6193 | 6194 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6194 | 6195 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6195 | -** then the highest transaction state of any schema on databse connection D | |
| 6196 | +** then the highest transaction state of any schema on database connection D | |
| 6196 | 6197 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6197 | 6198 | ** <ol> |
| 6198 | 6199 | ** <li value="0"> SQLITE_TXN_NONE |
| 6199 | 6200 | ** <li value="1"> SQLITE_TXN_READ |
| 6200 | 6201 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | ||
| 7737 | 7738 | */ |
| 7738 | 7739 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7739 | 7740 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7740 | 7741 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7741 | 7742 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7742 | -#define SQLITE_TESTCTRL_SEEK_COUNT 7 | |
| 7743 | 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | ||
| 7762 | 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | -#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ | |
| 7767 | +#define SQLITE_TESTCTRL_SEEK_COUNT 30 | |
| 7768 | +#define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ | |
| 7768 | 7769 | |
| 7769 | 7770 | /* |
| 7770 | 7771 | ** CAPI3REF: SQL Keyword Checking |
| 7771 | 7772 | ** |
| 7772 | 7773 | ** These routines provide access to the set of SQL language keywords |
| 7773 | 7774 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | #define SQLITE_SOURCE_ID "2020-10-31 18:58:37 7d01e84dc49074e6364267eea9fd20d46a457d2498121a0f218fbf482692392d" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -502,10 +502,11 @@ | |
| 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 508 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 509 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 510 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 511 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | |
| 6190 | ** CAPI3REF: Determine the transaction state of a database |
| 6191 | ** METHOD: sqlite3 |
| 6192 | ** |
| 6193 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6194 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6195 | ** then the highest transaction state of any schema on databse connection D |
| 6196 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6197 | ** <ol> |
| 6198 | ** <li value="0"> SQLITE_TXN_NONE |
| 6199 | ** <li value="1"> SQLITE_TXN_READ |
| 6200 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | |
| 7737 | */ |
| 7738 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7739 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7740 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7741 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7742 | #define SQLITE_TESTCTRL_SEEK_COUNT 7 |
| 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | |
| 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | #define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */ |
| 7768 | |
| 7769 | /* |
| 7770 | ** CAPI3REF: SQL Keyword Checking |
| 7771 | ** |
| 7772 | ** These routines provide access to the set of SQL language keywords |
| 7773 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.34.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3034000 |
| 128 | #define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| @@ -502,10 +502,11 @@ | |
| 502 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) |
| 503 | #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) |
| 504 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 505 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 506 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 507 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 508 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 509 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 510 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 511 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 512 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -6190,11 +6191,11 @@ | |
| 6191 | ** CAPI3REF: Determine the transaction state of a database |
| 6192 | ** METHOD: sqlite3 |
| 6193 | ** |
| 6194 | ** ^The sqlite3_txn_state(D,S) interface returns the current |
| 6195 | ** [transaction state] of schema S in database connection D. ^If S is NULL, |
| 6196 | ** then the highest transaction state of any schema on database connection D |
| 6197 | ** is returned. Transaction states are (in order of lowest to highest): |
| 6198 | ** <ol> |
| 6199 | ** <li value="0"> SQLITE_TXN_NONE |
| 6200 | ** <li value="1"> SQLITE_TXN_READ |
| 6201 | ** <li value="2"> SQLITE_TXN_WRITE |
| @@ -7737,11 +7738,10 @@ | |
| 7738 | */ |
| 7739 | #define SQLITE_TESTCTRL_FIRST 5 |
| 7740 | #define SQLITE_TESTCTRL_PRNG_SAVE 5 |
| 7741 | #define SQLITE_TESTCTRL_PRNG_RESTORE 6 |
| 7742 | #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ |
| 7743 | #define SQLITE_TESTCTRL_BITVEC_TEST 8 |
| 7744 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 |
| 7745 | #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 |
| 7746 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 |
| 7747 | #define SQLITE_TESTCTRL_ASSERT 12 |
| @@ -7762,11 +7762,12 @@ | |
| 7762 | #define SQLITE_TESTCTRL_IMPOSTER 25 |
| 7763 | #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 |
| 7764 | #define SQLITE_TESTCTRL_RESULT_INTREAL 27 |
| 7765 | #define SQLITE_TESTCTRL_PRNG_SEED 28 |
| 7766 | #define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29 |
| 7767 | #define SQLITE_TESTCTRL_SEEK_COUNT 30 |
| 7768 | #define SQLITE_TESTCTRL_LAST 30 /* Largest TESTCTRL */ |
| 7769 | |
| 7770 | /* |
| 7771 | ** CAPI3REF: SQL Keyword Checking |
| 7772 | ** |
| 7773 | ** These routines provide access to the set of SQL language keywords |
| 7774 |
+23
-2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1204,14 +1204,16 @@ | ||
| 1204 | 1204 | @ <pre> |
| 1205 | 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | 1206 | @ </pre> |
| 1207 | 1207 | } |
| 1208 | 1208 | } |
| 1209 | - style_finish_page("error"); | |
| 1210 | - if( zErr ){ | |
| 1209 | + if( zErr && zErr[0] ){ | |
| 1210 | + style_finish_page("error"); | |
| 1211 | 1211 | cgi_reply(); |
| 1212 | 1212 | fossil_exit(1); |
| 1213 | + }else{ | |
| 1214 | + style_finish_page("test"); | |
| 1213 | 1215 | } |
| 1214 | 1216 | } |
| 1215 | 1217 | |
| 1216 | 1218 | /* |
| 1217 | 1219 | ** Generate a Not Yet Implemented error page. |
| @@ -1226,10 +1228,29 @@ | ||
| 1226 | 1228 | void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ |
| 1227 | 1229 | fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1228 | 1230 | cgi_reset_content(); |
| 1229 | 1231 | webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1230 | 1232 | } |
| 1233 | + | |
| 1234 | +/* | |
| 1235 | +** Issue a 404 Not Found error for a webpage | |
| 1236 | +*/ | |
| 1237 | +void webpage_notfound_error(const char *zFormat, ...){ | |
| 1238 | + char *zMsg; | |
| 1239 | + va_list ap; | |
| 1240 | + if( zFormat ){ | |
| 1241 | + va_start(ap, zFormat); | |
| 1242 | + zMsg = vmprintf(zFormat, ap); | |
| 1243 | + va_end(ap); | |
| 1244 | + }else{ | |
| 1245 | + zMsg = "Not Found"; | |
| 1246 | + } | |
| 1247 | + style_header("Not Found"); | |
| 1248 | + @ <p>%h(zMsg)</p> | |
| 1249 | + cgi_set_status(404, "Not Found"); | |
| 1250 | + style_finish_page("enotfound"); | |
| 1251 | +} | |
| 1231 | 1252 | |
| 1232 | 1253 | #if INTERFACE |
| 1233 | 1254 | # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} |
| 1234 | 1255 | #endif |
| 1235 | 1256 | |
| 1236 | 1257 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1204,14 +1204,16 @@ | |
| 1204 | @ <pre> |
| 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | @ </pre> |
| 1207 | } |
| 1208 | } |
| 1209 | style_finish_page("error"); |
| 1210 | if( zErr ){ |
| 1211 | cgi_reply(); |
| 1212 | fossil_exit(1); |
| 1213 | } |
| 1214 | } |
| 1215 | |
| 1216 | /* |
| 1217 | ** Generate a Not Yet Implemented error page. |
| @@ -1226,10 +1228,29 @@ | |
| 1226 | void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ |
| 1227 | fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1228 | cgi_reset_content(); |
| 1229 | webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1230 | } |
| 1231 | |
| 1232 | #if INTERFACE |
| 1233 | # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} |
| 1234 | #endif |
| 1235 | |
| 1236 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1204,14 +1204,16 @@ | |
| 1204 | @ <pre> |
| 1205 | @ %h(blob_str(&g.httpHeader)) |
| 1206 | @ </pre> |
| 1207 | } |
| 1208 | } |
| 1209 | if( zErr && zErr[0] ){ |
| 1210 | style_finish_page("error"); |
| 1211 | cgi_reply(); |
| 1212 | fossil_exit(1); |
| 1213 | }else{ |
| 1214 | style_finish_page("test"); |
| 1215 | } |
| 1216 | } |
| 1217 | |
| 1218 | /* |
| 1219 | ** Generate a Not Yet Implemented error page. |
| @@ -1226,10 +1228,29 @@ | |
| 1228 | void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ |
| 1229 | fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1230 | cgi_reset_content(); |
| 1231 | webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
| 1232 | } |
| 1233 | |
| 1234 | /* |
| 1235 | ** Issue a 404 Not Found error for a webpage |
| 1236 | */ |
| 1237 | void webpage_notfound_error(const char *zFormat, ...){ |
| 1238 | char *zMsg; |
| 1239 | va_list ap; |
| 1240 | if( zFormat ){ |
| 1241 | va_start(ap, zFormat); |
| 1242 | zMsg = vmprintf(zFormat, ap); |
| 1243 | va_end(ap); |
| 1244 | }else{ |
| 1245 | zMsg = "Not Found"; |
| 1246 | } |
| 1247 | style_header("Not Found"); |
| 1248 | @ <p>%h(zMsg)</p> |
| 1249 | cgi_set_status(404, "Not Found"); |
| 1250 | style_finish_page("enotfound"); |
| 1251 | } |
| 1252 | |
| 1253 | #if INTERFACE |
| 1254 | # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} |
| 1255 | #endif |
| 1256 | |
| 1257 |
+144
-30
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -224,10 +224,26 @@ | ||
| 224 | 224 | */ |
| 225 | 225 | int timeline_tableid(void){ |
| 226 | 226 | static int id = 0; |
| 227 | 227 | return id++; |
| 228 | 228 | } |
| 229 | + | |
| 230 | +/* | |
| 231 | +** Return true if the checking identified by "rid" has a valid "closed" | |
| 232 | +** tag. | |
| 233 | +*/ | |
| 234 | +static int has_closed_tag(int rid){ | |
| 235 | + static Stmt q; | |
| 236 | + int res = 0; | |
| 237 | + db_static_prepare(&q, | |
| 238 | + "SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0", | |
| 239 | + TAG_CLOSED); | |
| 240 | + db_bind_int(&q, "$rid", rid); | |
| 241 | + res = db_step(&q)==SQLITE_ROW; | |
| 242 | + db_reset(&q); | |
| 243 | + return res; | |
| 244 | +} | |
| 229 | 245 | |
| 230 | 246 | /* |
| 231 | 247 | ** Output a timeline in the web format given a query. The query |
| 232 | 248 | ** should return these columns: |
| 233 | 249 | ** |
| @@ -554,13 +570,11 @@ | ||
| 554 | 570 | } |
| 555 | 571 | if( (tmFlags & TIMELINE_CLASSIC)!=0 ){ |
| 556 | 572 | if( zType[0]=='c' ){ |
| 557 | 573 | hyperlink_to_version(zUuid); |
| 558 | 574 | if( isLeaf ){ |
| 559 | - if( db_exists("SELECT 1 FROM tagxref" | |
| 560 | - " WHERE rid=%d AND tagid=%d AND tagtype>0", | |
| 561 | - rid, TAG_CLOSED) ){ | |
| 575 | + if( has_closed_tag(rid) ){ | |
| 562 | 576 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 563 | 577 | }else{ |
| 564 | 578 | @ <span class="timelineLeaf">Leaf:</span> |
| 565 | 579 | } |
| 566 | 580 | } |
| @@ -581,25 +595,32 @@ | ||
| 581 | 595 | if( zType[0]!='c' ){ |
| 582 | 596 | /* Comments for anything other than a check-in are generated by |
| 583 | 597 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 584 | 598 | if( zType[0]=='w' ){ |
| 585 | 599 | const char *zCom = blob_str(&comment); |
| 586 | - char *zWiki; | |
| 600 | + /* Except, the comments generated by "fossil rebuild" for a wiki | |
| 601 | + ** page edit consist of a single character '-', '+', or ':' (to | |
| 602 | + ** indicate "deleted", "added", or "edited") followed by the | |
| 603 | + ** raw wiki page name. We have to generate an appropriate | |
| 604 | + ** comment on-the-fly | |
| 605 | + */ | |
| 587 | 606 | wiki_hyperlink_override(zUuid); |
| 588 | - if( (tmFlags & TIMELINE_REFS)!=0 | |
| 589 | - && (zWiki = strstr(zCom,"wiki"))!=0 | |
| 590 | - ){ | |
| 591 | - /* The TIMELINE_REFS flag causes timeline comments of the | |
| 592 | - ** form "Changes to wiki..." or "Added wiki" to be changed | |
| 593 | - ** into just "Wiki..." */ | |
| 594 | - Blob rcom; | |
| 595 | - blob_init(&rcom, 0, 0); | |
| 596 | - blob_appendf(&rcom, "W%s", zWiki+1); | |
| 597 | - wiki_convert(&rcom, 0, WIKI_INLINE); | |
| 598 | - blob_reset(&rcom); | |
| 607 | + if( zCom[0]=='-' ){ | |
| 608 | + @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\ | |
| 609 | + @ %h(zCom+1)</a> | |
| 610 | + }else if( (tmFlags & TIMELINE_REFS)!=0 | |
| 611 | + && (zCom[0]=='+' || zCom[0]==':') ){ | |
| 612 | + @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a> | |
| 613 | + }else if( zCom[0]=='+' ){ | |
| 614 | + @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a> | |
| 615 | + }else if( zCom[0]==':' ){ | |
| 616 | + @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\ | |
| 617 | + @ %h(zCom+1)</a> | |
| 599 | 618 | }else{ |
| 600 | - wiki_convert(&comment, 0, WIKI_INLINE); | |
| 619 | + /* Legacy EVENT table entry that needs to be rebuilt */ | |
| 620 | + @ Changes to a wiki page → Obsolete EVENT table information. | |
| 621 | + @ Run "fossil rebuild" on the repository. | |
| 601 | 622 | } |
| 602 | 623 | wiki_hyperlink_override(0); |
| 603 | 624 | }else{ |
| 604 | 625 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 605 | 626 | } |
| @@ -655,13 +676,11 @@ | ||
| 655 | 676 | } |
| 656 | 677 | |
| 657 | 678 | if( (tmFlags & TIMELINE_CLASSIC)==0 ){ |
| 658 | 679 | if( zType[0]=='c' ){ |
| 659 | 680 | if( isLeaf ){ |
| 660 | - if( db_exists("SELECT 1 FROM tagxref" | |
| 661 | - " WHERE rid=%d AND tagid=%d AND tagtype>0", | |
| 662 | - rid, TAG_CLOSED) ){ | |
| 681 | + if( has_closed_tag(rid) ){ | |
| 663 | 682 | @ <span class='timelineLeaf'>Closed-Leaf</span> |
| 664 | 683 | }else{ |
| 665 | 684 | @ <span class='timelineLeaf'>Leaf</span> |
| 666 | 685 | } |
| 667 | 686 | } |
| @@ -1715,19 +1734,26 @@ | ||
| 1715 | 1734 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1716 | 1735 | int pd_rid; |
| 1717 | 1736 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1718 | 1737 | const char *z; |
| 1719 | 1738 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1739 | + char *zOlderButtonLabel = 0; /* Label for the Older Button */ | |
| 1720 | 1740 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1741 | + char *zNewerButtonLabel = 0; /* Label for the Newer button */ | |
| 1721 | 1742 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1722 | 1743 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1723 | 1744 | int disableY = 0; /* Disable type selector on submenu */ |
| 1724 | 1745 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1725 | 1746 | char *zPlural; /* Ending for plural forms */ |
| 1726 | 1747 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1748 | + int haveParameterN; /* True if n= query parameter present */ | |
| 1749 | + | |
| 1750 | + url_initialize(&url, "timeline"); | |
| 1751 | + cgi_query_parameters_to_url(&url); | |
| 1727 | 1752 | |
| 1728 | 1753 | /* Set number of rows to display */ |
| 1754 | + haveParameterN = P("n")!=0; | |
| 1729 | 1755 | cookie_read_parameter("n","n"); |
| 1730 | 1756 | z = P("n"); |
| 1731 | 1757 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1732 | 1758 | if( z ){ |
| 1733 | 1759 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1785,12 +1811,10 @@ | ||
| 1785 | 1811 | } |
| 1786 | 1812 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1787 | 1813 | cookie_write_parameter("y","y",zType); |
| 1788 | 1814 | } |
| 1789 | 1815 | cookie_render(); |
| 1790 | - url_initialize(&url, "timeline"); | |
| 1791 | - cgi_query_parameters_to_url(&url); | |
| 1792 | 1816 | |
| 1793 | 1817 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1794 | 1818 | if( P("cf")!=0 ){ |
| 1795 | 1819 | zCirca = db_text(0, |
| 1796 | 1820 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2052,11 +2076,10 @@ | ||
| 2052 | 2076 | " WHERE mlink.mid=x" |
| 2053 | 2077 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2054 | 2078 | glob_expr("filename.name", zChng) |
| 2055 | 2079 | ); |
| 2056 | 2080 | } |
| 2057 | -// tmFlags |= TIMELINE_DISJOINT; | |
| 2058 | 2081 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2059 | 2082 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2060 | 2083 | if( advancedMenu ){ |
| 2061 | 2084 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2062 | 2085 | } |
| @@ -2106,10 +2129,11 @@ | ||
| 2106 | 2129 | db_multi_exec("DELETE FROM ok"); |
| 2107 | 2130 | } |
| 2108 | 2131 | if( p_rid ){ |
| 2109 | 2132 | zBackTo = P("bt"); |
| 2110 | 2133 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2134 | + if( !haveParameterN ) nEntry = 0; | |
| 2111 | 2135 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2112 | 2136 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2113 | 2137 | if( np>0 || nd==0 ){ |
| 2114 | 2138 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2115 | 2139 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2204,27 +2228,57 @@ | ||
| 2204 | 2228 | } |
| 2205 | 2229 | if( bisectLocal || zBisect!=0 ){ |
| 2206 | 2230 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2207 | 2231 | } |
| 2208 | 2232 | if( zYearMonth ){ |
| 2233 | + char *zNext; | |
| 2209 | 2234 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2235 | + if( strlen(zYearMonth)>7 ){ | |
| 2236 | + zYearMonth = mprintf("%.7s", zYearMonth); | |
| 2237 | + } | |
| 2238 | + if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){ | |
| 2239 | + zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');"); | |
| 2240 | + } | |
| 2241 | + zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');", | |
| 2242 | + zYearMonth); | |
| 2243 | + if( db_int(0, | |
| 2244 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2245 | + " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)", | |
| 2246 | + zNext, blob_sql_text(&cond)) | |
| 2247 | + ){ | |
| 2248 | + zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); | |
| 2249 | + zNewerButtonLabel = "Following month"; | |
| 2250 | + } | |
| 2251 | + fossil_free(zNext); | |
| 2252 | + zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');", | |
| 2253 | + zYearMonth); | |
| 2254 | + if( db_int(0, | |
| 2255 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2256 | + " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)", | |
| 2257 | + zYearMonth, blob_sql_text(&cond)) | |
| 2258 | + ){ | |
| 2259 | + zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); | |
| 2260 | + zOlderButtonLabel = "Previous month"; | |
| 2261 | + } | |
| 2262 | + fossil_free(zNext); | |
| 2210 | 2263 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2211 | 2264 | zYearMonth); |
| 2265 | + nEntry = -1; | |
| 2212 | 2266 | } |
| 2213 | 2267 | else if( zYearWeek ){ |
| 2214 | - char *z; | |
| 2268 | + char *z, *zNext; | |
| 2215 | 2269 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2216 | 2270 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2217 | 2271 | if( z && z[0] ){ |
| 2218 | 2272 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2219 | 2273 | zYearWeek); |
| 2220 | 2274 | zYearWeek = z; |
| 2221 | 2275 | }else{ |
| 2222 | 2276 | if( strlen(zYearWeek)==7 ){ |
| 2223 | 2277 | zYearWeekStart = db_text(0, |
| 2224 | - "SELECT date('%.4q-01-01','+%d days','weekday 1')", | |
| 2225 | - zYearWeek, atoi(zYearWeek+5)*7); | |
| 2278 | + "SELECT date('%.4q-01-01','%+d days','weekday 1')", | |
| 2279 | + zYearWeek, atoi(zYearWeek+5)*7-6); | |
| 2226 | 2280 | }else{ |
| 2227 | 2281 | zYearWeekStart = 0; |
| 2228 | 2282 | } |
| 2229 | 2283 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2230 | 2284 | zYearWeekStart = db_text(0, |
| @@ -2231,20 +2285,61 @@ | ||
| 2231 | 2285 | "SELECT date('now','-6 days','weekday 1');"); |
| 2232 | 2286 | zYearWeek = db_text(0, |
| 2233 | 2287 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2234 | 2288 | } |
| 2235 | 2289 | } |
| 2290 | + zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart); | |
| 2291 | + if( db_int(0, | |
| 2292 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2293 | + " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", | |
| 2294 | + zNext, blob_sql_text(&cond)) | |
| 2295 | + ){ | |
| 2296 | + zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); | |
| 2297 | + zNewerButtonLabel = "Following week"; | |
| 2298 | + } | |
| 2299 | + fossil_free(zNext); | |
| 2300 | + zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart); | |
| 2301 | + if( db_int(0, | |
| 2302 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2303 | + " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", | |
| 2304 | + zYearWeekStart, blob_sql_text(&cond)) | |
| 2305 | + ){ | |
| 2306 | + zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); | |
| 2307 | + zOlderButtonLabel = "Previous week"; | |
| 2308 | + } | |
| 2309 | + fossil_free(zNext); | |
| 2236 | 2310 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2237 | 2311 | zYearWeek); |
| 2238 | 2312 | nEntry = -1; |
| 2239 | 2313 | } |
| 2240 | 2314 | else if( zDay ){ |
| 2315 | + char *zNext; | |
| 2241 | 2316 | zDay = timeline_expand_datetime(zDay); |
| 2242 | 2317 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2243 | 2318 | if( zDay==0 || zDay[0]==0 ){ |
| 2244 | 2319 | zDay = db_text(0, "SELECT date('now')"); |
| 2245 | 2320 | } |
| 2321 | + zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay); | |
| 2322 | + if( db_int(0, | |
| 2323 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2324 | + " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", | |
| 2325 | + zNext, blob_sql_text(&cond)) | |
| 2326 | + ){ | |
| 2327 | + zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); | |
| 2328 | + zNewerButtonLabel = "Following day"; | |
| 2329 | + } | |
| 2330 | + fossil_free(zNext); | |
| 2331 | + zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay); | |
| 2332 | + if( db_int(0, | |
| 2333 | + "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" | |
| 2334 | + " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", | |
| 2335 | + zDay, blob_sql_text(&cond)) | |
| 2336 | + ){ | |
| 2337 | + zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); | |
| 2338 | + zOlderButtonLabel = "Previous day"; | |
| 2339 | + } | |
| 2340 | + fossil_free(zNext); | |
| 2246 | 2341 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2247 | 2342 | zDay); |
| 2248 | 2343 | nEntry = -1; |
| 2249 | 2344 | } |
| 2250 | 2345 | else if( zNDays ){ |
| @@ -2440,11 +2535,12 @@ | ||
| 2440 | 2535 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2441 | 2536 | |
| 2442 | 2537 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2443 | 2538 | zPlural = n==1 ? "" : "s"; |
| 2444 | 2539 | if( zYearMonth ){ |
| 2445 | - blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth); | |
| 2540 | + blob_appendf(&desc, "%d %s%s for the month beginning %h-01", | |
| 2541 | + n, zEType, zPlural, zYearMonth); | |
| 2446 | 2542 | }else if( zYearWeek ){ |
| 2447 | 2543 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2448 | 2544 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2449 | 2545 | }else if( zDay ){ |
| 2450 | 2546 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2532,10 +2628,11 @@ | ||
| 2532 | 2628 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2533 | 2629 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2534 | 2630 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2535 | 2631 | ){ |
| 2536 | 2632 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2633 | + zOlderButtonLabel = "More"; | |
| 2537 | 2634 | } |
| 2538 | 2635 | free(zDate); |
| 2539 | 2636 | } |
| 2540 | 2637 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2541 | 2638 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2547,10 +2644,11 @@ | ||
| 2547 | 2644 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2548 | 2645 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2549 | 2646 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2550 | 2647 | ){ |
| 2551 | 2648 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2649 | + zNewerButtonLabel = "More"; | |
| 2552 | 2650 | } |
| 2553 | 2651 | free(zDate); |
| 2554 | 2652 | } |
| 2555 | 2653 | if( advancedMenu ){ |
| 2556 | 2654 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2616,17 +2714,19 @@ | ||
| 2616 | 2714 | if( zError ){ |
| 2617 | 2715 | @ <p class="generalError">%h(zError)</p> |
| 2618 | 2716 | } |
| 2619 | 2717 | |
| 2620 | 2718 | if( zNewerButton ){ |
| 2621 | - @ %z(chref("button","%z",zNewerButton))More ↑</a> | |
| 2719 | + @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ | |
| 2720 | + @ ↑</a> | |
| 2622 | 2721 | } |
| 2623 | 2722 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2624 | 2723 | selectedRid, secondaryRid, 0); |
| 2625 | 2724 | db_finalize(&q); |
| 2626 | 2725 | if( zOlderButton ){ |
| 2627 | - @ %z(chref("button","%z",zOlderButton))More ↓</a> | |
| 2726 | + @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ | |
| 2727 | + @ ↓</a> | |
| 2628 | 2728 | } |
| 2629 | 2729 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2630 | 2730 | style_finish_page("timeline"); |
| 2631 | 2731 | } |
| 2632 | 2732 | |
| @@ -2650,10 +2750,11 @@ | ||
| 2650 | 2750 | ** 3. Comment string and user |
| 2651 | 2751 | ** 4. Number of non-merge children |
| 2652 | 2752 | ** 5. Number of parents |
| 2653 | 2753 | ** 6. mtime |
| 2654 | 2754 | ** 7. branch |
| 2755 | +** 8. event-type: 'ci', 'w', 't', 'f', and so forth. | |
| 2655 | 2756 | */ |
| 2656 | 2757 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2657 | 2758 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2658 | 2759 | int nLine = 0; |
| 2659 | 2760 | int nEntry = 0; |
| @@ -2674,10 +2775,11 @@ | ||
| 2674 | 2775 | const char *zId = db_column_text(q, 1); |
| 2675 | 2776 | const char *zDate = db_column_text(q, 2); |
| 2676 | 2777 | const char *zCom = db_column_text(q, 3); |
| 2677 | 2778 | int nChild = db_column_int(q, 4); |
| 2678 | 2779 | int nParent = db_column_int(q, 5); |
| 2780 | + const char *zType = db_column_text(q, 8); | |
| 2679 | 2781 | char *zFree = 0; |
| 2680 | 2782 | int n = 0; |
| 2681 | 2783 | char zPrefix[80]; |
| 2682 | 2784 | |
| 2683 | 2785 | if( nAbsLimit!=0 ){ |
| @@ -2717,11 +2819,22 @@ | ||
| 2717 | 2819 | } |
| 2718 | 2820 | if( content_is_private(rid) ){ |
| 2719 | 2821 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2720 | 2822 | n += strlen(zPrefix+n); |
| 2721 | 2823 | } |
| 2722 | - zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); | |
| 2824 | + if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){ | |
| 2825 | + /* Special processing for Wiki comments */ | |
| 2826 | + if( zCom[0]=='+' ){ | |
| 2827 | + zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1); | |
| 2828 | + }else if( zCom[0]=='-' ){ | |
| 2829 | + zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1); | |
| 2830 | + }else{ | |
| 2831 | + zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1); | |
| 2832 | + } | |
| 2833 | + }else{ | |
| 2834 | + zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); | |
| 2835 | + } | |
| 2723 | 2836 | /* record another X lines */ |
| 2724 | 2837 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2725 | 2838 | fossil_free(zFree); |
| 2726 | 2839 | |
| 2727 | 2840 | if(verboseFlag){ |
| @@ -2787,11 +2900,12 @@ | ||
| 2787 | 2900 | @ || ')' as comment, |
| 2788 | 2901 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2789 | 2902 | @ AS primPlinkCount, |
| 2790 | 2903 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2791 | 2904 | @ event.mtime AS mtime, |
| 2792 | - @ tagxref.value AS branch | |
| 2905 | + @ tagxref.value AS branch, | |
| 2906 | + @ event.type | |
| 2793 | 2907 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2794 | 2908 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2795 | 2909 | @ AND tagxref.tagtype>0 |
| 2796 | 2910 | @ AND tagxref.rid=blob.rid |
| 2797 | 2911 | @ WHERE blob.rid=event.objid |
| 2798 | 2912 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -224,10 +224,26 @@ | |
| 224 | */ |
| 225 | int timeline_tableid(void){ |
| 226 | static int id = 0; |
| 227 | return id++; |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** Output a timeline in the web format given a query. The query |
| 232 | ** should return these columns: |
| 233 | ** |
| @@ -554,13 +570,11 @@ | |
| 554 | } |
| 555 | if( (tmFlags & TIMELINE_CLASSIC)!=0 ){ |
| 556 | if( zType[0]=='c' ){ |
| 557 | hyperlink_to_version(zUuid); |
| 558 | if( isLeaf ){ |
| 559 | if( db_exists("SELECT 1 FROM tagxref" |
| 560 | " WHERE rid=%d AND tagid=%d AND tagtype>0", |
| 561 | rid, TAG_CLOSED) ){ |
| 562 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 563 | }else{ |
| 564 | @ <span class="timelineLeaf">Leaf:</span> |
| 565 | } |
| 566 | } |
| @@ -581,25 +595,32 @@ | |
| 581 | if( zType[0]!='c' ){ |
| 582 | /* Comments for anything other than a check-in are generated by |
| 583 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 584 | if( zType[0]=='w' ){ |
| 585 | const char *zCom = blob_str(&comment); |
| 586 | char *zWiki; |
| 587 | wiki_hyperlink_override(zUuid); |
| 588 | if( (tmFlags & TIMELINE_REFS)!=0 |
| 589 | && (zWiki = strstr(zCom,"wiki"))!=0 |
| 590 | ){ |
| 591 | /* The TIMELINE_REFS flag causes timeline comments of the |
| 592 | ** form "Changes to wiki..." or "Added wiki" to be changed |
| 593 | ** into just "Wiki..." */ |
| 594 | Blob rcom; |
| 595 | blob_init(&rcom, 0, 0); |
| 596 | blob_appendf(&rcom, "W%s", zWiki+1); |
| 597 | wiki_convert(&rcom, 0, WIKI_INLINE); |
| 598 | blob_reset(&rcom); |
| 599 | }else{ |
| 600 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 601 | } |
| 602 | wiki_hyperlink_override(0); |
| 603 | }else{ |
| 604 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 605 | } |
| @@ -655,13 +676,11 @@ | |
| 655 | } |
| 656 | |
| 657 | if( (tmFlags & TIMELINE_CLASSIC)==0 ){ |
| 658 | if( zType[0]=='c' ){ |
| 659 | if( isLeaf ){ |
| 660 | if( db_exists("SELECT 1 FROM tagxref" |
| 661 | " WHERE rid=%d AND tagid=%d AND tagtype>0", |
| 662 | rid, TAG_CLOSED) ){ |
| 663 | @ <span class='timelineLeaf'>Closed-Leaf</span> |
| 664 | }else{ |
| 665 | @ <span class='timelineLeaf'>Leaf</span> |
| 666 | } |
| 667 | } |
| @@ -1715,19 +1734,26 @@ | |
| 1715 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1716 | int pd_rid; |
| 1717 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1718 | const char *z; |
| 1719 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1720 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1721 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1722 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1723 | int disableY = 0; /* Disable type selector on submenu */ |
| 1724 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1725 | char *zPlural; /* Ending for plural forms */ |
| 1726 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1727 | |
| 1728 | /* Set number of rows to display */ |
| 1729 | cookie_read_parameter("n","n"); |
| 1730 | z = P("n"); |
| 1731 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1732 | if( z ){ |
| 1733 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1785,12 +1811,10 @@ | |
| 1785 | } |
| 1786 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1787 | cookie_write_parameter("y","y",zType); |
| 1788 | } |
| 1789 | cookie_render(); |
| 1790 | url_initialize(&url, "timeline"); |
| 1791 | cgi_query_parameters_to_url(&url); |
| 1792 | |
| 1793 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1794 | if( P("cf")!=0 ){ |
| 1795 | zCirca = db_text(0, |
| 1796 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2052,11 +2076,10 @@ | |
| 2052 | " WHERE mlink.mid=x" |
| 2053 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2054 | glob_expr("filename.name", zChng) |
| 2055 | ); |
| 2056 | } |
| 2057 | // tmFlags |= TIMELINE_DISJOINT; |
| 2058 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2059 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2060 | if( advancedMenu ){ |
| 2061 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2062 | } |
| @@ -2106,10 +2129,11 @@ | |
| 2106 | db_multi_exec("DELETE FROM ok"); |
| 2107 | } |
| 2108 | if( p_rid ){ |
| 2109 | zBackTo = P("bt"); |
| 2110 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2111 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2112 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2113 | if( np>0 || nd==0 ){ |
| 2114 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2115 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2204,27 +2228,57 @@ | |
| 2204 | } |
| 2205 | if( bisectLocal || zBisect!=0 ){ |
| 2206 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2207 | } |
| 2208 | if( zYearMonth ){ |
| 2209 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2210 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2211 | zYearMonth); |
| 2212 | } |
| 2213 | else if( zYearWeek ){ |
| 2214 | char *z; |
| 2215 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2216 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2217 | if( z && z[0] ){ |
| 2218 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2219 | zYearWeek); |
| 2220 | zYearWeek = z; |
| 2221 | }else{ |
| 2222 | if( strlen(zYearWeek)==7 ){ |
| 2223 | zYearWeekStart = db_text(0, |
| 2224 | "SELECT date('%.4q-01-01','+%d days','weekday 1')", |
| 2225 | zYearWeek, atoi(zYearWeek+5)*7); |
| 2226 | }else{ |
| 2227 | zYearWeekStart = 0; |
| 2228 | } |
| 2229 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2230 | zYearWeekStart = db_text(0, |
| @@ -2231,20 +2285,61 @@ | |
| 2231 | "SELECT date('now','-6 days','weekday 1');"); |
| 2232 | zYearWeek = db_text(0, |
| 2233 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2234 | } |
| 2235 | } |
| 2236 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2237 | zYearWeek); |
| 2238 | nEntry = -1; |
| 2239 | } |
| 2240 | else if( zDay ){ |
| 2241 | zDay = timeline_expand_datetime(zDay); |
| 2242 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2243 | if( zDay==0 || zDay[0]==0 ){ |
| 2244 | zDay = db_text(0, "SELECT date('now')"); |
| 2245 | } |
| 2246 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2247 | zDay); |
| 2248 | nEntry = -1; |
| 2249 | } |
| 2250 | else if( zNDays ){ |
| @@ -2440,11 +2535,12 @@ | |
| 2440 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2441 | |
| 2442 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2443 | zPlural = n==1 ? "" : "s"; |
| 2444 | if( zYearMonth ){ |
| 2445 | blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth); |
| 2446 | }else if( zYearWeek ){ |
| 2447 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2448 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2449 | }else if( zDay ){ |
| 2450 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2532,10 +2628,11 @@ | |
| 2532 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2533 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2534 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2535 | ){ |
| 2536 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2537 | } |
| 2538 | free(zDate); |
| 2539 | } |
| 2540 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2541 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2547,10 +2644,11 @@ | |
| 2547 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2548 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2549 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2550 | ){ |
| 2551 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2552 | } |
| 2553 | free(zDate); |
| 2554 | } |
| 2555 | if( advancedMenu ){ |
| 2556 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2616,17 +2714,19 @@ | |
| 2616 | if( zError ){ |
| 2617 | @ <p class="generalError">%h(zError)</p> |
| 2618 | } |
| 2619 | |
| 2620 | if( zNewerButton ){ |
| 2621 | @ %z(chref("button","%z",zNewerButton))More ↑</a> |
| 2622 | } |
| 2623 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2624 | selectedRid, secondaryRid, 0); |
| 2625 | db_finalize(&q); |
| 2626 | if( zOlderButton ){ |
| 2627 | @ %z(chref("button","%z",zOlderButton))More ↓</a> |
| 2628 | } |
| 2629 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2630 | style_finish_page("timeline"); |
| 2631 | } |
| 2632 | |
| @@ -2650,10 +2750,11 @@ | |
| 2650 | ** 3. Comment string and user |
| 2651 | ** 4. Number of non-merge children |
| 2652 | ** 5. Number of parents |
| 2653 | ** 6. mtime |
| 2654 | ** 7. branch |
| 2655 | */ |
| 2656 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2657 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2658 | int nLine = 0; |
| 2659 | int nEntry = 0; |
| @@ -2674,10 +2775,11 @@ | |
| 2674 | const char *zId = db_column_text(q, 1); |
| 2675 | const char *zDate = db_column_text(q, 2); |
| 2676 | const char *zCom = db_column_text(q, 3); |
| 2677 | int nChild = db_column_int(q, 4); |
| 2678 | int nParent = db_column_int(q, 5); |
| 2679 | char *zFree = 0; |
| 2680 | int n = 0; |
| 2681 | char zPrefix[80]; |
| 2682 | |
| 2683 | if( nAbsLimit!=0 ){ |
| @@ -2717,11 +2819,22 @@ | |
| 2717 | } |
| 2718 | if( content_is_private(rid) ){ |
| 2719 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2720 | n += strlen(zPrefix+n); |
| 2721 | } |
| 2722 | zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); |
| 2723 | /* record another X lines */ |
| 2724 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2725 | fossil_free(zFree); |
| 2726 | |
| 2727 | if(verboseFlag){ |
| @@ -2787,11 +2900,12 @@ | |
| 2787 | @ || ')' as comment, |
| 2788 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2789 | @ AS primPlinkCount, |
| 2790 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2791 | @ event.mtime AS mtime, |
| 2792 | @ tagxref.value AS branch |
| 2793 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2794 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2795 | @ AND tagxref.tagtype>0 |
| 2796 | @ AND tagxref.rid=blob.rid |
| 2797 | @ WHERE blob.rid=event.objid |
| 2798 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -224,10 +224,26 @@ | |
| 224 | */ |
| 225 | int timeline_tableid(void){ |
| 226 | static int id = 0; |
| 227 | return id++; |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** Return true if the checking identified by "rid" has a valid "closed" |
| 232 | ** tag. |
| 233 | */ |
| 234 | static int has_closed_tag(int rid){ |
| 235 | static Stmt q; |
| 236 | int res = 0; |
| 237 | db_static_prepare(&q, |
| 238 | "SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0", |
| 239 | TAG_CLOSED); |
| 240 | db_bind_int(&q, "$rid", rid); |
| 241 | res = db_step(&q)==SQLITE_ROW; |
| 242 | db_reset(&q); |
| 243 | return res; |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | ** Output a timeline in the web format given a query. The query |
| 248 | ** should return these columns: |
| 249 | ** |
| @@ -554,13 +570,11 @@ | |
| 570 | } |
| 571 | if( (tmFlags & TIMELINE_CLASSIC)!=0 ){ |
| 572 | if( zType[0]=='c' ){ |
| 573 | hyperlink_to_version(zUuid); |
| 574 | if( isLeaf ){ |
| 575 | if( has_closed_tag(rid) ){ |
| 576 | @ <span class="timelineLeaf">Closed-Leaf:</span> |
| 577 | }else{ |
| 578 | @ <span class="timelineLeaf">Leaf:</span> |
| 579 | } |
| 580 | } |
| @@ -581,25 +595,32 @@ | |
| 595 | if( zType[0]!='c' ){ |
| 596 | /* Comments for anything other than a check-in are generated by |
| 597 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| 598 | if( zType[0]=='w' ){ |
| 599 | const char *zCom = blob_str(&comment); |
| 600 | /* Except, the comments generated by "fossil rebuild" for a wiki |
| 601 | ** page edit consist of a single character '-', '+', or ':' (to |
| 602 | ** indicate "deleted", "added", or "edited") followed by the |
| 603 | ** raw wiki page name. We have to generate an appropriate |
| 604 | ** comment on-the-fly |
| 605 | */ |
| 606 | wiki_hyperlink_override(zUuid); |
| 607 | if( zCom[0]=='-' ){ |
| 608 | @ Deleted wiki page "%z(href("%R/whistory?name=%t",zCom+1))\ |
| 609 | @ %h(zCom+1)</a> |
| 610 | }else if( (tmFlags & TIMELINE_REFS)!=0 |
| 611 | && (zCom[0]=='+' || zCom[0]==':') ){ |
| 612 | @ Wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a> |
| 613 | }else if( zCom[0]=='+' ){ |
| 614 | @ Added wiki page "%z(href("%R/wiki?name=%t",zCom+1))%h(zCom+1)</a> |
| 615 | }else if( zCom[0]==':' ){ |
| 616 | @ Changes to wiki page "%z(href("%R/wiki?name=%t",zCom+1))\ |
| 617 | @ %h(zCom+1)</a> |
| 618 | }else{ |
| 619 | /* Legacy EVENT table entry that needs to be rebuilt */ |
| 620 | @ Changes to a wiki page → Obsolete EVENT table information. |
| 621 | @ Run "fossil rebuild" on the repository. |
| 622 | } |
| 623 | wiki_hyperlink_override(0); |
| 624 | }else{ |
| 625 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 626 | } |
| @@ -655,13 +676,11 @@ | |
| 676 | } |
| 677 | |
| 678 | if( (tmFlags & TIMELINE_CLASSIC)==0 ){ |
| 679 | if( zType[0]=='c' ){ |
| 680 | if( isLeaf ){ |
| 681 | if( has_closed_tag(rid) ){ |
| 682 | @ <span class='timelineLeaf'>Closed-Leaf</span> |
| 683 | }else{ |
| 684 | @ <span class='timelineLeaf'>Leaf</span> |
| 685 | } |
| 686 | } |
| @@ -1715,19 +1734,26 @@ | |
| 1734 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1735 | int pd_rid; |
| 1736 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1737 | const char *z; |
| 1738 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1739 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1740 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| 1741 | char *zNewerButtonLabel = 0; /* Label for the Newer button */ |
| 1742 | int selectedRid = 0; /* Show a highlight on this RID */ |
| 1743 | int secondaryRid = 0; /* Show secondary highlight */ |
| 1744 | int disableY = 0; /* Disable type selector on submenu */ |
| 1745 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1746 | char *zPlural; /* Ending for plural forms */ |
| 1747 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1748 | int haveParameterN; /* True if n= query parameter present */ |
| 1749 | |
| 1750 | url_initialize(&url, "timeline"); |
| 1751 | cgi_query_parameters_to_url(&url); |
| 1752 | |
| 1753 | /* Set number of rows to display */ |
| 1754 | haveParameterN = P("n")!=0; |
| 1755 | cookie_read_parameter("n","n"); |
| 1756 | z = P("n"); |
| 1757 | if( z==0 ) z = db_get("timeline-default-length",0); |
| 1758 | if( z ){ |
| 1759 | if( fossil_strcmp(z,"all")==0 ){ |
| @@ -1785,12 +1811,10 @@ | |
| 1811 | } |
| 1812 | if( zType[0]=='a' || zType[0]=='c' ){ |
| 1813 | cookie_write_parameter("y","y",zType); |
| 1814 | } |
| 1815 | cookie_render(); |
| 1816 | |
| 1817 | /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ |
| 1818 | if( P("cf")!=0 ){ |
| 1819 | zCirca = db_text(0, |
| 1820 | "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" |
| @@ -2052,11 +2076,10 @@ | |
| 2076 | " WHERE mlink.mid=x" |
| 2077 | " AND mlink.fnid=filename.fnid AND %s)", |
| 2078 | glob_expr("filename.name", zChng) |
| 2079 | ); |
| 2080 | } |
| 2081 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2082 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2083 | if( advancedMenu ){ |
| 2084 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2085 | } |
| @@ -2106,10 +2129,11 @@ | |
| 2129 | db_multi_exec("DELETE FROM ok"); |
| 2130 | } |
| 2131 | if( p_rid ){ |
| 2132 | zBackTo = P("bt"); |
| 2133 | ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0; |
| 2134 | if( !haveParameterN ) nEntry = 0; |
| 2135 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2136 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2137 | if( np>0 || nd==0 ){ |
| 2138 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2139 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| @@ -2204,27 +2228,57 @@ | |
| 2228 | } |
| 2229 | if( bisectLocal || zBisect!=0 ){ |
| 2230 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); |
| 2231 | } |
| 2232 | if( zYearMonth ){ |
| 2233 | char *zNext; |
| 2234 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2235 | if( strlen(zYearMonth)>7 ){ |
| 2236 | zYearMonth = mprintf("%.7s", zYearMonth); |
| 2237 | } |
| 2238 | if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){ |
| 2239 | zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');"); |
| 2240 | } |
| 2241 | zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');", |
| 2242 | zYearMonth); |
| 2243 | if( db_int(0, |
| 2244 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2245 | " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)", |
| 2246 | zNext, blob_sql_text(&cond)) |
| 2247 | ){ |
| 2248 | zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); |
| 2249 | zNewerButtonLabel = "Following month"; |
| 2250 | } |
| 2251 | fossil_free(zNext); |
| 2252 | zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');", |
| 2253 | zYearMonth); |
| 2254 | if( db_int(0, |
| 2255 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2256 | " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)", |
| 2257 | zYearMonth, blob_sql_text(&cond)) |
| 2258 | ){ |
| 2259 | zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0)); |
| 2260 | zOlderButtonLabel = "Previous month"; |
| 2261 | } |
| 2262 | fossil_free(zNext); |
| 2263 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", |
| 2264 | zYearMonth); |
| 2265 | nEntry = -1; |
| 2266 | } |
| 2267 | else if( zYearWeek ){ |
| 2268 | char *z, *zNext; |
| 2269 | zYearWeek = timeline_expand_datetime(zYearWeek); |
| 2270 | z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); |
| 2271 | if( z && z[0] ){ |
| 2272 | zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", |
| 2273 | zYearWeek); |
| 2274 | zYearWeek = z; |
| 2275 | }else{ |
| 2276 | if( strlen(zYearWeek)==7 ){ |
| 2277 | zYearWeekStart = db_text(0, |
| 2278 | "SELECT date('%.4q-01-01','%+d days','weekday 1')", |
| 2279 | zYearWeek, atoi(zYearWeek+5)*7-6); |
| 2280 | }else{ |
| 2281 | zYearWeekStart = 0; |
| 2282 | } |
| 2283 | if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ |
| 2284 | zYearWeekStart = db_text(0, |
| @@ -2231,20 +2285,61 @@ | |
| 2285 | "SELECT date('now','-6 days','weekday 1');"); |
| 2286 | zYearWeek = db_text(0, |
| 2287 | "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); |
| 2288 | } |
| 2289 | } |
| 2290 | zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart); |
| 2291 | if( db_int(0, |
| 2292 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2293 | " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", |
| 2294 | zNext, blob_sql_text(&cond)) |
| 2295 | ){ |
| 2296 | zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); |
| 2297 | zNewerButtonLabel = "Following week"; |
| 2298 | } |
| 2299 | fossil_free(zNext); |
| 2300 | zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart); |
| 2301 | if( db_int(0, |
| 2302 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2303 | " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", |
| 2304 | zYearWeekStart, blob_sql_text(&cond)) |
| 2305 | ){ |
| 2306 | zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0)); |
| 2307 | zOlderButtonLabel = "Previous week"; |
| 2308 | } |
| 2309 | fossil_free(zNext); |
| 2310 | blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", |
| 2311 | zYearWeek); |
| 2312 | nEntry = -1; |
| 2313 | } |
| 2314 | else if( zDay ){ |
| 2315 | char *zNext; |
| 2316 | zDay = timeline_expand_datetime(zDay); |
| 2317 | zDay = db_text(0, "SELECT date(%Q)", zDay); |
| 2318 | if( zDay==0 || zDay[0]==0 ){ |
| 2319 | zDay = db_text(0, "SELECT date('now')"); |
| 2320 | } |
| 2321 | zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay); |
| 2322 | if( db_int(0, |
| 2323 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2324 | " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)", |
| 2325 | zNext, blob_sql_text(&cond)) |
| 2326 | ){ |
| 2327 | zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); |
| 2328 | zNewerButtonLabel = "Following day"; |
| 2329 | } |
| 2330 | fossil_free(zNext); |
| 2331 | zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay); |
| 2332 | if( db_int(0, |
| 2333 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2334 | " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)", |
| 2335 | zDay, blob_sql_text(&cond)) |
| 2336 | ){ |
| 2337 | zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0)); |
| 2338 | zOlderButtonLabel = "Previous day"; |
| 2339 | } |
| 2340 | fossil_free(zNext); |
| 2341 | blob_append_sql(&cond, " AND %Q=date(event.mtime) ", |
| 2342 | zDay); |
| 2343 | nEntry = -1; |
| 2344 | } |
| 2345 | else if( zNDays ){ |
| @@ -2440,11 +2535,12 @@ | |
| 2535 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2536 | |
| 2537 | n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); |
| 2538 | zPlural = n==1 ? "" : "s"; |
| 2539 | if( zYearMonth ){ |
| 2540 | blob_appendf(&desc, "%d %s%s for the month beginning %h-01", |
| 2541 | n, zEType, zPlural, zYearMonth); |
| 2542 | }else if( zYearWeek ){ |
| 2543 | blob_appendf(&desc, "%d %s%s for week %h beginning on %h", |
| 2544 | n, zEType, zPlural, zYearWeek, zYearWeekStart); |
| 2545 | }else if( zDay ){ |
| 2546 | blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); |
| @@ -2532,10 +2628,11 @@ | |
| 2628 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2629 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2630 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2631 | ){ |
| 2632 | zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); |
| 2633 | zOlderButtonLabel = "More"; |
| 2634 | } |
| 2635 | free(zDate); |
| 2636 | } |
| 2637 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2638 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| @@ -2547,10 +2644,11 @@ | |
| 2644 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2645 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2646 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2647 | ){ |
| 2648 | zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0)); |
| 2649 | zNewerButtonLabel = "More"; |
| 2650 | } |
| 2651 | free(zDate); |
| 2652 | } |
| 2653 | if( advancedMenu ){ |
| 2654 | if( zType[0]=='a' || zType[0]=='c' ){ |
| @@ -2616,17 +2714,19 @@ | |
| 2714 | if( zError ){ |
| 2715 | @ <p class="generalError">%h(zError)</p> |
| 2716 | } |
| 2717 | |
| 2718 | if( zNewerButton ){ |
| 2719 | @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ |
| 2720 | @ ↑</a> |
| 2721 | } |
| 2722 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName, |
| 2723 | selectedRid, secondaryRid, 0); |
| 2724 | db_finalize(&q); |
| 2725 | if( zOlderButton ){ |
| 2726 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 2727 | @ ↓</a> |
| 2728 | } |
| 2729 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2730 | style_finish_page("timeline"); |
| 2731 | } |
| 2732 | |
| @@ -2650,10 +2750,11 @@ | |
| 2750 | ** 3. Comment string and user |
| 2751 | ** 4. Number of non-merge children |
| 2752 | ** 5. Number of parents |
| 2753 | ** 6. mtime |
| 2754 | ** 7. branch |
| 2755 | ** 8. event-type: 'ci', 'w', 't', 'f', and so forth. |
| 2756 | */ |
| 2757 | void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ |
| 2758 | int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; |
| 2759 | int nLine = 0; |
| 2760 | int nEntry = 0; |
| @@ -2674,10 +2775,11 @@ | |
| 2775 | const char *zId = db_column_text(q, 1); |
| 2776 | const char *zDate = db_column_text(q, 2); |
| 2777 | const char *zCom = db_column_text(q, 3); |
| 2778 | int nChild = db_column_int(q, 4); |
| 2779 | int nParent = db_column_int(q, 5); |
| 2780 | const char *zType = db_column_text(q, 8); |
| 2781 | char *zFree = 0; |
| 2782 | int n = 0; |
| 2783 | char zPrefix[80]; |
| 2784 | |
| 2785 | if( nAbsLimit!=0 ){ |
| @@ -2717,11 +2819,22 @@ | |
| 2819 | } |
| 2820 | if( content_is_private(rid) ){ |
| 2821 | sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); |
| 2822 | n += strlen(zPrefix+n); |
| 2823 | } |
| 2824 | if( zType[0]=='w' && (zCom[0]=='+' || zCom[0]=='-' || zCom[0]==':') ){ |
| 2825 | /* Special processing for Wiki comments */ |
| 2826 | if( zCom[0]=='+' ){ |
| 2827 | zFree = mprintf("[%S] Add wiki page \"%s\"", zId, zCom+1); |
| 2828 | }else if( zCom[0]=='-' ){ |
| 2829 | zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1); |
| 2830 | }else{ |
| 2831 | zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1); |
| 2832 | } |
| 2833 | }else{ |
| 2834 | zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); |
| 2835 | } |
| 2836 | /* record another X lines */ |
| 2837 | nLine += comment_print(zFree, zCom, 9, width, get_comment_format()); |
| 2838 | fossil_free(zFree); |
| 2839 | |
| 2840 | if(verboseFlag){ |
| @@ -2787,11 +2900,12 @@ | |
| 2900 | @ || ')' as comment, |
| 2901 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 2902 | @ AS primPlinkCount, |
| 2903 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 2904 | @ event.mtime AS mtime, |
| 2905 | @ tagxref.value AS branch, |
| 2906 | @ event.type |
| 2907 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 2908 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 2909 | @ AND tagxref.tagtype>0 |
| 2910 | @ AND tagxref.rid=blob.rid |
| 2911 | @ WHERE blob.rid=event.objid |
| 2912 |
+1
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -446,10 +446,11 @@ | ||
| 446 | 446 | ){ |
| 447 | 447 | goto ticket_schema_error; |
| 448 | 448 | } |
| 449 | 449 | break; |
| 450 | 450 | } |
| 451 | + case SQLITE_FUNCTION: | |
| 451 | 452 | case SQLITE_REINDEX: |
| 452 | 453 | case SQLITE_TRANSACTION: |
| 453 | 454 | case SQLITE_READ: { |
| 454 | 455 | break; |
| 455 | 456 | } |
| 456 | 457 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -446,10 +446,11 @@ | |
| 446 | ){ |
| 447 | goto ticket_schema_error; |
| 448 | } |
| 449 | break; |
| 450 | } |
| 451 | case SQLITE_REINDEX: |
| 452 | case SQLITE_TRANSACTION: |
| 453 | case SQLITE_READ: { |
| 454 | break; |
| 455 | } |
| 456 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -446,10 +446,11 @@ | |
| 446 | ){ |
| 447 | goto ticket_schema_error; |
| 448 | } |
| 449 | break; |
| 450 | } |
| 451 | case SQLITE_FUNCTION: |
| 452 | case SQLITE_REINDEX: |
| 453 | case SQLITE_TRANSACTION: |
| 454 | case SQLITE_READ: { |
| 455 | break; |
| 456 | } |
| 457 |
+1
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -595,11 +595,11 @@ | ||
| 595 | 595 | if( dryRunFlag ){ |
| 596 | 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | 597 | }else{ |
| 598 | 598 | char *zPwd; |
| 599 | 599 | ensure_empty_dirs_created(1); |
| 600 | - sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, | |
| 600 | + sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, | |
| 601 | 601 | file_rmdir_sql_function, 0, 0); |
| 602 | 602 | zPwd = file_getcwd(0,0); |
| 603 | 603 | db_multi_exec( |
| 604 | 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 | 606 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -595,11 +595,11 @@ | |
| 595 | if( dryRunFlag ){ |
| 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | }else{ |
| 598 | char *zPwd; |
| 599 | ensure_empty_dirs_created(1); |
| 600 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0, |
| 601 | file_rmdir_sql_function, 0, 0); |
| 602 | zPwd = file_getcwd(0,0); |
| 603 | db_multi_exec( |
| 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -595,11 +595,11 @@ | |
| 595 | if( dryRunFlag ){ |
| 596 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| 597 | }else{ |
| 598 | char *zPwd; |
| 599 | ensure_empty_dirs_created(1); |
| 600 | sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, |
| 601 | file_rmdir_sql_function, 0, 0); |
| 602 | zPwd = file_getcwd(0,0); |
| 603 | db_multi_exec( |
| 604 | "SELECT rmdir(%Q||name) FROM dir_to_delete" |
| 605 | " WHERE (%Q||name)<>%Q ORDER BY name DESC", |
| 606 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -1299,11 +1299,11 @@ | ||
| 1299 | 1299 | well_formed_wiki_name_rules(); |
| 1300 | 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | 1301 | } |
| 1302 | 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | - "pikchr", 0); | |
| 1304 | + "pikchr", NULL); | |
| 1305 | 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | 1307 | builtin_fulfill_js_requests(); |
| 1308 | 1308 | /* Dynamically populate the editor... */ |
| 1309 | 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 | 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", 0); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", NULL); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -1299,11 +1299,11 @@ | ||
| 1299 | 1299 | well_formed_wiki_name_rules(); |
| 1300 | 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | 1301 | } |
| 1302 | 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | - "pikchr", 0); | |
| 1304 | + "pikchr", NULL); | |
| 1305 | 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | 1307 | builtin_fulfill_js_requests(); |
| 1308 | 1308 | /* Dynamically populate the editor... */ |
| 1309 | 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 | 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", 0); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1299,11 +1299,11 @@ | |
| 1299 | well_formed_wiki_name_rules(); |
| 1300 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1301 | } |
| 1302 | builtin_fossil_js_bundle_or("fetch", "dom", "tabs", "confirmer", |
| 1303 | "storage", "popupwidget", "copybutton", |
| 1304 | "pikchr", NULL); |
| 1305 | builtin_request_js("sbsdiff.js"); |
| 1306 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1307 | builtin_fulfill_js_requests(); |
| 1308 | /* Dynamically populate the editor... */ |
| 1309 | style_script_begin(__FILE__,__LINE__); |
| 1310 |
+1
-12
| --- www/alerts.md | ||
| +++ www/alerts.md | ||
| @@ -530,22 +530,11 @@ | ||
| 530 | 530 | |
| 531 | 531 | |
| 532 | 532 | <a id="backup"></a> |
| 533 | 533 | ## Cloning, Syncing, and Backups |
| 534 | 534 | |
| 535 | -The Admin → Notification settings are not replicated using clone or | |
| 536 | -sync, and it is not possible to push such settings from one repository | |
| 537 | -to another. In a network of peer repositories, you only want one | |
| 538 | -repository sending email alerts. If you were to replicate the email | |
| 539 | -alert settings to a separate repository, then subscribers would get | |
| 540 | -multiple alerts for each event, which would be bad. | |
| 541 | - | |
| 542 | -However, the subscriber list can be synced for backup purposes. Use the | |
| 543 | -[`fossil config pull subscriber`](/help?cmd=configuration) command to | |
| 544 | -pull the latest subscriber list from a server into a backup repository. | |
| 545 | - | |
| 546 | -The `push`, `export`, and `import` commands all work similarly. | |
| 535 | +That’s [covered elsewhere](./backup.md#alerts). | |
| 547 | 536 | |
| 548 | 537 | |
| 549 | 538 | <a id="pages" name="commands"></a> |
| 550 | 539 | ## Controlling the Email Alert System |
| 551 | 540 | |
| 552 | 541 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -530,22 +530,11 @@ | |
| 530 | |
| 531 | |
| 532 | <a id="backup"></a> |
| 533 | ## Cloning, Syncing, and Backups |
| 534 | |
| 535 | The Admin → Notification settings are not replicated using clone or |
| 536 | sync, and it is not possible to push such settings from one repository |
| 537 | to another. In a network of peer repositories, you only want one |
| 538 | repository sending email alerts. If you were to replicate the email |
| 539 | alert settings to a separate repository, then subscribers would get |
| 540 | multiple alerts for each event, which would be bad. |
| 541 | |
| 542 | However, the subscriber list can be synced for backup purposes. Use the |
| 543 | [`fossil config pull subscriber`](/help?cmd=configuration) command to |
| 544 | pull the latest subscriber list from a server into a backup repository. |
| 545 | |
| 546 | The `push`, `export`, and `import` commands all work similarly. |
| 547 | |
| 548 | |
| 549 | <a id="pages" name="commands"></a> |
| 550 | ## Controlling the Email Alert System |
| 551 | |
| 552 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -530,22 +530,11 @@ | |
| 530 | |
| 531 | |
| 532 | <a id="backup"></a> |
| 533 | ## Cloning, Syncing, and Backups |
| 534 | |
| 535 | That’s [covered elsewhere](./backup.md#alerts). |
| 536 | |
| 537 | |
| 538 | <a id="pages" name="commands"></a> |
| 539 | ## Controlling the Email Alert System |
| 540 | |
| 541 |
+48
-11
| --- www/backup.md | ||
| +++ www/backup.md | ||
| @@ -26,17 +26,54 @@ | ||
| 26 | 26 | reasons. |
| 27 | 27 | |
| 28 | 28 | |
| 29 | 29 | ## <a id="config"></a> Configuration Drift |
| 30 | 30 | |
| 31 | -Fossil allows the local configuration in certain areas to differ from | |
| 32 | -that of the remote. With the exception of the prior item, you get a copy | |
| 33 | -of these configuration areas on initial clone, but after that, some | |
| 34 | -remote configuration changes don’t sync down automatically, such as the | |
| 35 | -remote’s skin. You can ask for updates by running the | |
| 36 | -[`fossil config pull skin`](./help?cmd=config) command, but that | |
| 37 | -does not happen automatically during the course of normal development. | |
| 31 | +Fossil allows the local configuration to differ in several areas from | |
| 32 | +that of the remote. You get a copy | |
| 33 | +of *some* of these configuration areas on initial clone — not all! — but after that, | |
| 34 | +remote configuration changes mostly do not sync down automatically. | |
| 35 | + | |
| 36 | + | |
| 37 | +#### <a id="skin"></a> Skin | |
| 38 | + | |
| 39 | +Changes to the remote’s skin don’t sync down, on purpose, since you may | |
| 40 | +want to have a different skin on the local clone than on the remote. You | |
| 41 | +can ask for updates with [`fossil config pull skin`][cfg], but that does | |
| 42 | +not happen automatically during the course of normal development. | |
| 43 | + | |
| 44 | + | |
| 45 | +#### <a id="alerts"></a> Email Alerts | |
| 46 | + | |
| 47 | +The Admin → Notification settings do not get copied on clone or sync, | |
| 48 | +and it is not possible to push such settings from one repository to | |
| 49 | +another. We did this on purpose because you may have a network of peer | |
| 50 | +repositories, and you only want one repository sending email alerts. If | |
| 51 | +Fossil were to automatically replicate the email alert settings to a | |
| 52 | +separate repository, subscribers would get multiple alerts for each | |
| 53 | +event, which would be *bad.* | |
| 54 | + | |
| 55 | +The only element of the email alert configuration that can be pulled | |
| 56 | +over the sync protocol on demand is the subscriber list, via | |
| 57 | +[`fossil config pull subscriber`][cfg]. | |
| 58 | + | |
| 59 | + | |
| 60 | +#### <a id="project"></a> Project Configuration | |
| 61 | + | |
| 62 | +This is normally generated once during `fossil init` and never changed, | |
| 63 | +so Fossil doesn’t pull this information without being forced, on | |
| 64 | +purpose. You could accidentally merge two separate Fossil repos by | |
| 65 | +pushing one repo’s project config up to another, for example. | |
| 66 | + | |
| 67 | + | |
| 68 | +#### <a id="other-cfg"></a> Others | |
| 69 | + | |
| 70 | +A repo’s URL aliases, [interwiki configuration](./interwiki.md), and | |
| 71 | +[ticket customizations](./custom_tcket.wiki) also do not normally sync. | |
| 72 | + | |
| 73 | +[cfg]: /help?cmd=configuration | |
| 74 | + | |
| 38 | 75 | |
| 39 | 76 | |
| 40 | 77 | ## <a id="private"></a> Private Branches |
| 41 | 78 | |
| 42 | 79 | The very nature of Fossil’s [private branch feature][pbr] ensures that |
| @@ -92,11 +129,11 @@ | ||
| 92 | 129 | source of truth, those users still syncing with `svr2` won’t have their |
| 93 | 130 | commits pushed up to `svr1` unless you’ve set up bidirectional sync, |
| 94 | 131 | rather than have the two backup servers do `pull` only. |
| 95 | 132 | |
| 96 | 133 | |
| 97 | -# Solution 1: Explicit Pulls | |
| 134 | +# <a id="sync-solution"></a> Solution 1: Explicit Pulls | |
| 98 | 135 | |
| 99 | 136 | The following script solves most of the above problems for the use case |
| 100 | 137 | where you want a *nearly-complete* clone of the remote repository using nothing |
| 101 | 138 | but the normal Fossil sync protocol. It only does so if you are logged into |
| 102 | 139 | the remote as a user with Setup capability, however. |
| @@ -119,11 +156,11 @@ | ||
| 119 | 156 | that it will continue to have information that the remote says should |
| 120 | 157 | not exist any more. That would be not so much a “backup” as an |
| 121 | 158 | “archive,” which might not be what you want. |
| 122 | 159 | |
| 123 | 160 | |
| 124 | -# Solution 2: SQL-Level Backup | |
| 161 | +# <a id="sql-solution"></a> Solution 2: SQL-Level Backup | |
| 125 | 162 | |
| 126 | 163 | The first method doesn’t get you a copy of the remote’s |
| 127 | 164 | [private branches][pbr], on purpose. It may also miss other info on the |
| 128 | 165 | remote, such as SQL-level customizations that the sync protocol can’t |
| 129 | 166 | see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can |
| @@ -211,11 +248,11 @@ | ||
| 211 | 248 | care of. If all variables defined in earlier scripts are available, then |
| 212 | 249 | restoration is: |
| 213 | 250 | |
| 214 | 251 | ``` |
| 215 | 252 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 216 | - xz -d | fossil --no-repository ~/museum/restored-repo.fossil | |
| 253 | + xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil | |
| 217 | 254 | ``` |
| 218 | 255 | |
| 219 | 256 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 220 | 257 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 221 | 258 | file and writes the result to stdout. |
| @@ -228,11 +265,11 @@ | ||
| 228 | 265 | restoration: |
| 229 | 266 | Fossil serves as a dogfooding project for SQLite, |
| 230 | 267 | often making use of the latest features, so it is quite likely that a given |
| 231 | 268 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 232 | 269 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 233 | -pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil | |
| 270 | +pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil | |
| 234 | 271 | normally goes looking for tables created by `fossil init`, and it won’t |
| 235 | 272 | find them in a newly-created repo DB. We get around this by passing |
| 236 | 273 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 237 | 274 | this way saves you from needing to go and build a matching version of |
| 238 | 275 | `sqlite3` just to restore the backup. |
| 239 | 276 |
| --- www/backup.md | |
| +++ www/backup.md | |
| @@ -26,17 +26,54 @@ | |
| 26 | reasons. |
| 27 | |
| 28 | |
| 29 | ## <a id="config"></a> Configuration Drift |
| 30 | |
| 31 | Fossil allows the local configuration in certain areas to differ from |
| 32 | that of the remote. With the exception of the prior item, you get a copy |
| 33 | of these configuration areas on initial clone, but after that, some |
| 34 | remote configuration changes don’t sync down automatically, such as the |
| 35 | remote’s skin. You can ask for updates by running the |
| 36 | [`fossil config pull skin`](./help?cmd=config) command, but that |
| 37 | does not happen automatically during the course of normal development. |
| 38 | |
| 39 | |
| 40 | ## <a id="private"></a> Private Branches |
| 41 | |
| 42 | The very nature of Fossil’s [private branch feature][pbr] ensures that |
| @@ -92,11 +129,11 @@ | |
| 92 | source of truth, those users still syncing with `svr2` won’t have their |
| 93 | commits pushed up to `svr1` unless you’ve set up bidirectional sync, |
| 94 | rather than have the two backup servers do `pull` only. |
| 95 | |
| 96 | |
| 97 | # Solution 1: Explicit Pulls |
| 98 | |
| 99 | The following script solves most of the above problems for the use case |
| 100 | where you want a *nearly-complete* clone of the remote repository using nothing |
| 101 | but the normal Fossil sync protocol. It only does so if you are logged into |
| 102 | the remote as a user with Setup capability, however. |
| @@ -119,11 +156,11 @@ | |
| 119 | that it will continue to have information that the remote says should |
| 120 | not exist any more. That would be not so much a “backup” as an |
| 121 | “archive,” which might not be what you want. |
| 122 | |
| 123 | |
| 124 | # Solution 2: SQL-Level Backup |
| 125 | |
| 126 | The first method doesn’t get you a copy of the remote’s |
| 127 | [private branches][pbr], on purpose. It may also miss other info on the |
| 128 | remote, such as SQL-level customizations that the sync protocol can’t |
| 129 | see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can |
| @@ -211,11 +248,11 @@ | |
| 211 | care of. If all variables defined in earlier scripts are available, then |
| 212 | restoration is: |
| 213 | |
| 214 | ``` |
| 215 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 216 | xz -d | fossil --no-repository ~/museum/restored-repo.fossil |
| 217 | ``` |
| 218 | |
| 219 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 220 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 221 | file and writes the result to stdout. |
| @@ -228,11 +265,11 @@ | |
| 228 | restoration: |
| 229 | Fossil serves as a dogfooding project for SQLite, |
| 230 | often making use of the latest features, so it is quite likely that a given |
| 231 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 232 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 233 | pipe the decrpted SQL dump into `fossil sql`, because on startup, Fossil |
| 234 | normally goes looking for tables created by `fossil init`, and it won’t |
| 235 | find them in a newly-created repo DB. We get around this by passing |
| 236 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 237 | this way saves you from needing to go and build a matching version of |
| 238 | `sqlite3` just to restore the backup. |
| 239 |
| --- www/backup.md | |
| +++ www/backup.md | |
| @@ -26,17 +26,54 @@ | |
| 26 | reasons. |
| 27 | |
| 28 | |
| 29 | ## <a id="config"></a> Configuration Drift |
| 30 | |
| 31 | Fossil allows the local configuration to differ in several areas from |
| 32 | that of the remote. You get a copy |
| 33 | of *some* of these configuration areas on initial clone — not all! — but after that, |
| 34 | remote configuration changes mostly do not sync down automatically. |
| 35 | |
| 36 | |
| 37 | #### <a id="skin"></a> Skin |
| 38 | |
| 39 | Changes to the remote’s skin don’t sync down, on purpose, since you may |
| 40 | want to have a different skin on the local clone than on the remote. You |
| 41 | can ask for updates with [`fossil config pull skin`][cfg], but that does |
| 42 | not happen automatically during the course of normal development. |
| 43 | |
| 44 | |
| 45 | #### <a id="alerts"></a> Email Alerts |
| 46 | |
| 47 | The Admin → Notification settings do not get copied on clone or sync, |
| 48 | and it is not possible to push such settings from one repository to |
| 49 | another. We did this on purpose because you may have a network of peer |
| 50 | repositories, and you only want one repository sending email alerts. If |
| 51 | Fossil were to automatically replicate the email alert settings to a |
| 52 | separate repository, subscribers would get multiple alerts for each |
| 53 | event, which would be *bad.* |
| 54 | |
| 55 | The only element of the email alert configuration that can be pulled |
| 56 | over the sync protocol on demand is the subscriber list, via |
| 57 | [`fossil config pull subscriber`][cfg]. |
| 58 | |
| 59 | |
| 60 | #### <a id="project"></a> Project Configuration |
| 61 | |
| 62 | This is normally generated once during `fossil init` and never changed, |
| 63 | so Fossil doesn’t pull this information without being forced, on |
| 64 | purpose. You could accidentally merge two separate Fossil repos by |
| 65 | pushing one repo’s project config up to another, for example. |
| 66 | |
| 67 | |
| 68 | #### <a id="other-cfg"></a> Others |
| 69 | |
| 70 | A repo’s URL aliases, [interwiki configuration](./interwiki.md), and |
| 71 | [ticket customizations](./custom_tcket.wiki) also do not normally sync. |
| 72 | |
| 73 | [cfg]: /help?cmd=configuration |
| 74 | |
| 75 | |
| 76 | |
| 77 | ## <a id="private"></a> Private Branches |
| 78 | |
| 79 | The very nature of Fossil’s [private branch feature][pbr] ensures that |
| @@ -92,11 +129,11 @@ | |
| 129 | source of truth, those users still syncing with `svr2` won’t have their |
| 130 | commits pushed up to `svr1` unless you’ve set up bidirectional sync, |
| 131 | rather than have the two backup servers do `pull` only. |
| 132 | |
| 133 | |
| 134 | # <a id="sync-solution"></a> Solution 1: Explicit Pulls |
| 135 | |
| 136 | The following script solves most of the above problems for the use case |
| 137 | where you want a *nearly-complete* clone of the remote repository using nothing |
| 138 | but the normal Fossil sync protocol. It only does so if you are logged into |
| 139 | the remote as a user with Setup capability, however. |
| @@ -119,11 +156,11 @@ | |
| 156 | that it will continue to have information that the remote says should |
| 157 | not exist any more. That would be not so much a “backup” as an |
| 158 | “archive,” which might not be what you want. |
| 159 | |
| 160 | |
| 161 | # <a id="sql-solution"></a> Solution 2: SQL-Level Backup |
| 162 | |
| 163 | The first method doesn’t get you a copy of the remote’s |
| 164 | [private branches][pbr], on purpose. It may also miss other info on the |
| 165 | remote, such as SQL-level customizations that the sync protocol can’t |
| 166 | see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can |
| @@ -211,11 +248,11 @@ | |
| 248 | care of. If all variables defined in earlier scripts are available, then |
| 249 | restoration is: |
| 250 | |
| 251 | ``` |
| 252 | openssl enc -d -aes-256-cbc -pbkdf2 -iter 52830 -pass pass:"$pass" -in "$gd" | |
| 253 | xz -d | fossil sql --no-repository ~/museum/restored-repo.fossil |
| 254 | ``` |
| 255 | |
| 256 | We changed the `-e` to `-d` on the `openssl` command to get decryption, |
| 257 | and we changed the `-out` to `-in` so it reads from the encrypted backup |
| 258 | file and writes the result to stdout. |
| @@ -228,11 +265,11 @@ | |
| 265 | restoration: |
| 266 | Fossil serves as a dogfooding project for SQLite, |
| 267 | often making use of the latest features, so it is quite likely that a given |
| 268 | random `sqlite3` binary in your `PATH` will be unable to understand the |
| 269 | file created by “`fossil sql .dump`”! The tricky bit is, you can’t just |
| 270 | pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil |
| 271 | normally goes looking for tables created by `fossil init`, and it won’t |
| 272 | find them in a newly-created repo DB. We get around this by passing |
| 273 | the `--no-repository` flag, which suppresses this behavior. Doing it |
| 274 | this way saves you from needing to go and build a matching version of |
| 275 | `sqlite3` just to restore the backup. |
| 276 |
+1
-1
| --- www/embeddeddoc.wiki | ||
| +++ www/embeddeddoc.wiki | ||
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | pull the documentation file from the local source tree on disk, not |
| 57 | 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | 58 | only works when you start your server using the |
| 59 | 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | -the documentation you are currently editing but have not yet you checked in. | |
| 61 | +the documentation you are currently editing but have not yet checked in. | |
| 62 | 62 | |
| 63 | 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | 64 | user to preview local changes to documentation before committing the |
| 65 | 65 | change. This is an important facility, since unlike other document |
| 66 | 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 | 67 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | pull the documentation file from the local source tree on disk, not |
| 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | only works when you start your server using the |
| 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | the documentation you are currently editing but have not yet you checked in. |
| 62 | |
| 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | user to preview local changes to documentation before committing the |
| 65 | change. This is an important facility, since unlike other document |
| 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -56,11 +56,11 @@ | |
| 56 | pull the documentation file from the local source tree on disk, not |
| 57 | from the any check-in. The "<b>ckout</b>" keyword |
| 58 | only works when you start your server using the |
| 59 | "[/help?cmd=server|fossil server]" or "[/help?cmd=ui|fossil ui]" |
| 60 | commands. The "/doc/ckout" URL is intended to show a preview of |
| 61 | the documentation you are currently editing but have not yet checked in. |
| 62 | |
| 63 | The original designed purpose of the "ckout" feature is to allow the |
| 64 | user to preview local changes to documentation before committing the |
| 65 | change. This is an important facility, since unlike other document |
| 66 | languages like HTML, there is still a lot of variation among rendering |
| 67 |
+11
-11
| --- www/fileedit-page.md | ||
| +++ www/fileedit-page.md | ||
| @@ -9,11 +9,11 @@ | ||
| 9 | 9 | |
| 10 | 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | 11 | browser halfway around the world comes with several obligatory caveats |
| 12 | 12 | and disclaimers... |
| 13 | 13 | |
| 14 | -## `/fileedit` Does *Nothing* by Default. | |
| 14 | +## <a id="cap"></a> `/fileedit` Does *Nothing* by Default. | |
| 15 | 15 | |
| 16 | 16 | In order to "activate" it, a user with [the "setup" |
| 17 | 17 | permission](./caps/index.md) must set the |
| 18 | 18 | [fileedit-glob](/help?cmd=fileedit-glob) repository setting to a |
| 19 | 19 | comma- or newline-delimited list of globs representing a whitelist of |
| @@ -20,11 +20,11 @@ | ||
| 20 | 20 | files which may be edited online. Any user with commit access may then |
| 21 | 21 | edit files matching one of those globs. Certain pages within the UI |
| 22 | 22 | get an "edit" link added to them when the current user's permissions |
| 23 | 23 | and the whitelist both permit editing of that file. |
| 24 | 24 | |
| 25 | -## CSRF & HTTP Referrer Headers | |
| 25 | +## <a id="csrf"></a> CSRF & HTTP Referrer Headers | |
| 26 | 26 | |
| 27 | 27 | In order to protect against [Cross-site Request Forgery (CSRF)][csrf] |
| 28 | 28 | attacks, Fossil UI features which write to the database require that |
| 29 | 29 | the browser send the so-called [HTTP `Referer` header][referer] |
| 30 | 30 | (noting that the misspelling of "referrer" is a historical accident |
| @@ -53,16 +53,16 @@ | ||
| 53 | 53 | |
| 54 | 54 | [referer]: https://en.wikipedia.org/wiki/HTTP_referer |
| 55 | 55 | [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery |
| 56 | 56 | [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest |
| 57 | 57 | |
| 58 | -## `/fileedit` **Works by Creating Commits** | |
| 58 | +## <a id="commit"></a> `/fileedit` **Works by Creating Commits** | |
| 59 | 59 | |
| 60 | 60 | Thus any edits made via that page become a normal part of the |
| 61 | 61 | repository. |
| 62 | 62 | |
| 63 | -## `/fileedit` is *Intended* for use with Embedded Docs | |
| 63 | +## <a id="intent"></a> `/fileedit` is *Intended* for use with Embedded Docs | |
| 64 | 64 | |
| 65 | 65 | ... and similar text files, and is most certainly |
| 66 | 66 | **not intended for editing code**. |
| 67 | 67 | |
| 68 | 68 | Editing files with unusual syntax requirements, e.g. hard tabs in |
| @@ -75,11 +75,11 @@ | ||
| 75 | 75 | changes. **Files with mixed EOL styles** *will be normalized to a single |
| 76 | 76 | EOL style* when modified using `/fileedit`. When "inheriting" the EOL |
| 77 | 77 | style from a previous version which has mixed styles, the first EOL |
| 78 | 78 | style detected in the previous version of the file is used. |
| 79 | 79 | |
| 80 | -## `/fileedit` **is Not a Replacement for a Checkout** | |
| 80 | +## <a id="checkout"></a> `/fileedit` **is Not a Replacement for a Checkout** | |
| 81 | 81 | |
| 82 | 82 | A full-featured checkout allows far more possibilities than this basic |
| 83 | 83 | online editor permits, and the feature scope of `/fileedit` is |
| 84 | 84 | intentionally kept small, implementing only the bare necessities |
| 85 | 85 | needed for performing basic edits online. It *is not, and will never |
| @@ -108,11 +108,11 @@ | ||
| 108 | 108 | whether or not to implement them subject to notable contributor |
| 109 | 109 | debate. e.g. the ability to add new files or remove/rename older |
| 110 | 110 | files. |
| 111 | 111 | |
| 112 | 112 | |
| 113 | -## `/fileedit` **Stores Only Limited Local Edits While Working** | |
| 113 | +## <a id="storage"></a> `/fileedit` **Stores Only Limited Local Edits While Working** | |
| 114 | 114 | |
| 115 | 115 | When changes are made to a given checkin/file combination, |
| 116 | 116 | `/fileedit` will, if possible, store them in [`window.localStorage` |
| 117 | 117 | or `window.sessionStorage`][html5storage], if available, but... |
| 118 | 118 | |
| @@ -142,23 +142,23 @@ | ||
| 142 | 142 | If `/filepage` determines that no peristent storage is available a |
| 143 | 143 | warning is displayed on the editor page. |
| 144 | 144 | |
| 145 | 145 | [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API |
| 146 | 146 | |
| 147 | -## The Power is Yours, but... | |
| 147 | +## <a id="power"></a> The Power is Yours, but... | |
| 148 | 148 | |
| 149 | 149 | > "With great power comes great responsibility." |
| 150 | 150 | |
| 151 | 151 | **Use this feature judiciously, *if at all*.** |
| 152 | 152 | |
| 153 | 153 | Now, with those warnings and caveats out of the way... |
| 154 | 154 | |
| 155 | 155 | ----- |
| 156 | 156 | |
| 157 | -# Tips and Tricks | |
| 157 | +# <a id="tips"></a> Tips and Tricks | |
| 158 | 158 | |
| 159 | -## `fossil` Global-scope JS Object | |
| 159 | +## <a id="global-js"></a> `fossil` Global-scope JS Object | |
| 160 | 160 | |
| 161 | 161 | `/fileedit` is largely implemented in JavaScript, and makes heavy use |
| 162 | 162 | of the global-scope `fossil` object, which provides |
| 163 | 163 | infrastructure-level features intended for use by Fossil UI pages. |
| 164 | 164 | (That said, that infrastructure was introduced with `/fileedit`, and |
| @@ -169,11 +169,11 @@ | ||
| 169 | 169 | listening to page-specific events so that JS code installed via |
| 170 | 170 | [client-side edits to the site skin's footer](customskin.md) may react |
| 171 | 171 | to those changes somehow. The next section describes one such use for |
| 172 | 172 | such events... |
| 173 | 173 | |
| 174 | -## Integrating Syntax Highlighting | |
| 174 | +## <a id="syn-hl"></a> Integrating Syntax Highlighting | |
| 175 | 175 | |
| 176 | 176 | Assuming a repository has integrated a 3rd-party syntax highlighting |
| 177 | 177 | solution, it can probably (depending on its API) be told how to |
| 178 | 178 | highlight `/fileedit`'s wiki/markdown-format previews. Here are |
| 179 | 179 | instructions for doing so with [highlightjs](https://highlightjs.org/): |
| @@ -223,11 +223,11 @@ | ||
| 223 | 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | 224 | but makes used of the other two. It fishes all `code` blocks out of |
| 225 | 225 | the preview which explicitly have a CSS class named |
| 226 | 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | 227 | |
| 228 | -## Integrating a Custom Editor Widget | |
| 228 | +## <a id="editor"></a> Integrating a Custom Editor Widget | |
| 229 | 229 | |
| 230 | 230 | (These instructions also work for the `/wikiedit` page by eplacing |
| 231 | 231 | "fileedit" with "wikiedit" in any strings or symbol names!) |
| 232 | 232 | |
| 233 | 233 | It is possible to replace `/filepage`'s basic text-editing widget (a |
| 234 | 234 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | |
| 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | browser halfway around the world comes with several obligatory caveats |
| 12 | and disclaimers... |
| 13 | |
| 14 | ## `/fileedit` Does *Nothing* by Default. |
| 15 | |
| 16 | In order to "activate" it, a user with [the "setup" |
| 17 | permission](./caps/index.md) must set the |
| 18 | [fileedit-glob](/help?cmd=fileedit-glob) repository setting to a |
| 19 | comma- or newline-delimited list of globs representing a whitelist of |
| @@ -20,11 +20,11 @@ | |
| 20 | files which may be edited online. Any user with commit access may then |
| 21 | edit files matching one of those globs. Certain pages within the UI |
| 22 | get an "edit" link added to them when the current user's permissions |
| 23 | and the whitelist both permit editing of that file. |
| 24 | |
| 25 | ## CSRF & HTTP Referrer Headers |
| 26 | |
| 27 | In order to protect against [Cross-site Request Forgery (CSRF)][csrf] |
| 28 | attacks, Fossil UI features which write to the database require that |
| 29 | the browser send the so-called [HTTP `Referer` header][referer] |
| 30 | (noting that the misspelling of "referrer" is a historical accident |
| @@ -53,16 +53,16 @@ | |
| 53 | |
| 54 | [referer]: https://en.wikipedia.org/wiki/HTTP_referer |
| 55 | [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery |
| 56 | [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest |
| 57 | |
| 58 | ## `/fileedit` **Works by Creating Commits** |
| 59 | |
| 60 | Thus any edits made via that page become a normal part of the |
| 61 | repository. |
| 62 | |
| 63 | ## `/fileedit` is *Intended* for use with Embedded Docs |
| 64 | |
| 65 | ... and similar text files, and is most certainly |
| 66 | **not intended for editing code**. |
| 67 | |
| 68 | Editing files with unusual syntax requirements, e.g. hard tabs in |
| @@ -75,11 +75,11 @@ | |
| 75 | changes. **Files with mixed EOL styles** *will be normalized to a single |
| 76 | EOL style* when modified using `/fileedit`. When "inheriting" the EOL |
| 77 | style from a previous version which has mixed styles, the first EOL |
| 78 | style detected in the previous version of the file is used. |
| 79 | |
| 80 | ## `/fileedit` **is Not a Replacement for a Checkout** |
| 81 | |
| 82 | A full-featured checkout allows far more possibilities than this basic |
| 83 | online editor permits, and the feature scope of `/fileedit` is |
| 84 | intentionally kept small, implementing only the bare necessities |
| 85 | needed for performing basic edits online. It *is not, and will never |
| @@ -108,11 +108,11 @@ | |
| 108 | whether or not to implement them subject to notable contributor |
| 109 | debate. e.g. the ability to add new files or remove/rename older |
| 110 | files. |
| 111 | |
| 112 | |
| 113 | ## `/fileedit` **Stores Only Limited Local Edits While Working** |
| 114 | |
| 115 | When changes are made to a given checkin/file combination, |
| 116 | `/fileedit` will, if possible, store them in [`window.localStorage` |
| 117 | or `window.sessionStorage`][html5storage], if available, but... |
| 118 | |
| @@ -142,23 +142,23 @@ | |
| 142 | If `/filepage` determines that no peristent storage is available a |
| 143 | warning is displayed on the editor page. |
| 144 | |
| 145 | [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API |
| 146 | |
| 147 | ## The Power is Yours, but... |
| 148 | |
| 149 | > "With great power comes great responsibility." |
| 150 | |
| 151 | **Use this feature judiciously, *if at all*.** |
| 152 | |
| 153 | Now, with those warnings and caveats out of the way... |
| 154 | |
| 155 | ----- |
| 156 | |
| 157 | # Tips and Tricks |
| 158 | |
| 159 | ## `fossil` Global-scope JS Object |
| 160 | |
| 161 | `/fileedit` is largely implemented in JavaScript, and makes heavy use |
| 162 | of the global-scope `fossil` object, which provides |
| 163 | infrastructure-level features intended for use by Fossil UI pages. |
| 164 | (That said, that infrastructure was introduced with `/fileedit`, and |
| @@ -169,11 +169,11 @@ | |
| 169 | listening to page-specific events so that JS code installed via |
| 170 | [client-side edits to the site skin's footer](customskin.md) may react |
| 171 | to those changes somehow. The next section describes one such use for |
| 172 | such events... |
| 173 | |
| 174 | ## Integrating Syntax Highlighting |
| 175 | |
| 176 | Assuming a repository has integrated a 3rd-party syntax highlighting |
| 177 | solution, it can probably (depending on its API) be told how to |
| 178 | highlight `/fileedit`'s wiki/markdown-format previews. Here are |
| 179 | instructions for doing so with [highlightjs](https://highlightjs.org/): |
| @@ -223,11 +223,11 @@ | |
| 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | but makes used of the other two. It fishes all `code` blocks out of |
| 225 | the preview which explicitly have a CSS class named |
| 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | |
| 228 | ## Integrating a Custom Editor Widget |
| 229 | |
| 230 | (These instructions also work for the `/wikiedit` page by eplacing |
| 231 | "fileedit" with "wikiedit" in any strings or symbol names!) |
| 232 | |
| 233 | It is possible to replace `/filepage`'s basic text-editing widget (a |
| 234 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -9,11 +9,11 @@ | |
| 9 | |
| 10 | Predictably, the ability to edit files in a repository from a web |
| 11 | browser halfway around the world comes with several obligatory caveats |
| 12 | and disclaimers... |
| 13 | |
| 14 | ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default. |
| 15 | |
| 16 | In order to "activate" it, a user with [the "setup" |
| 17 | permission](./caps/index.md) must set the |
| 18 | [fileedit-glob](/help?cmd=fileedit-glob) repository setting to a |
| 19 | comma- or newline-delimited list of globs representing a whitelist of |
| @@ -20,11 +20,11 @@ | |
| 20 | files which may be edited online. Any user with commit access may then |
| 21 | edit files matching one of those globs. Certain pages within the UI |
| 22 | get an "edit" link added to them when the current user's permissions |
| 23 | and the whitelist both permit editing of that file. |
| 24 | |
| 25 | ## <a id="csrf"></a> CSRF & HTTP Referrer Headers |
| 26 | |
| 27 | In order to protect against [Cross-site Request Forgery (CSRF)][csrf] |
| 28 | attacks, Fossil UI features which write to the database require that |
| 29 | the browser send the so-called [HTTP `Referer` header][referer] |
| 30 | (noting that the misspelling of "referrer" is a historical accident |
| @@ -53,16 +53,16 @@ | |
| 53 | |
| 54 | [referer]: https://en.wikipedia.org/wiki/HTTP_referer |
| 55 | [csrf]: https://en.wikipedia.org/wiki/Cross-site_request_forgery |
| 56 | [xhr]: https://en.wikipedia.org/wiki/XMLHttpRequest |
| 57 | |
| 58 | ## <a id="commit"></a> `/fileedit` **Works by Creating Commits** |
| 59 | |
| 60 | Thus any edits made via that page become a normal part of the |
| 61 | repository. |
| 62 | |
| 63 | ## <a id="intent"></a> `/fileedit` is *Intended* for use with Embedded Docs |
| 64 | |
| 65 | ... and similar text files, and is most certainly |
| 66 | **not intended for editing code**. |
| 67 | |
| 68 | Editing files with unusual syntax requirements, e.g. hard tabs in |
| @@ -75,11 +75,11 @@ | |
| 75 | changes. **Files with mixed EOL styles** *will be normalized to a single |
| 76 | EOL style* when modified using `/fileedit`. When "inheriting" the EOL |
| 77 | style from a previous version which has mixed styles, the first EOL |
| 78 | style detected in the previous version of the file is used. |
| 79 | |
| 80 | ## <a id="checkout"></a> `/fileedit` **is Not a Replacement for a Checkout** |
| 81 | |
| 82 | A full-featured checkout allows far more possibilities than this basic |
| 83 | online editor permits, and the feature scope of `/fileedit` is |
| 84 | intentionally kept small, implementing only the bare necessities |
| 85 | needed for performing basic edits online. It *is not, and will never |
| @@ -108,11 +108,11 @@ | |
| 108 | whether or not to implement them subject to notable contributor |
| 109 | debate. e.g. the ability to add new files or remove/rename older |
| 110 | files. |
| 111 | |
| 112 | |
| 113 | ## <a id="storage"></a> `/fileedit` **Stores Only Limited Local Edits While Working** |
| 114 | |
| 115 | When changes are made to a given checkin/file combination, |
| 116 | `/fileedit` will, if possible, store them in [`window.localStorage` |
| 117 | or `window.sessionStorage`][html5storage], if available, but... |
| 118 | |
| @@ -142,23 +142,23 @@ | |
| 142 | If `/filepage` determines that no peristent storage is available a |
| 143 | warning is displayed on the editor page. |
| 144 | |
| 145 | [html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API |
| 146 | |
| 147 | ## <a id="power"></a> The Power is Yours, but... |
| 148 | |
| 149 | > "With great power comes great responsibility." |
| 150 | |
| 151 | **Use this feature judiciously, *if at all*.** |
| 152 | |
| 153 | Now, with those warnings and caveats out of the way... |
| 154 | |
| 155 | ----- |
| 156 | |
| 157 | # <a id="tips"></a> Tips and Tricks |
| 158 | |
| 159 | ## <a id="global-js"></a> `fossil` Global-scope JS Object |
| 160 | |
| 161 | `/fileedit` is largely implemented in JavaScript, and makes heavy use |
| 162 | of the global-scope `fossil` object, which provides |
| 163 | infrastructure-level features intended for use by Fossil UI pages. |
| 164 | (That said, that infrastructure was introduced with `/fileedit`, and |
| @@ -169,11 +169,11 @@ | |
| 169 | listening to page-specific events so that JS code installed via |
| 170 | [client-side edits to the site skin's footer](customskin.md) may react |
| 171 | to those changes somehow. The next section describes one such use for |
| 172 | such events... |
| 173 | |
| 174 | ## <a id="syn-hl"></a> Integrating Syntax Highlighting |
| 175 | |
| 176 | Assuming a repository has integrated a 3rd-party syntax highlighting |
| 177 | solution, it can probably (depending on its API) be told how to |
| 178 | highlight `/fileedit`'s wiki/markdown-format previews. Here are |
| 179 | instructions for doing so with [highlightjs](https://highlightjs.org/): |
| @@ -223,11 +223,11 @@ | |
| 223 | The event listener callback shown above doesn't use the `mimetype`, |
| 224 | but makes used of the other two. It fishes all `code` blocks out of |
| 225 | the preview which explicitly have a CSS class named |
| 226 | `language-`something, and then asks highlightjs to highlight them. |
| 227 | |
| 228 | ## <a id="editor"></a> Integrating a Custom Editor Widget |
| 229 | |
| 230 | (These instructions also work for the `/wikiedit` page by eplacing |
| 231 | "fileedit" with "wikiedit" in any strings or symbol names!) |
| 232 | |
| 233 | It is possible to replace `/filepage`'s basic text-editing widget (a |
| 234 |
+1
-1
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -936,11 +936,11 @@ | ||
| 936 | 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | 940 | lightweight web server, |
| 941 | - <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>, | |
| 941 | + <tt>[https://sqlite.org/althttpd/|althttpd]</tt>, | |
| 942 | 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | 943 | these sites. |
| 944 | 944 | |
| 945 | 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 | 947 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -936,11 +936,11 @@ | |
| 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | lightweight web server, |
| 941 | <tt>[https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md|althttpd]</tt>, |
| 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | these sites. |
| 944 | |
| 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -936,11 +936,11 @@ | |
| 936 | isn't normally synchronized with a "<tt>fossil clone</tt>" command unless |
| 937 | you add the "-u" option. (See "[./aboutdownload.wiki|How the |
| 938 | Download Page Works]" for details.) There may also be some purely |
| 939 | static elements of the web site served via D. Richard Hipp's own |
| 940 | lightweight web server, |
| 941 | <tt>[https://sqlite.org/althttpd/|althttpd]</tt>, |
| 942 | which is configured as a front end to Fossil running in CGI mode on |
| 943 | these sites. |
| 944 | |
| 945 | <li><p>That estimate is based on pricing at Digital Ocean in |
| 946 | mid-2019: Fossil will run just fine on the smallest instance they |
| 947 |
+2
-2
| --- www/hashpolicy.wiki | ||
| +++ www/hashpolicy.wiki | ||
| @@ -51,15 +51,15 @@ | ||
| 51 | 51 | The Hardened SHA1 algorithm automatically detects when the artifact |
| 52 | 52 | being hashed is specifically designed to exploit the known weaknesses |
| 53 | 53 | in the SHA1 algorithm, and when it detects such an attack it changes |
| 54 | 54 | the hash algorithm (by increasing the number of rounds in the compression |
| 55 | 55 | function) to make the algorithm secure again. If the attack detection |
| 56 | -gets a false possible, that means that Hardened SHA1 will get a different | |
| 56 | +gets a false-positive, that means that Hardened SHA1 will get a different | |
| 57 | 57 | answer than the standard FIPS PUB 180-4 SHA1, but the creators of |
| 58 | 58 | Hardened SHA1 (see the second paper |
| 59 | 59 | [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]) |
| 60 | -report that the probability of a false positive is vanishingly small - | |
| 60 | +report that the probability of a false-positive is vanishingly small - | |
| 61 | 61 | less than 1 false positive out of 10<sup><font size=1>27</font></sup> |
| 62 | 62 | hashes. |
| 63 | 63 | |
| 64 | 64 | Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that |
| 65 | 65 | much hashing, so performance is not really an issue. |
| 66 | 66 |
| --- www/hashpolicy.wiki | |
| +++ www/hashpolicy.wiki | |
| @@ -51,15 +51,15 @@ | |
| 51 | The Hardened SHA1 algorithm automatically detects when the artifact |
| 52 | being hashed is specifically designed to exploit the known weaknesses |
| 53 | in the SHA1 algorithm, and when it detects such an attack it changes |
| 54 | the hash algorithm (by increasing the number of rounds in the compression |
| 55 | function) to make the algorithm secure again. If the attack detection |
| 56 | gets a false possible, that means that Hardened SHA1 will get a different |
| 57 | answer than the standard FIPS PUB 180-4 SHA1, but the creators of |
| 58 | Hardened SHA1 (see the second paper |
| 59 | [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]) |
| 60 | report that the probability of a false positive is vanishingly small - |
| 61 | less than 1 false positive out of 10<sup><font size=1>27</font></sup> |
| 62 | hashes. |
| 63 | |
| 64 | Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that |
| 65 | much hashing, so performance is not really an issue. |
| 66 |
| --- www/hashpolicy.wiki | |
| +++ www/hashpolicy.wiki | |
| @@ -51,15 +51,15 @@ | |
| 51 | The Hardened SHA1 algorithm automatically detects when the artifact |
| 52 | being hashed is specifically designed to exploit the known weaknesses |
| 53 | in the SHA1 algorithm, and when it detects such an attack it changes |
| 54 | the hash algorithm (by increasing the number of rounds in the compression |
| 55 | function) to make the algorithm secure again. If the attack detection |
| 56 | gets a false-positive, that means that Hardened SHA1 will get a different |
| 57 | answer than the standard FIPS PUB 180-4 SHA1, but the creators of |
| 58 | Hardened SHA1 (see the second paper |
| 59 | [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]) |
| 60 | report that the probability of a false-positive is vanishingly small - |
| 61 | less than 1 false positive out of 10<sup><font size=1>27</font></sup> |
| 62 | hashes. |
| 63 | |
| 64 | Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that |
| 65 | much hashing, so performance is not really an issue. |
| 66 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -148,10 +148,11 @@ | ||
| 148 | 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | +<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | |
| 153 | 154 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 154 | 155 | book</a> |
| 155 | 156 | </ul> |
| 156 | 157 | <a name="pindex"></a> |
| 157 | 158 | <h2>Permuted Index:</h2> |
| 158 | 159 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -148,10 +148,11 @@ | |
| 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 154 | book</a> |
| 155 | </ul> |
| 156 | <a name="pindex"></a> |
| 157 | <h2>Permuted Index:</h2> |
| 158 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -148,10 +148,11 @@ | |
| 148 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 149 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 150 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 151 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 152 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 153 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 154 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 155 | book</a> |
| 156 | </ul> |
| 157 | <a name="pindex"></a> |
| 158 | <h2>Permuted Index:</h2> |
| 159 |
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -13,10 +13,11 @@ | ||
| 13 | 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | +<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> | |
| 18 | 19 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 19 | 20 | book</a> |
| 20 | 21 | </ul> |
| 21 | 22 | <a name="pindex"></a> |
| 22 | 23 | <h2>Permuted Index:</h2> |
| 23 | 24 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -13,10 +13,11 @@ | |
| 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 19 | book</a> |
| 20 | </ul> |
| 21 | <a name="pindex"></a> |
| 22 | <h2>Permuted Index:</h2> |
| 23 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -13,10 +13,11 @@ | |
| 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | <li> <a href='build.wiki'>Compiling and installing Fossil</a> |
| 15 | <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> |
| 16 | <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> |
| 17 | <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> |
| 18 | <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> |
| 19 | <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's |
| 20 | book</a> |
| 21 | </ul> |
| 22 | <a name="pindex"></a> |
| 23 | <h2>Permuted Index:</h2> |
| 24 |
+1
-1
| --- www/server/any/althttpd.md | ||
| +++ www/server/any/althttpd.md | ||
| @@ -36,7 +36,7 @@ | ||
| 36 | 36 | you created to start using your Fossil server. |
| 37 | 37 | |
| 38 | 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | 39 | |
| 40 | 40 | |
| 41 | -[althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md | |
| 41 | +[althttpd]: https://sqlite.org/althttpd/ | |
| 42 | 42 | [cgi]: ../../cgi.wiki |
| 43 | 43 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -36,7 +36,7 @@ | |
| 36 | you created to start using your Fossil server. |
| 37 | |
| 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | |
| 40 | |
| 41 | [althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md |
| 42 | [cgi]: ../../cgi.wiki |
| 43 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -36,7 +36,7 @@ | |
| 36 | you created to start using your Fossil server. |
| 37 | |
| 38 | *[Return to the top-level Fossil server article.](../)* |
| 39 | |
| 40 | |
| 41 | [althttpd]: https://sqlite.org/althttpd/ |
| 42 | [cgi]: ../../cgi.wiki |
| 43 |
+29
-8
| --- www/server/debian/nginx.md | ||
| +++ www/server/debian/nginx.md | ||
| @@ -217,25 +217,46 @@ | ||
| 217 | 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | 220 | slash, as some other HTTP servers will. |
| 221 | 221 | |
| 222 | + | |
| 223 | +## <a name="large-uv"></a> Allowing Large Unversioned Files | |
| 224 | + | |
| 225 | +By default, nginx only accepts HTTP messages [up to a | |
| 226 | +meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) | |
| 227 | +in size. Fossil chunks its sync protocol such that this is not normally | |
| 228 | +a problem, but when sending [unversioned content][uv], it uses a single | |
| 229 | +message for the entire file. Therefore, if you will be storing files | |
| 230 | +larger than this limit as unversioned content, you need to raise the | |
| 231 | +limit. Within the `location` block: | |
| 232 | + | |
| 233 | + # Allow large unversioned file uploads, such as PDFs | |
| 234 | + client_max_body_size 20M; | |
| 235 | + | |
| 236 | +[uv]: ../../unvers.wiki | |
| 237 | + | |
| 222 | 238 | |
| 223 | 239 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 224 | 240 | |
| 225 | -You can have `fail2ban` recognize attacks and automatically block them, | |
| 226 | -but the stock configuration doesn’t work with our Fossil setup above, so | |
| 227 | -we have to do a bit of local adjustment. | |
| 241 | +One of the nice things that falls out of proxying Fossil behind nginx is | |
| 242 | +that it makes it easier to configure `fail2ban` to recognize attacks on | |
| 243 | +Fossil and automatically block them. Fossil logs the sorts of errors we | |
| 244 | +want to detect, but it does so in places like the repository’s admin | |
| 245 | +log, a SQL table, which `fail2ban` doesn’t know how to query. By putting | |
| 246 | +Fossil behind an nginx proxy, we convert these failures to log file | |
| 247 | +form, which `fail2ban` is designed to handle. | |
| 228 | 248 | |
| 229 | -First, install it: | |
| 249 | +First, install `fail2ban`, if you haven’t already: | |
| 230 | 250 | |
| 231 | 251 | sudo apt install fail2ban |
| 232 | 252 | |
| 233 | -Out of the box, you get SSH monitoring only. There are nginx monitors | |
| 234 | -included with the package, but they don’t look in the right places for | |
| 235 | -the right things. We’d like it to react to Fossil `/login` failures, for | |
| 236 | -example. Put the following into | |
| 253 | +We’d like `fail2ban` to react to Fossil `/login` failures. The stock | |
| 254 | +configuration of `fail2ban` only detects a few common sorts of SSH | |
| 255 | +attacks by default, and its included (but disabled) nginx attack | |
| 256 | +detectors don’t include one that knows how to detect an attack on | |
| 257 | +Fossil. We have to teach it by putting the following into | |
| 237 | 258 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 238 | 259 | |
| 239 | 260 | [Definition] |
| 240 | 261 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 241 | 262 | |
| 242 | 263 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -217,25 +217,46 @@ | |
| 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | slash, as some other HTTP servers will. |
| 221 | |
| 222 | |
| 223 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 224 | |
| 225 | You can have `fail2ban` recognize attacks and automatically block them, |
| 226 | but the stock configuration doesn’t work with our Fossil setup above, so |
| 227 | we have to do a bit of local adjustment. |
| 228 | |
| 229 | First, install it: |
| 230 | |
| 231 | sudo apt install fail2ban |
| 232 | |
| 233 | Out of the box, you get SSH monitoring only. There are nginx monitors |
| 234 | included with the package, but they don’t look in the right places for |
| 235 | the right things. We’d like it to react to Fossil `/login` failures, for |
| 236 | example. Put the following into |
| 237 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 238 | |
| 239 | [Definition] |
| 240 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 241 | |
| 242 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -217,25 +217,46 @@ | |
| 217 | The most common thing people get wrong when hand-rolling a configuration |
| 218 | like this is to get the slashes wrong. Fossil is sensitive to this. For |
| 219 | instance, Fossil will not collapse double slashes down to a single |
| 220 | slash, as some other HTTP servers will. |
| 221 | |
| 222 | |
| 223 | ## <a name="large-uv"></a> Allowing Large Unversioned Files |
| 224 | |
| 225 | By default, nginx only accepts HTTP messages [up to a |
| 226 | meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) |
| 227 | in size. Fossil chunks its sync protocol such that this is not normally |
| 228 | a problem, but when sending [unversioned content][uv], it uses a single |
| 229 | message for the entire file. Therefore, if you will be storing files |
| 230 | larger than this limit as unversioned content, you need to raise the |
| 231 | limit. Within the `location` block: |
| 232 | |
| 233 | # Allow large unversioned file uploads, such as PDFs |
| 234 | client_max_body_size 20M; |
| 235 | |
| 236 | [uv]: ../../unvers.wiki |
| 237 | |
| 238 | |
| 239 | ## <a name="fail2ban"></a> Integrating `fail2ban` |
| 240 | |
| 241 | One of the nice things that falls out of proxying Fossil behind nginx is |
| 242 | that it makes it easier to configure `fail2ban` to recognize attacks on |
| 243 | Fossil and automatically block them. Fossil logs the sorts of errors we |
| 244 | want to detect, but it does so in places like the repository’s admin |
| 245 | log, a SQL table, which `fail2ban` doesn’t know how to query. By putting |
| 246 | Fossil behind an nginx proxy, we convert these failures to log file |
| 247 | form, which `fail2ban` is designed to handle. |
| 248 | |
| 249 | First, install `fail2ban`, if you haven’t already: |
| 250 | |
| 251 | sudo apt install fail2ban |
| 252 | |
| 253 | We’d like `fail2ban` to react to Fossil `/login` failures. The stock |
| 254 | configuration of `fail2ban` only detects a few common sorts of SSH |
| 255 | attacks by default, and its included (but disabled) nginx attack |
| 256 | detectors don’t include one that knows how to detect an attack on |
| 257 | Fossil. We have to teach it by putting the following into |
| 258 | `/etc/fail2ban/filter.d/nginx-fossil-login.conf`: |
| 259 | |
| 260 | [Definition] |
| 261 | failregex = ^<HOST> - .*POST .*/login HTTP/..." 401 |
| 262 | |
| 263 |
| --- www/server/openbsd/fastcgi.md | ||
| +++ www/server/openbsd/fastcgi.md | ||
| @@ -159,15 +159,27 @@ | ||
| 159 | 159 | root "/acme" |
| 160 | 160 | request strip 2 |
| 161 | 161 | } |
| 162 | 162 | } |
| 163 | 163 | ``` |
| 164 | + | |
| 165 | +[The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server | |
| 166 | +is 1 MiB. Fossil chunks its sync protocol such that this is not | |
| 167 | +normally a problem, but when sending [unversioned content][uv], it uses | |
| 168 | +a single message for the entire file. Therefore, if you will be storing | |
| 169 | +files larger than this limit as unversioned content, you need to raise | |
| 170 | +the limit as we’ve done above with the “`connection max request body`” | |
| 171 | +setting, raising the limit to 100 MiB. | |
| 172 | + | |
| 173 | +[dlim]: https://man.openbsd.org/httpd.conf.5#connection | |
| 174 | +[uv]: ../../unvers.wiki | |
| 164 | 175 | |
| 165 | 176 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 166 | 177 | out the `https` server block and proceed to securing a free |
| 167 | 178 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 168 | 179 | [Start `httpd`](#starthttpd). |
| 180 | + | |
| 169 | 181 | |
| 170 | 182 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 171 | 183 | |
| 172 | 184 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 173 | 185 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 174 | 186 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -159,15 +159,27 @@ | |
| 159 | root "/acme" |
| 160 | request strip 2 |
| 161 | } |
| 162 | } |
| 163 | ``` |
| 164 | |
| 165 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 166 | out the `https` server block and proceed to securing a free |
| 167 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 168 | [Start `httpd`](#starthttpd). |
| 169 | |
| 170 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 171 | |
| 172 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 173 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 174 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -159,15 +159,27 @@ | |
| 159 | root "/acme" |
| 160 | request strip 2 |
| 161 | } |
| 162 | } |
| 163 | ``` |
| 164 | |
| 165 | [The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server |
| 166 | is 1 MiB. Fossil chunks its sync protocol such that this is not |
| 167 | normally a problem, but when sending [unversioned content][uv], it uses |
| 168 | a single message for the entire file. Therefore, if you will be storing |
| 169 | files larger than this limit as unversioned content, you need to raise |
| 170 | the limit as we’ve done above with the “`connection max request body`” |
| 171 | setting, raising the limit to 100 MiB. |
| 172 | |
| 173 | [dlim]: https://man.openbsd.org/httpd.conf.5#connection |
| 174 | [uv]: ../../unvers.wiki |
| 175 | |
| 176 | **NOTE:** If not already in possession of a HTTPS certificate, comment |
| 177 | out the `https` server block and proceed to securing a free |
| 178 | [Let's Encrypt Certificate](#letsencrypt); otherwise skip to |
| 179 | [Start `httpd`](#starthttpd). |
| 180 | |
| 181 | |
| 182 | ## <a name="letsencrypt"></a>Let's Encrypt Certificate |
| 183 | |
| 184 | In order for `httpd` to serve HTTPS, secure a free certificate from |
| 185 | Let's Encrypt using `acme-client`. Before issuing the request, however, |
| 186 |
+1
-1
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -12,11 +12,11 @@ | ||
| 12 | 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | 14 | is unimportant. It is assumed that the hash names |
| 15 | 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | -hash values for available artifacts, then sharing the content of artifacts | |
| 17 | +of hashes for available artifacts, then sharing the content of artifacts | |
| 18 | 18 | whose names are missing from one side or the other of the connection. |
| 19 | 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | 21 | employed that usually reduce the number of hashes that need to be |
| 22 | 22 | shared to a few hundred.</p> |
| 23 | 23 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -12,11 +12,11 @@ | |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | is unimportant. It is assumed that the hash names |
| 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | hash values for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred.</p> |
| 23 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -12,11 +12,11 @@ | |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| 14 | is unimportant. It is assumed that the hash names |
| 15 | of artifacts are unique - that every artifact has a different hash. |
| 16 | To a first approximation, synchronization proceeds by sharing lists |
| 17 | of hashes for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred.</p> |
| 23 |