Fossil SCM
Merge trunk, since cloning local filesystem repositories still does not work
Commit
719dcd29cd8a99d592793211fe60f6c0d39a450087ab5aa440ed193372802a01
Parent
cbd52334efe299e…
99 files changed
+1
-1
+4
-1
+1
-1
+2
-2
+16
-16
+15
-2
+1
+1
-1
+2
-1
+5
-6
+1
-1
+459
-10
+2
-16
+8
-8
+5
-5
+3
-3
+9
-7
-1
+4
-1
+1
-1
+131
-19
+375
-21
+2
-2
+3
-3
+80
-8
+16
-11
+4
-1
+1
-1
+26
+221
-144
+2
-2
+7
-11
+1
-1
+33
-3
+41
-8
+3
+93
+23
-9
+8
+126
-104
+46
-10
+43
+65
+45
-8
+1
-1
+33
+1
-1
+1
-1
+1
-1
+104
-48
+2
+5
-4
+50
-4
+9
-13
+13
-5
+7
-1
+4
-1
+2
-2
-11
-6
-6
+9
-1
+1
-1
+58
-12
+1
-1
+2
+2
+54
-22
+1
-1
+9
+772
-531
+1
-1
-13
+28
-10
+69
-231
+47
-190
+185
+5
-4
+5
-4
+6
-2
+1
-1
+2
-2
+716
-236
-328
+2
-2
+6
-6
+1
-1
+5
-5
+1
+6
+4
-10
+9
-13
+15
-12
+19
-3
+1
-1
+1
-1
+10
-6
~
VERSION
~
skins/ardoise/css.txt
~
skins/eagle/css.txt
~
skins/xekri/css.txt
~
src/add.c
~
src/ajax.c
~
src/allrepo.c
~
src/attach.c
~
src/bisect.c
~
src/blob.c
~
src/branch.c
~
src/browse.c
~
src/builtin.c
~
src/bundle.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/clone.c
~
src/codecheck1.c
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/default.css
~
src/descendants.c
~
src/diff.c
~
src/dispatch.c
~
src/doc.c
~
src/etag.c
~
src/export.c
~
src/file.c
~
src/fileedit.c
~
src/finfo.c
~
src/forum.c
~
src/forum.js
~
src/fossil.bootstrap.js
~
src/fossil.confirmer.js
~
src/fossil.copybutton.js
~
src/fossil.dom.js
~
src/fossil.fetch.js
~
src/fossil.numbered-lines.js
~
src/fossil.page.fileedit.js
~
src/fossil.page.forumpost.js
~
src/fossil.page.wikiedit.js
~
src/fossil.popupwidget.js
~
src/fossil.tabs.js
~
src/fusefs.c
~
src/glob.c
~
src/graph.js
~
src/hook.c
~
src/import.c
~
src/info.c
~
src/json_config.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/makemake.tcl
~
src/mkbuiltin.c
~
src/mkindex.c
~
src/piechart.c
~
src/publish.c
~
src/purge.c
~
src/rebuild.c
~
src/rebuild.c
~
src/sbsdiff.js
~
src/scroll.js
~
src/setup.c
~
src/setupuser.c
~
src/sha1.c
~
src/sha3.c
~
src/shell.c
~
src/skins.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stash.c
~
src/stat.c
~
src/style.c
~
src/style.fileedit.css
~
src/style.wikiedit.css
~
src/sync.c
~
src/sync.c
~
src/timeline.c
~
src/undo.c
~
src/update.c
~
src/wiki.c
-
src/wysiwyg.c
~
src/xfer.c
~
test/merge1.test
~
test/merge3.test
~
test/merge4.test
~
test/tester.tcl
~
test/wiki.test
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.msc
~
www/changes.wiki
~
www/fileedit-page.md
~
www/gitusers.md
~
www/server/openbsd/fastcgi.md
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.12 | |
| 1 | +2.13 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.12 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.13 |
| 2 |
+4
-1
| --- skins/ardoise/css.txt | ||
| +++ skins/ardoise/css.txt | ||
| @@ -497,11 +497,11 @@ | ||
| 497 | 497 | border: 0; |
| 498 | 498 | border-radius: 5px; |
| 499 | 499 | box-shadow: none; |
| 500 | 500 | box-sizing: border-box |
| 501 | 501 | } |
| 502 | -textarea { | |
| 502 | +textarea, select { | |
| 503 | 503 | height: initial; |
| 504 | 504 | } |
| 505 | 505 | input[type=email]:hover, |
| 506 | 506 | input[type=number]:hover, |
| 507 | 507 | input[type=password]:hover, |
| @@ -572,10 +572,13 @@ | ||
| 572 | 572 | white-space: nowrap; |
| 573 | 573 | background: #000; |
| 574 | 574 | border: 2px solid #bbb; |
| 575 | 575 | border-radius: 5px |
| 576 | 576 | } |
| 577 | +table.numbered-lines td.file-content > pre { | |
| 578 | + margin-top: -2px/*offset CODE tag border*/; | |
| 579 | +} | |
| 577 | 580 | pre > code { |
| 578 | 581 | padding: 1rem 1.5rem; |
| 579 | 582 | white-space: pre |
| 580 | 583 | } |
| 581 | 584 | td, |
| 582 | 585 |
| --- skins/ardoise/css.txt | |
| +++ skins/ardoise/css.txt | |
| @@ -497,11 +497,11 @@ | |
| 497 | border: 0; |
| 498 | border-radius: 5px; |
| 499 | box-shadow: none; |
| 500 | box-sizing: border-box |
| 501 | } |
| 502 | textarea { |
| 503 | height: initial; |
| 504 | } |
| 505 | input[type=email]:hover, |
| 506 | input[type=number]:hover, |
| 507 | input[type=password]:hover, |
| @@ -572,10 +572,13 @@ | |
| 572 | white-space: nowrap; |
| 573 | background: #000; |
| 574 | border: 2px solid #bbb; |
| 575 | border-radius: 5px |
| 576 | } |
| 577 | pre > code { |
| 578 | padding: 1rem 1.5rem; |
| 579 | white-space: pre |
| 580 | } |
| 581 | td, |
| 582 |
| --- skins/ardoise/css.txt | |
| +++ skins/ardoise/css.txt | |
| @@ -497,11 +497,11 @@ | |
| 497 | border: 0; |
| 498 | border-radius: 5px; |
| 499 | box-shadow: none; |
| 500 | box-sizing: border-box |
| 501 | } |
| 502 | textarea, select { |
| 503 | height: initial; |
| 504 | } |
| 505 | input[type=email]:hover, |
| 506 | input[type=number]:hover, |
| 507 | input[type=password]:hover, |
| @@ -572,10 +572,13 @@ | |
| 572 | white-space: nowrap; |
| 573 | background: #000; |
| 574 | border: 2px solid #bbb; |
| 575 | border-radius: 5px |
| 576 | } |
| 577 | table.numbered-lines td.file-content > pre { |
| 578 | margin-top: -2px/*offset CODE tag border*/; |
| 579 | } |
| 580 | pre > code { |
| 581 | padding: 1rem 1.5rem; |
| 582 | white-space: pre |
| 583 | } |
| 584 | td, |
| 585 |
+1
-1
| --- skins/eagle/css.txt | ||
| +++ skins/eagle/css.txt | ||
| @@ -399,11 +399,11 @@ | ||
| 399 | 399 | |
| 400 | 400 | div.filetreeline:hover { |
| 401 | 401 | background-color: #7EA2D9; |
| 402 | 402 | } |
| 403 | 403 | |
| 404 | -div.selectedText { | |
| 404 | +table.numbered-lines td.line-numbers span.selected-line { | |
| 405 | 405 | background-color: #7EA2D9; |
| 406 | 406 | } |
| 407 | 407 | |
| 408 | 408 | .statistics-report-graph-line { |
| 409 | 409 | background-color: #7EA2D9; |
| 410 | 410 |
| --- skins/eagle/css.txt | |
| +++ skins/eagle/css.txt | |
| @@ -399,11 +399,11 @@ | |
| 399 | |
| 400 | div.filetreeline:hover { |
| 401 | background-color: #7EA2D9; |
| 402 | } |
| 403 | |
| 404 | div.selectedText { |
| 405 | background-color: #7EA2D9; |
| 406 | } |
| 407 | |
| 408 | .statistics-report-graph-line { |
| 409 | background-color: #7EA2D9; |
| 410 |
| --- skins/eagle/css.txt | |
| +++ skins/eagle/css.txt | |
| @@ -399,11 +399,11 @@ | |
| 399 | |
| 400 | div.filetreeline:hover { |
| 401 | background-color: #7EA2D9; |
| 402 | } |
| 403 | |
| 404 | table.numbered-lines td.line-numbers span.selected-line { |
| 405 | background-color: #7EA2D9; |
| 406 | } |
| 407 | |
| 408 | .statistics-report-graph-line { |
| 409 | background-color: #7EA2D9; |
| 410 |
+2
-2
| --- skins/xekri/css.txt | ||
| +++ skins/xekri/css.txt | ||
| @@ -1000,15 +1000,15 @@ | ||
| 1000 | 1000 | /************************************** |
| 1001 | 1001 | * Did not encounter these |
| 1002 | 1002 | */ |
| 1003 | 1003 | |
| 1004 | 1004 | /* selected lines of text within a linenumbered artifact display */ |
| 1005 | -div.selectedText { | |
| 1005 | +table.numbered-lines td.line-numbers span.selected-line { | |
| 1006 | 1006 | font-weight: bold; |
| 1007 | 1007 | color: #00f; |
| 1008 | 1008 | background-color: #d5d5ff; |
| 1009 | - border: 1px #00f solid; | |
| 1009 | + border-color: #00f; | |
| 1010 | 1010 | } |
| 1011 | 1011 | |
| 1012 | 1012 | /* format for missing privileges note on user setup page */ |
| 1013 | 1013 | p.missingPriv { |
| 1014 | 1014 | color: #00f; |
| 1015 | 1015 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -1000,15 +1000,15 @@ | |
| 1000 | /************************************** |
| 1001 | * Did not encounter these |
| 1002 | */ |
| 1003 | |
| 1004 | /* selected lines of text within a linenumbered artifact display */ |
| 1005 | div.selectedText { |
| 1006 | font-weight: bold; |
| 1007 | color: #00f; |
| 1008 | background-color: #d5d5ff; |
| 1009 | border: 1px #00f solid; |
| 1010 | } |
| 1011 | |
| 1012 | /* format for missing privileges note on user setup page */ |
| 1013 | p.missingPriv { |
| 1014 | color: #00f; |
| 1015 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -1000,15 +1000,15 @@ | |
| 1000 | /************************************** |
| 1001 | * Did not encounter these |
| 1002 | */ |
| 1003 | |
| 1004 | /* selected lines of text within a linenumbered artifact display */ |
| 1005 | table.numbered-lines td.line-numbers span.selected-line { |
| 1006 | font-weight: bold; |
| 1007 | color: #00f; |
| 1008 | background-color: #d5d5ff; |
| 1009 | border-color: #00f; |
| 1010 | } |
| 1011 | |
| 1012 | /* format for missing privileges note on user setup page */ |
| 1013 | p.missingPriv { |
| 1014 | color: #00f; |
| 1015 |
+16
-16
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -320,11 +320,11 @@ | ||
| 320 | 320 | ** COMMAND: add |
| 321 | 321 | ** |
| 322 | 322 | ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? |
| 323 | 323 | ** |
| 324 | 324 | ** Make arrangements to add one or more files or directories to the |
| 325 | -** current checkout at the next commit. | |
| 325 | +** current checkout at the next [[commit]]. | |
| 326 | 326 | ** |
| 327 | 327 | ** When adding files or directories recursively, filenames that begin |
| 328 | 328 | ** with "." are excluded by default. To include such files, add |
| 329 | 329 | ** the "--dotfiles" option to the command-line. |
| 330 | 330 | ** |
| @@ -359,11 +359,11 @@ | ||
| 359 | 359 | ** |
| 360 | 360 | ** The following options are only valid with --reset: |
| 361 | 361 | ** -v|--verbose Outputs information about each --reset file. |
| 362 | 362 | ** -n|--dry-run Display instead of run actions. |
| 363 | 363 | ** |
| 364 | -** See also: addremove, rm | |
| 364 | +** See also: [[addremove]], [[rm]] | |
| 365 | 365 | */ |
| 366 | 366 | void add_cmd(void){ |
| 367 | 367 | int i; /* Loop counter */ |
| 368 | 368 | int vid; /* Currently checked out version */ |
| 369 | 369 | int nRoot; /* Full path characters in g.zLocalRoot */ |
| @@ -541,11 +541,11 @@ | ||
| 541 | 541 | ** than --verbose or --dry-run may be used with |
| 542 | 542 | ** --reset. |
| 543 | 543 | ** --verbose|-v Outputs information about each --reset file. |
| 544 | 544 | ** Only usable with --reset. |
| 545 | 545 | ** |
| 546 | -** See also: addremove, add | |
| 546 | +** See also: [[addremove]], [[add]] | |
| 547 | 547 | */ |
| 548 | 548 | void delete_cmd(void){ |
| 549 | 549 | int i; |
| 550 | 550 | int removeFiles; |
| 551 | 551 | int dryRunFlag = find_option("dry-run","n",0)!=0; |
| @@ -689,22 +689,22 @@ | ||
| 689 | 689 | /* |
| 690 | 690 | ** COMMAND: addremove |
| 691 | 691 | ** |
| 692 | 692 | ** Usage: %fossil addremove ?OPTIONS? |
| 693 | 693 | ** |
| 694 | -** Do all necessary "add" and "rm" commands to synchronize the repository | |
| 695 | -** with the content of the working checkout: | |
| 694 | +** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the | |
| 695 | +** repository with the content of the working checkout: | |
| 696 | 696 | ** |
| 697 | 697 | ** * All files in the checkout but not in the repository (that is, |
| 698 | 698 | ** all files displayed using the "extras" command) are added as |
| 699 | -** if by the "add" command. | |
| 699 | +** if by the "[[add]]" command. | |
| 700 | 700 | ** |
| 701 | 701 | ** * All files in the repository but missing from the checkout (that is, |
| 702 | 702 | ** all files that show as MISSING with the "status" command) are |
| 703 | -** removed as if by the "rm" command. | |
| 703 | +** removed as if by the "[[rm]]" command. | |
| 704 | 704 | ** |
| 705 | -** The command does not "commit". You must run the "commit" separately | |
| 705 | +** The command does not "[[commit]]". You must run the "[[commit]]" separately | |
| 706 | 706 | ** as a separate step. |
| 707 | 707 | ** |
| 708 | 708 | ** Files and directories whose names begin with "." are ignored unless |
| 709 | 709 | ** the --dotfiles option is used. |
| 710 | 710 | ** |
| @@ -733,11 +733,11 @@ | ||
| 733 | 733 | ** removed. No flags other than --verbose and |
| 734 | 734 | ** --dry-run may be used with --reset. |
| 735 | 735 | ** --verbose|-v Outputs information about each --reset file. |
| 736 | 736 | ** Only usable with --reset. |
| 737 | 737 | ** |
| 738 | -** See also: add, rm | |
| 738 | +** See also: [[add]], [[rm]] | |
| 739 | 739 | */ |
| 740 | 740 | void addremove_cmd(void){ |
| 741 | 741 | Blob path; |
| 742 | 742 | const char *zCleanFlag; |
| 743 | 743 | const char *zIgnoreFlag; |
| @@ -953,11 +953,11 @@ | ||
| 953 | 953 | ** Move or rename one or more files or directories within the repository tree. |
| 954 | 954 | ** You can either rename a file or directory or move it to another subdirectory. |
| 955 | 955 | ** |
| 956 | 956 | ** The 'mv' command does NOT normally rename or move the files on disk. |
| 957 | 957 | ** This command merely records the fact that file names have changed so |
| 958 | -** that appropriate notations can be made at the next commit/check-in. | |
| 958 | +** that appropriate notations can be made at the next [[commit]]. | |
| 959 | 959 | ** However, the default behavior of this command may be overridden via |
| 960 | 960 | ** command line options listed below and/or the 'mv-rm-files' setting. |
| 961 | 961 | ** |
| 962 | 962 | ** The 'rename' command never renames or moves files on disk, even when the |
| 963 | 963 | ** command line options and/or the 'mv-rm-files' setting would otherwise |
| @@ -966,17 +966,17 @@ | ||
| 966 | 966 | ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" |
| 967 | 967 | ** setting is non-zero, files WILL BE renamed or moved on disk |
| 968 | 968 | ** as well. This does NOT apply to the 'rename' command. |
| 969 | 969 | ** |
| 970 | 970 | ** Options: |
| 971 | -** --soft Skip moving files within the checkout. | |
| 972 | -** This supersedes the --hard option. | |
| 973 | -** --hard Move files within the checkout. | |
| 974 | -** --case-sensitive <BOOL> Override the case-sensitive setting. | |
| 975 | -** -n|--dry-run If given, display instead of run actions. | |
| 971 | +** --soft Skip moving files within the checkout. | |
| 972 | +** This supersedes the --hard option. | |
| 973 | +** --hard Move files within the checkout. | |
| 974 | +** --case-sensitive <BOOL> Override the case-sensitive setting. | |
| 975 | +** -n|--dry-run If given, display instead of run actions. | |
| 976 | 976 | ** |
| 977 | -** See also: changes, status | |
| 977 | +** See also: [[changes]], [[status]] | |
| 978 | 978 | */ |
| 979 | 979 | void mv_cmd(void){ |
| 980 | 980 | int i; |
| 981 | 981 | int vid; |
| 982 | 982 | int moveFiles; |
| 983 | 983 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -320,11 +320,11 @@ | |
| 320 | ** COMMAND: add |
| 321 | ** |
| 322 | ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? |
| 323 | ** |
| 324 | ** Make arrangements to add one or more files or directories to the |
| 325 | ** current checkout at the next commit. |
| 326 | ** |
| 327 | ** When adding files or directories recursively, filenames that begin |
| 328 | ** with "." are excluded by default. To include such files, add |
| 329 | ** the "--dotfiles" option to the command-line. |
| 330 | ** |
| @@ -359,11 +359,11 @@ | |
| 359 | ** |
| 360 | ** The following options are only valid with --reset: |
| 361 | ** -v|--verbose Outputs information about each --reset file. |
| 362 | ** -n|--dry-run Display instead of run actions. |
| 363 | ** |
| 364 | ** See also: addremove, rm |
| 365 | */ |
| 366 | void add_cmd(void){ |
| 367 | int i; /* Loop counter */ |
| 368 | int vid; /* Currently checked out version */ |
| 369 | int nRoot; /* Full path characters in g.zLocalRoot */ |
| @@ -541,11 +541,11 @@ | |
| 541 | ** than --verbose or --dry-run may be used with |
| 542 | ** --reset. |
| 543 | ** --verbose|-v Outputs information about each --reset file. |
| 544 | ** Only usable with --reset. |
| 545 | ** |
| 546 | ** See also: addremove, add |
| 547 | */ |
| 548 | void delete_cmd(void){ |
| 549 | int i; |
| 550 | int removeFiles; |
| 551 | int dryRunFlag = find_option("dry-run","n",0)!=0; |
| @@ -689,22 +689,22 @@ | |
| 689 | /* |
| 690 | ** COMMAND: addremove |
| 691 | ** |
| 692 | ** Usage: %fossil addremove ?OPTIONS? |
| 693 | ** |
| 694 | ** Do all necessary "add" and "rm" commands to synchronize the repository |
| 695 | ** with the content of the working checkout: |
| 696 | ** |
| 697 | ** * All files in the checkout but not in the repository (that is, |
| 698 | ** all files displayed using the "extras" command) are added as |
| 699 | ** if by the "add" command. |
| 700 | ** |
| 701 | ** * All files in the repository but missing from the checkout (that is, |
| 702 | ** all files that show as MISSING with the "status" command) are |
| 703 | ** removed as if by the "rm" command. |
| 704 | ** |
| 705 | ** The command does not "commit". You must run the "commit" separately |
| 706 | ** as a separate step. |
| 707 | ** |
| 708 | ** Files and directories whose names begin with "." are ignored unless |
| 709 | ** the --dotfiles option is used. |
| 710 | ** |
| @@ -733,11 +733,11 @@ | |
| 733 | ** removed. No flags other than --verbose and |
| 734 | ** --dry-run may be used with --reset. |
| 735 | ** --verbose|-v Outputs information about each --reset file. |
| 736 | ** Only usable with --reset. |
| 737 | ** |
| 738 | ** See also: add, rm |
| 739 | */ |
| 740 | void addremove_cmd(void){ |
| 741 | Blob path; |
| 742 | const char *zCleanFlag; |
| 743 | const char *zIgnoreFlag; |
| @@ -953,11 +953,11 @@ | |
| 953 | ** Move or rename one or more files or directories within the repository tree. |
| 954 | ** You can either rename a file or directory or move it to another subdirectory. |
| 955 | ** |
| 956 | ** The 'mv' command does NOT normally rename or move the files on disk. |
| 957 | ** This command merely records the fact that file names have changed so |
| 958 | ** that appropriate notations can be made at the next commit/check-in. |
| 959 | ** However, the default behavior of this command may be overridden via |
| 960 | ** command line options listed below and/or the 'mv-rm-files' setting. |
| 961 | ** |
| 962 | ** The 'rename' command never renames or moves files on disk, even when the |
| 963 | ** command line options and/or the 'mv-rm-files' setting would otherwise |
| @@ -966,17 +966,17 @@ | |
| 966 | ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" |
| 967 | ** setting is non-zero, files WILL BE renamed or moved on disk |
| 968 | ** as well. This does NOT apply to the 'rename' command. |
| 969 | ** |
| 970 | ** Options: |
| 971 | ** --soft Skip moving files within the checkout. |
| 972 | ** This supersedes the --hard option. |
| 973 | ** --hard Move files within the checkout. |
| 974 | ** --case-sensitive <BOOL> Override the case-sensitive setting. |
| 975 | ** -n|--dry-run If given, display instead of run actions. |
| 976 | ** |
| 977 | ** See also: changes, status |
| 978 | */ |
| 979 | void mv_cmd(void){ |
| 980 | int i; |
| 981 | int vid; |
| 982 | int moveFiles; |
| 983 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -320,11 +320,11 @@ | |
| 320 | ** COMMAND: add |
| 321 | ** |
| 322 | ** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...? |
| 323 | ** |
| 324 | ** Make arrangements to add one or more files or directories to the |
| 325 | ** current checkout at the next [[commit]]. |
| 326 | ** |
| 327 | ** When adding files or directories recursively, filenames that begin |
| 328 | ** with "." are excluded by default. To include such files, add |
| 329 | ** the "--dotfiles" option to the command-line. |
| 330 | ** |
| @@ -359,11 +359,11 @@ | |
| 359 | ** |
| 360 | ** The following options are only valid with --reset: |
| 361 | ** -v|--verbose Outputs information about each --reset file. |
| 362 | ** -n|--dry-run Display instead of run actions. |
| 363 | ** |
| 364 | ** See also: [[addremove]], [[rm]] |
| 365 | */ |
| 366 | void add_cmd(void){ |
| 367 | int i; /* Loop counter */ |
| 368 | int vid; /* Currently checked out version */ |
| 369 | int nRoot; /* Full path characters in g.zLocalRoot */ |
| @@ -541,11 +541,11 @@ | |
| 541 | ** than --verbose or --dry-run may be used with |
| 542 | ** --reset. |
| 543 | ** --verbose|-v Outputs information about each --reset file. |
| 544 | ** Only usable with --reset. |
| 545 | ** |
| 546 | ** See also: [[addremove]], [[add]] |
| 547 | */ |
| 548 | void delete_cmd(void){ |
| 549 | int i; |
| 550 | int removeFiles; |
| 551 | int dryRunFlag = find_option("dry-run","n",0)!=0; |
| @@ -689,22 +689,22 @@ | |
| 689 | /* |
| 690 | ** COMMAND: addremove |
| 691 | ** |
| 692 | ** Usage: %fossil addremove ?OPTIONS? |
| 693 | ** |
| 694 | ** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the |
| 695 | ** repository with the content of the working checkout: |
| 696 | ** |
| 697 | ** * All files in the checkout but not in the repository (that is, |
| 698 | ** all files displayed using the "extras" command) are added as |
| 699 | ** if by the "[[add]]" command. |
| 700 | ** |
| 701 | ** * All files in the repository but missing from the checkout (that is, |
| 702 | ** all files that show as MISSING with the "status" command) are |
| 703 | ** removed as if by the "[[rm]]" command. |
| 704 | ** |
| 705 | ** The command does not "[[commit]]". You must run the "[[commit]]" separately |
| 706 | ** as a separate step. |
| 707 | ** |
| 708 | ** Files and directories whose names begin with "." are ignored unless |
| 709 | ** the --dotfiles option is used. |
| 710 | ** |
| @@ -733,11 +733,11 @@ | |
| 733 | ** removed. No flags other than --verbose and |
| 734 | ** --dry-run may be used with --reset. |
| 735 | ** --verbose|-v Outputs information about each --reset file. |
| 736 | ** Only usable with --reset. |
| 737 | ** |
| 738 | ** See also: [[add]], [[rm]] |
| 739 | */ |
| 740 | void addremove_cmd(void){ |
| 741 | Blob path; |
| 742 | const char *zCleanFlag; |
| 743 | const char *zIgnoreFlag; |
| @@ -953,11 +953,11 @@ | |
| 953 | ** Move or rename one or more files or directories within the repository tree. |
| 954 | ** You can either rename a file or directory or move it to another subdirectory. |
| 955 | ** |
| 956 | ** The 'mv' command does NOT normally rename or move the files on disk. |
| 957 | ** This command merely records the fact that file names have changed so |
| 958 | ** that appropriate notations can be made at the next [[commit]]. |
| 959 | ** However, the default behavior of this command may be overridden via |
| 960 | ** command line options listed below and/or the 'mv-rm-files' setting. |
| 961 | ** |
| 962 | ** The 'rename' command never renames or moves files on disk, even when the |
| 963 | ** command line options and/or the 'mv-rm-files' setting would otherwise |
| @@ -966,17 +966,17 @@ | |
| 966 | ** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files" |
| 967 | ** setting is non-zero, files WILL BE renamed or moved on disk |
| 968 | ** as well. This does NOT apply to the 'rename' command. |
| 969 | ** |
| 970 | ** Options: |
| 971 | ** --soft Skip moving files within the checkout. |
| 972 | ** This supersedes the --hard option. |
| 973 | ** --hard Move files within the checkout. |
| 974 | ** --case-sensitive <BOOL> Override the case-sensitive setting. |
| 975 | ** -n|--dry-run If given, display instead of run actions. |
| 976 | ** |
| 977 | ** See also: [[changes]], [[status]] |
| 978 | */ |
| 979 | void mv_cmd(void){ |
| 980 | int i; |
| 981 | int vid; |
| 982 | int moveFiles; |
| 983 |
+15
-2
| --- src/ajax.c | ||
| +++ src/ajax.c | ||
| @@ -130,11 +130,12 @@ | ||
| 130 | 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | 131 | break; |
| 132 | 132 | default:{ |
| 133 | 133 | const char *zContent = blob_str(pContent); |
| 134 | 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | - output_text_with_line_numbers(zContent, "on"); | |
| 135 | + output_text_with_line_numbers(zContent, blob_size(pContent), | |
| 136 | + zName, "on"); | |
| 136 | 137 | }else{ |
| 137 | 138 | const char *zExt = strrchr(zName,'.'); |
| 138 | 139 | if(zExt && zExt[1]){ |
| 139 | 140 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 140 | 141 | zExt+1, zContent); |
| @@ -163,10 +164,20 @@ | ||
| 163 | 164 | }else{ |
| 164 | 165 | CX("<pre class='udiff'>%b</pre>",&out); |
| 165 | 166 | } |
| 166 | 167 | blob_reset(&out); |
| 167 | 168 | } |
| 169 | + | |
| 170 | +/* | |
| 171 | +** Uses P(zKey) to fetch a CGI environment variable. If that var is | |
| 172 | +** NULL or starts with '0' or 'f' then this function returns false, | |
| 173 | +** else it returns true. | |
| 174 | +*/ | |
| 175 | +int ajax_p_bool(char const *zKey){ | |
| 176 | + const char * zVal = P(zKey); | |
| 177 | + return (!zVal || '0'==*zVal || 'f'==*zVal) ? 0 : 1; | |
| 178 | +} | |
| 168 | 179 | |
| 169 | 180 | /* |
| 170 | 181 | ** Helper for /ajax routes. Clears the CGI content buffer, sets an |
| 171 | 182 | ** HTTP error status code, and queues up a JSON response in the form |
| 172 | 183 | ** of an object: |
| @@ -323,10 +334,11 @@ | ||
| 323 | 334 | if(zRenderMode!=0){ |
| 324 | 335 | cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); |
| 325 | 336 | } |
| 326 | 337 | } |
| 327 | 338 | |
| 339 | +#if INTERFACE | |
| 328 | 340 | /* |
| 329 | 341 | ** Internal mapping of ajax sub-route names to various metadata. |
| 330 | 342 | */ |
| 331 | 343 | struct AjaxRoute { |
| 332 | 344 | const char *zName; /* Name part of the route after "ajax/" */ |
| @@ -334,16 +346,17 @@ | ||
| 334 | 346 | int bWriteMode; /* True if requires write mode */ |
| 335 | 347 | int bPost; /* True if requires POST (i.e. CSRF |
| 336 | 348 | ** verification) */ |
| 337 | 349 | }; |
| 338 | 350 | typedef struct AjaxRoute AjaxRoute; |
| 351 | +#endif /*INTERFACE*/ | |
| 339 | 352 | |
| 340 | 353 | /* |
| 341 | 354 | ** Comparison function for bsearch() for searching an AjaxRoute |
| 342 | 355 | ** list for a matching name. |
| 343 | 356 | */ |
| 344 | -static int cmp_ajax_route_name(const void *a, const void *b){ | |
| 357 | +int cmp_ajax_route_name(const void *a, const void *b){ | |
| 345 | 358 | const AjaxRoute * rA = (const AjaxRoute*)a; |
| 346 | 359 | const AjaxRoute * rB = (const AjaxRoute*)b; |
| 347 | 360 | return fossil_strcmp(rA->zName, rB->zName); |
| 348 | 361 | } |
| 349 | 362 | |
| 350 | 363 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -130,11 +130,12 @@ | |
| 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | break; |
| 132 | default:{ |
| 133 | const char *zContent = blob_str(pContent); |
| 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | output_text_with_line_numbers(zContent, "on"); |
| 136 | }else{ |
| 137 | const char *zExt = strrchr(zName,'.'); |
| 138 | if(zExt && zExt[1]){ |
| 139 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 140 | zExt+1, zContent); |
| @@ -163,10 +164,20 @@ | |
| 163 | }else{ |
| 164 | CX("<pre class='udiff'>%b</pre>",&out); |
| 165 | } |
| 166 | blob_reset(&out); |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | ** Helper for /ajax routes. Clears the CGI content buffer, sets an |
| 171 | ** HTTP error status code, and queues up a JSON response in the form |
| 172 | ** of an object: |
| @@ -323,10 +334,11 @@ | |
| 323 | if(zRenderMode!=0){ |
| 324 | cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | /* |
| 329 | ** Internal mapping of ajax sub-route names to various metadata. |
| 330 | */ |
| 331 | struct AjaxRoute { |
| 332 | const char *zName; /* Name part of the route after "ajax/" */ |
| @@ -334,16 +346,17 @@ | |
| 334 | int bWriteMode; /* True if requires write mode */ |
| 335 | int bPost; /* True if requires POST (i.e. CSRF |
| 336 | ** verification) */ |
| 337 | }; |
| 338 | typedef struct AjaxRoute AjaxRoute; |
| 339 | |
| 340 | /* |
| 341 | ** Comparison function for bsearch() for searching an AjaxRoute |
| 342 | ** list for a matching name. |
| 343 | */ |
| 344 | static int cmp_ajax_route_name(const void *a, const void *b){ |
| 345 | const AjaxRoute * rA = (const AjaxRoute*)a; |
| 346 | const AjaxRoute * rB = (const AjaxRoute*)b; |
| 347 | return fossil_strcmp(rA->zName, rB->zName); |
| 348 | } |
| 349 | |
| 350 |
| --- src/ajax.c | |
| +++ src/ajax.c | |
| @@ -130,11 +130,12 @@ | |
| 130 | wiki_render_by_mimetype(pContent, zMime); |
| 131 | break; |
| 132 | default:{ |
| 133 | const char *zContent = blob_str(pContent); |
| 134 | if(AJAX_PREVIEW_LINE_NUMBERS & flags){ |
| 135 | output_text_with_line_numbers(zContent, blob_size(pContent), |
| 136 | zName, "on"); |
| 137 | }else{ |
| 138 | const char *zExt = strrchr(zName,'.'); |
| 139 | if(zExt && zExt[1]){ |
| 140 | CX("<pre><code class='language-%s'>%h</code></pre>", |
| 141 | zExt+1, zContent); |
| @@ -163,10 +164,20 @@ | |
| 164 | }else{ |
| 165 | CX("<pre class='udiff'>%b</pre>",&out); |
| 166 | } |
| 167 | blob_reset(&out); |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | ** Uses P(zKey) to fetch a CGI environment variable. If that var is |
| 172 | ** NULL or starts with '0' or 'f' then this function returns false, |
| 173 | ** else it returns true. |
| 174 | */ |
| 175 | int ajax_p_bool(char const *zKey){ |
| 176 | const char * zVal = P(zKey); |
| 177 | return (!zVal || '0'==*zVal || 'f'==*zVal) ? 0 : 1; |
| 178 | } |
| 179 | |
| 180 | /* |
| 181 | ** Helper for /ajax routes. Clears the CGI content buffer, sets an |
| 182 | ** HTTP error status code, and queues up a JSON response in the form |
| 183 | ** of an object: |
| @@ -323,10 +334,11 @@ | |
| 334 | if(zRenderMode!=0){ |
| 335 | cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode); |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | #if INTERFACE |
| 340 | /* |
| 341 | ** Internal mapping of ajax sub-route names to various metadata. |
| 342 | */ |
| 343 | struct AjaxRoute { |
| 344 | const char *zName; /* Name part of the route after "ajax/" */ |
| @@ -334,16 +346,17 @@ | |
| 346 | int bWriteMode; /* True if requires write mode */ |
| 347 | int bPost; /* True if requires POST (i.e. CSRF |
| 348 | ** verification) */ |
| 349 | }; |
| 350 | typedef struct AjaxRoute AjaxRoute; |
| 351 | #endif /*INTERFACE*/ |
| 352 | |
| 353 | /* |
| 354 | ** Comparison function for bsearch() for searching an AjaxRoute |
| 355 | ** list for a matching name. |
| 356 | */ |
| 357 | int cmp_ajax_route_name(const void *a, const void *b){ |
| 358 | const AjaxRoute * rA = (const AjaxRoute*)a; |
| 359 | const AjaxRoute * rB = (const AjaxRoute*)b; |
| 360 | return fossil_strcmp(rA->zName, rB->zName); |
| 361 | } |
| 362 | |
| 363 |
+1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -219,10 +219,11 @@ | ||
| 219 | 219 | zCmd = "dbstat --omit-version-info -R"; |
| 220 | 220 | showLabel = 1; |
| 221 | 221 | quiet = 1; |
| 222 | 222 | collect_argument(&extra, "brief", "b"); |
| 223 | 223 | collect_argument(&extra, "db-check", 0); |
| 224 | + collect_argument(&extra, "db-verify", 0); | |
| 224 | 225 | }else if( strncmp(zCmd, "extras", n)==0 ){ |
| 225 | 226 | if( showFile ){ |
| 226 | 227 | zCmd = "extras --chdir"; |
| 227 | 228 | }else{ |
| 228 | 229 | zCmd = "extras --header --chdir"; |
| 229 | 230 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -219,10 +219,11 @@ | |
| 219 | zCmd = "dbstat --omit-version-info -R"; |
| 220 | showLabel = 1; |
| 221 | quiet = 1; |
| 222 | collect_argument(&extra, "brief", "b"); |
| 223 | collect_argument(&extra, "db-check", 0); |
| 224 | }else if( strncmp(zCmd, "extras", n)==0 ){ |
| 225 | if( showFile ){ |
| 226 | zCmd = "extras --chdir"; |
| 227 | }else{ |
| 228 | zCmd = "extras --header --chdir"; |
| 229 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -219,10 +219,11 @@ | |
| 219 | zCmd = "dbstat --omit-version-info -R"; |
| 220 | showLabel = 1; |
| 221 | quiet = 1; |
| 222 | collect_argument(&extra, "brief", "b"); |
| 223 | collect_argument(&extra, "db-check", 0); |
| 224 | collect_argument(&extra, "db-verify", 0); |
| 225 | }else if( strncmp(zCmd, "extras", n)==0 ){ |
| 226 | if( showFile ){ |
| 227 | zCmd = "extras --chdir"; |
| 228 | }else{ |
| 229 | zCmd = "extras --header --chdir"; |
| 230 |
+1
-1
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -617,11 +617,11 @@ | ||
| 617 | 617 | const char *z; |
| 618 | 618 | content_get(ridSrc, &attach); |
| 619 | 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | 620 | z = blob_str(&attach); |
| 621 | 621 | if( zLn ){ |
| 622 | - output_text_with_line_numbers(z, zLn); | |
| 622 | + output_text_with_line_numbers(z, blob_size(&attach), zName, zLn); | |
| 623 | 623 | }else{ |
| 624 | 624 | @ <pre> |
| 625 | 625 | @ %h(z) |
| 626 | 626 | @ </pre> |
| 627 | 627 | } |
| 628 | 628 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | const char *z; |
| 618 | content_get(ridSrc, &attach); |
| 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | z = blob_str(&attach); |
| 621 | if( zLn ){ |
| 622 | output_text_with_line_numbers(z, zLn); |
| 623 | }else{ |
| 624 | @ <pre> |
| 625 | @ %h(z) |
| 626 | @ </pre> |
| 627 | } |
| 628 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -617,11 +617,11 @@ | |
| 617 | const char *z; |
| 618 | content_get(ridSrc, &attach); |
| 619 | blob_to_utf8_no_bom(&attach, 0); |
| 620 | z = blob_str(&attach); |
| 621 | if( zLn ){ |
| 622 | output_text_with_line_numbers(z, blob_size(&attach), zName, zLn); |
| 623 | }else{ |
| 624 | @ <pre> |
| 625 | @ %h(z) |
| 626 | @ </pre> |
| 627 | } |
| 628 |
+2
-1
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -381,11 +381,12 @@ | ||
| 381 | 381 | /* |
| 382 | 382 | ** COMMAND: bisect |
| 383 | 383 | ** |
| 384 | 384 | ** Usage: %fossil bisect SUBCOMMAND ... |
| 385 | 385 | ** |
| 386 | -** Run various subcommands useful for searching for bugs. | |
| 386 | +** Run various subcommands useful for searching back through the change | |
| 387 | +** history for a particular checkin that causes or fixes a problem. | |
| 387 | 388 | ** |
| 388 | 389 | ** > fossil bisect bad ?VERSION? |
| 389 | 390 | ** |
| 390 | 391 | ** Identify version VERSION as non-working. If VERSION is omitted, |
| 391 | 392 | ** the current checkout is marked as non-working. |
| 392 | 393 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -381,11 +381,12 @@ | |
| 381 | /* |
| 382 | ** COMMAND: bisect |
| 383 | ** |
| 384 | ** Usage: %fossil bisect SUBCOMMAND ... |
| 385 | ** |
| 386 | ** Run various subcommands useful for searching for bugs. |
| 387 | ** |
| 388 | ** > fossil bisect bad ?VERSION? |
| 389 | ** |
| 390 | ** Identify version VERSION as non-working. If VERSION is omitted, |
| 391 | ** the current checkout is marked as non-working. |
| 392 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -381,11 +381,12 @@ | |
| 381 | /* |
| 382 | ** COMMAND: bisect |
| 383 | ** |
| 384 | ** Usage: %fossil bisect SUBCOMMAND ... |
| 385 | ** |
| 386 | ** Run various subcommands useful for searching back through the change |
| 387 | ** history for a particular checkin that causes or fixes a problem. |
| 388 | ** |
| 389 | ** > fossil bisect bad ?VERSION? |
| 390 | ** |
| 391 | ** Identify version VERSION as non-working. If VERSION is omitted, |
| 392 | ** the current checkout is marked as non-working. |
| 393 |
+5
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -487,10 +487,15 @@ | ||
| 487 | 487 | ** |
| 488 | 488 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 489 | 489 | ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If |
| 490 | 490 | ** it didn't, it would be possible to bypass that hard-coded limit via |
| 491 | 491 | ** this function. |
| 492 | +** | |
| 493 | +** We've had at least one report: | |
| 494 | +** https://fossil-scm.org/forum/forumpost/b7bbd28db4 | |
| 495 | +** which implies that this is unconditionally failing on mingw 32-bit | |
| 496 | +** builds. | |
| 492 | 497 | */ |
| 493 | 498 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 494 | 499 | if(newSize>=0x7fff0000 ){ |
| 495 | 500 | blob_panic(); |
| 496 | 501 | }else if(newSize>pBlob->nUsed){ |
| 497 | 502 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -487,10 +487,15 @@ | |
| 487 | ** |
| 488 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 489 | ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If |
| 490 | ** it didn't, it would be possible to bypass that hard-coded limit via |
| 491 | ** this function. |
| 492 | */ |
| 493 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 494 | if(newSize>=0x7fff0000 ){ |
| 495 | blob_panic(); |
| 496 | }else if(newSize>pBlob->nUsed){ |
| 497 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -487,10 +487,15 @@ | |
| 487 | ** |
| 488 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 489 | ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If |
| 490 | ** it didn't, it would be possible to bypass that hard-coded limit via |
| 491 | ** this function. |
| 492 | ** |
| 493 | ** We've had at least one report: |
| 494 | ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 |
| 495 | ** which implies that this is unconditionally failing on mingw 32-bit |
| 496 | ** builds. |
| 497 | */ |
| 498 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 499 | if(newSize>=0x7fff0000 ){ |
| 500 | blob_panic(); |
| 501 | }else if(newSize>pBlob->nUsed){ |
| 502 |
-6
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -382,16 +382,10 @@ | ||
| 382 | 382 | ** Either no timezone suffix or "Z" means UTC. |
| 383 | 383 | ** |
| 384 | 384 | ** Options valid for all subcommands: |
| 385 | 385 | ** |
| 386 | 386 | ** -R|--repository FILE Run commands on repository FILE |
| 387 | -** | |
| 388 | -** Summary: | |
| 389 | -** fossil branch current | |
| 390 | -** fossil branch info BRANCH-NAME | |
| 391 | -** fossil branch [list|ls] | |
| 392 | -** fossil branch new BRANCH-NAME BASIS | |
| 393 | 387 | */ |
| 394 | 388 | void branch_cmd(void){ |
| 395 | 389 | int n; |
| 396 | 390 | const char *zCmd = "list"; |
| 397 | 391 | db_find_and_open_repository(0, 0); |
| 398 | 392 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -382,16 +382,10 @@ | |
| 382 | ** Either no timezone suffix or "Z" means UTC. |
| 383 | ** |
| 384 | ** Options valid for all subcommands: |
| 385 | ** |
| 386 | ** -R|--repository FILE Run commands on repository FILE |
| 387 | ** |
| 388 | ** Summary: |
| 389 | ** fossil branch current |
| 390 | ** fossil branch info BRANCH-NAME |
| 391 | ** fossil branch [list|ls] |
| 392 | ** fossil branch new BRANCH-NAME BASIS |
| 393 | */ |
| 394 | void branch_cmd(void){ |
| 395 | int n; |
| 396 | const char *zCmd = "list"; |
| 397 | db_find_and_open_repository(0, 0); |
| 398 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -382,16 +382,10 @@ | |
| 382 | ** Either no timezone suffix or "Z" means UTC. |
| 383 | ** |
| 384 | ** Options valid for all subcommands: |
| 385 | ** |
| 386 | ** -R|--repository FILE Run commands on repository FILE |
| 387 | */ |
| 388 | void branch_cmd(void){ |
| 389 | int n; |
| 390 | const char *zCmd = "list"; |
| 391 | db_find_and_open_repository(0, 0); |
| 392 |
+1
-1
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -911,11 +911,11 @@ | ||
| 911 | 911 | } |
| 912 | 912 | } |
| 913 | 913 | } |
| 914 | 914 | @ </ul> |
| 915 | 915 | @ </ul></div> |
| 916 | - style_load_one_js_file("tree.js"); | |
| 916 | + builtin_request_js("tree.js"); | |
| 917 | 917 | style_footer(); |
| 918 | 918 | |
| 919 | 919 | /* We could free memory used by sTree here if we needed to. But |
| 920 | 920 | ** the process is about to exit, so doing so would not really accomplish |
| 921 | 921 | ** anything useful. */ |
| 922 | 922 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -911,11 +911,11 @@ | |
| 911 | } |
| 912 | } |
| 913 | } |
| 914 | @ </ul> |
| 915 | @ </ul></div> |
| 916 | style_load_one_js_file("tree.js"); |
| 917 | style_footer(); |
| 918 | |
| 919 | /* We could free memory used by sTree here if we needed to. But |
| 920 | ** the process is about to exit, so doing so would not really accomplish |
| 921 | ** anything useful. */ |
| 922 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -911,11 +911,11 @@ | |
| 911 | } |
| 912 | } |
| 913 | } |
| 914 | @ </ul> |
| 915 | @ </ul></div> |
| 916 | builtin_request_js("tree.js"); |
| 917 | style_footer(); |
| 918 | |
| 919 | /* We could free memory used by sTree here if we needed to. But |
| 920 | ** the process is about to exit, so doing so would not really accomplish |
| 921 | ** anything useful. */ |
| 922 |
+459
-10
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -28,13 +28,15 @@ | ||
| 28 | 28 | ** builtin_data.h file. Include that information here: |
| 29 | 29 | */ |
| 30 | 30 | #include "builtin_data.h" |
| 31 | 31 | |
| 32 | 32 | /* |
| 33 | -** Return a pointer to built-in content | |
| 33 | +** Return the index in the aBuiltinFiles[] array for the file | |
| 34 | +** whose name is zFilename. Or return -1 if the file is not | |
| 35 | +** found. | |
| 34 | 36 | */ |
| 35 | -const unsigned char *builtin_file(const char *zFilename, int *piSize){ | |
| 37 | +static int builtin_file_index(const char *zFilename){ | |
| 36 | 38 | int lwr, upr, i, c; |
| 37 | 39 | lwr = 0; |
| 38 | 40 | upr = count(aBuiltinFiles) - 1; |
| 39 | 41 | while( upr>=lwr ){ |
| 40 | 42 | i = (upr+lwr)/2; |
| @@ -42,16 +44,28 @@ | ||
| 42 | 44 | if( c<0 ){ |
| 43 | 45 | lwr = i+1; |
| 44 | 46 | }else if( c>0 ){ |
| 45 | 47 | upr = i-1; |
| 46 | 48 | }else{ |
| 47 | - if( piSize ) *piSize = aBuiltinFiles[i].nByte; | |
| 48 | - return aBuiltinFiles[i].pData; | |
| 49 | + return i; | |
| 49 | 50 | } |
| 50 | 51 | } |
| 51 | - if( piSize ) *piSize = 0; | |
| 52 | - return 0; | |
| 52 | + return -1; | |
| 53 | +} | |
| 54 | + | |
| 55 | +/* | |
| 56 | +** Return a pointer to built-in content | |
| 57 | +*/ | |
| 58 | +const unsigned char *builtin_file(const char *zFilename, int *piSize){ | |
| 59 | + int i = builtin_file_index(zFilename); | |
| 60 | + if( i>=0 ){ | |
| 61 | + if( piSize ) *piSize = aBuiltinFiles[i].nByte; | |
| 62 | + return aBuiltinFiles[i].pData; | |
| 63 | + }else{ | |
| 64 | + if( piSize ) *piSize = 0; | |
| 65 | + return 0; | |
| 66 | + } | |
| 53 | 67 | } |
| 54 | 68 | const char *builtin_text(const char *zFilename){ |
| 55 | 69 | return (char*)builtin_file(zFilename, 0); |
| 56 | 70 | } |
| 57 | 71 | |
| @@ -65,11 +79,11 @@ | ||
| 65 | 79 | */ |
| 66 | 80 | void test_builtin_list(void){ |
| 67 | 81 | int i, size = 0;; |
| 68 | 82 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 69 | 83 | const int n = aBuiltinFiles[i].nByte; |
| 70 | - fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n); | |
| 84 | + fossil_print("%3d. %-45s %6d\n", i+1, aBuiltinFiles[i].zName,n); | |
| 71 | 85 | size += n; |
| 72 | 86 | } |
| 73 | 87 | if(find_option("verbose","v",0)!=0){ |
| 74 | 88 | fossil_print("%d entries totaling %d bytes\n", i, size); |
| 75 | 89 | } |
| @@ -81,16 +95,18 @@ | ||
| 81 | 95 | ** Show all built-in text files. |
| 82 | 96 | */ |
| 83 | 97 | void test_builtin_list_page(void){ |
| 84 | 98 | int i; |
| 85 | 99 | style_header("Built-in Text Files"); |
| 86 | - @ <ul> | |
| 100 | + @ <ol> | |
| 87 | 101 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 88 | 102 | const char *z = aBuiltinFiles[i].zName; |
| 89 | - @ <li>%z(href("%R/builtin?name=%T&id=%S",z,MANIFEST_UUID))%h(z)</a> | |
| 103 | + char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain", | |
| 104 | + z,fossil_exe_id()); | |
| 105 | + @ <li>%z(zUrl)%h(z)</a> | |
| 90 | 106 | } |
| 91 | - @ </ul> | |
| 107 | + @ </ol> | |
| 92 | 108 | style_footer(); |
| 93 | 109 | } |
| 94 | 110 | |
| 95 | 111 | /* |
| 96 | 112 | ** COMMAND: test-builtin-get |
| @@ -110,5 +126,438 @@ | ||
| 110 | 126 | } |
| 111 | 127 | blob_init(&x, (const char*)pData, nByte); |
| 112 | 128 | blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); |
| 113 | 129 | blob_reset(&x); |
| 114 | 130 | } |
| 131 | + | |
| 132 | +/* | |
| 133 | +** Input zList is a list of numeric identifiers for files in | |
| 134 | +** aBuiltinFiles[]. Return the concatenation of all of those | |
| 135 | +** files using mimetype zType, or as application/javascript if | |
| 136 | +** zType is 0. | |
| 137 | +*/ | |
| 138 | +static void builtin_deliver_multiple_js_files( | |
| 139 | + const char *zList, /* List of numeric identifiers */ | |
| 140 | + const char *zType /* Override mimetype */ | |
| 141 | +){ | |
| 142 | + Blob *pOut; | |
| 143 | + if( zType==0 ) zType = "application/javascript"; | |
| 144 | + cgi_set_content_type(zType); | |
| 145 | + pOut = cgi_output_blob(); | |
| 146 | + while( zList[0] ){ | |
| 147 | + int i = atoi(zList); | |
| 148 | + if( i>0 && i<=count(aBuiltinFiles) ){ | |
| 149 | + blob_appendf(pOut, "/* %s */\n", aBuiltinFiles[i-1].zName); | |
| 150 | + blob_append(pOut, (const char*)aBuiltinFiles[i-1].pData, | |
| 151 | + aBuiltinFiles[i-1].nByte); | |
| 152 | + } | |
| 153 | + while( fossil_isdigit(zList[0]) ) zList++; | |
| 154 | + if( zList[0]==',' ) zList++; | |
| 155 | + } | |
| 156 | + return; | |
| 157 | +} | |
| 158 | + | |
| 159 | +/* | |
| 160 | +** WEBPAGE: builtin | |
| 161 | +** | |
| 162 | +** Return one of many built-in content files. Query parameters: | |
| 163 | +** | |
| 164 | +** name=FILENAME Return the single file whose name is FILENAME. | |
| 165 | +** mimetype=TYPE Override the mimetype in the returned file to | |
| 166 | +** be TYPE. If this query parameter is omitted | |
| 167 | +** (the usual case) then the mimetype is inferred | |
| 168 | +** from the suffix on FILENAME | |
| 169 | +** m=IDLIST IDLIST is a comma-separated list of integers | |
| 170 | +** that specify multiple javascript files to be | |
| 171 | +** concatenated and returned all at once. | |
| 172 | +** id=UNIQUEID Version number of the "builtin" files. Used | |
| 173 | +** for cache control only. | |
| 174 | +** | |
| 175 | +** At least one of the name= or m= query parameters must be present. | |
| 176 | +** | |
| 177 | +** If the id= query parameter is present, then Fossil assumes that the | |
| 178 | +** result is immutable and sets a very large cache retention time (1 year). | |
| 179 | +*/ | |
| 180 | +void builtin_webpage(void){ | |
| 181 | + Blob out; | |
| 182 | + const char *zName = P("name"); | |
| 183 | + const char *zTxt = 0; | |
| 184 | + const char *zId = P("id"); | |
| 185 | + const char *zType = P("mimetype"); | |
| 186 | + int nId; | |
| 187 | + if( zName ) zTxt = builtin_text(zName); | |
| 188 | + if( zTxt==0 ){ | |
| 189 | + const char *zM = P("m"); | |
| 190 | + if( zM ){ | |
| 191 | + if( zId && (nId = (int)strlen(zId))>=8 | |
| 192 | + && strncmp(zId,fossil_exe_id(),nId)==0 | |
| 193 | + ){ | |
| 194 | + g.isConst = 1; | |
| 195 | + } | |
| 196 | + etag_check(0,0); | |
| 197 | + builtin_deliver_multiple_js_files(zM, zType); | |
| 198 | + return; | |
| 199 | + } | |
| 200 | + cgi_set_status(404, "Not Found"); | |
| 201 | + @ File "%h(zName)" not found | |
| 202 | + return; | |
| 203 | + } | |
| 204 | + if( zType==0 ){ | |
| 205 | + if( sqlite3_strglob("*.js", zName)==0 ){ | |
| 206 | + zType = "application/javascript"; | |
| 207 | + }else{ | |
| 208 | + zType = mimetype_from_name(zName); | |
| 209 | + } | |
| 210 | + } | |
| 211 | + cgi_set_content_type(zType); | |
| 212 | + if( zId | |
| 213 | + && (nId = (int)strlen(zId))>=8 | |
| 214 | + && strncmp(zId,fossil_exe_id(),nId)==0 | |
| 215 | + ){ | |
| 216 | + g.isConst = 1; | |
| 217 | + } | |
| 218 | + etag_check(0,0); | |
| 219 | + blob_init(&out, zTxt, -1); | |
| 220 | + cgi_set_content(&out); | |
| 221 | +} | |
| 222 | + | |
| 223 | +/* Variables controlling the JS cache. | |
| 224 | +*/ | |
| 225 | +static struct { | |
| 226 | + int aReq[30]; /* Indexes of all requested built-in JS files */ | |
| 227 | + int nReq; /* Number of slots in aReq[] currently used */ | |
| 228 | + int nSent; /* Number of slots in aReq[] fulfilled */ | |
| 229 | + int eDelivery; /* Delivery mechanism */ | |
| 230 | +} builtin; | |
| 231 | + | |
| 232 | +#if INTERFACE | |
| 233 | +/* Various delivery mechanisms. The 0 option is the default. | |
| 234 | +*/ | |
| 235 | +#define JS_INLINE 0 /* inline, batched together at end of file */ | |
| 236 | +#define JS_SEPARATE 1 /* Separate HTTP request for each JS file */ | |
| 237 | +#define JS_BUNDLED 2 /* One HTTP request to load all JS files */ | |
| 238 | + /* concatenated together into a bundle */ | |
| 239 | +#endif /* INTERFACE */ | |
| 240 | + | |
| 241 | +/* | |
| 242 | +** The argument is a request to change the javascript delivery mode. | |
| 243 | +** The argument is a string which is a command-line option or CGI | |
| 244 | +** parameter. Try to match it against one of the delivery options | |
| 245 | +** and set things up accordingly. Throw an error if no match unless | |
| 246 | +** bSilent is true. | |
| 247 | +*/ | |
| 248 | +void builtin_set_js_delivery_mode(const char *zMode, int bSilent){ | |
| 249 | + if( zMode==0 ) return; | |
| 250 | + if( strcmp(zMode, "inline")==0 ){ | |
| 251 | + builtin.eDelivery = JS_INLINE; | |
| 252 | + }else | |
| 253 | + if( strcmp(zMode, "separate")==0 ){ | |
| 254 | + builtin.eDelivery = JS_SEPARATE; | |
| 255 | + }else | |
| 256 | + if( strcmp(zMode, "bundled")==0 ){ | |
| 257 | + builtin.eDelivery = JS_BUNDLED; | |
| 258 | + }else if( !bSilent ){ | |
| 259 | + fossil_fatal("unknown javascript delivery mode \"%s\" - should be" | |
| 260 | + " one of: inline separate bundled", zMode); | |
| 261 | + } | |
| 262 | +} | |
| 263 | + | |
| 264 | +/* | |
| 265 | +** The caller wants the Javascript file named by zFilename to be | |
| 266 | +** included in the generated page. Add the file to the queue of | |
| 267 | +** requested javascript resources, if it is not there already. | |
| 268 | +** | |
| 269 | +** The current implementation queues the file to be included in the | |
| 270 | +** output later. However, the caller should not depend on that | |
| 271 | +** behavior. In the future, this routine might decide to insert | |
| 272 | +** the requested javascript inline, immedaitely, or to insert | |
| 273 | +** a <script src=..> element to reference the javascript as a | |
| 274 | +** separate resource. The exact behavior might change in the future | |
| 275 | +** so pages that use this interface must not rely on any particular | |
| 276 | +** behavior. | |
| 277 | +** | |
| 278 | +** All this routine guarantees is that the named javascript file | |
| 279 | +** will be requested by the browser at some point. This routine | |
| 280 | +** does not guarantee when the javascript will be included, and it | |
| 281 | +** does not guarantee whether the javascript will be added inline or | |
| 282 | +** delivered as a separate resource. | |
| 283 | +*/ | |
| 284 | +void builtin_request_js(const char *zFilename){ | |
| 285 | + int i = builtin_file_index(zFilename); | |
| 286 | + int j; | |
| 287 | + if( i<0 ){ | |
| 288 | + fossil_panic("unknown javascript file: \"%s\"", zFilename); | |
| 289 | + } | |
| 290 | + for(j=0; j<builtin.nReq; j++){ | |
| 291 | + if( builtin.aReq[j]==i ) return; /* Already queued or sent */ | |
| 292 | + } | |
| 293 | + if( builtin.nReq>=count(builtin.aReq) ){ | |
| 294 | + fossil_panic("too many javascript files requested"); | |
| 295 | + } | |
| 296 | + builtin.aReq[builtin.nReq++] = i; | |
| 297 | +} | |
| 298 | + | |
| 299 | +/* | |
| 300 | +** Fulfill all pending requests for javascript files. | |
| 301 | +** | |
| 302 | +** The current implementation delivers all javascript in-line. However, | |
| 303 | +** the caller should not depend on this. Future changes to this routine | |
| 304 | +** might choose to deliver javascript as separate resources. | |
| 305 | +*/ | |
| 306 | +void builtin_fulfill_js_requests(void){ | |
| 307 | + if( builtin.nSent>=builtin.nReq ) return; /* nothing to do */ | |
| 308 | + switch( builtin.eDelivery ){ | |
| 309 | + case JS_INLINE: { | |
| 310 | + CX("<script nonce='%h'>\n",style_nonce()); | |
| 311 | + do{ | |
| 312 | + int i = builtin.aReq[builtin.nSent++]; | |
| 313 | + CX("/* %s */\n", aBuiltinFiles[i].zName); | |
| 314 | + cgi_append_content((const char*)aBuiltinFiles[i].pData, | |
| 315 | + aBuiltinFiles[i].nByte); | |
| 316 | + }while( builtin.nSent<builtin.nReq ); | |
| 317 | + CX("</script>\n"); | |
| 318 | + break; | |
| 319 | + } | |
| 320 | + case JS_BUNDLED: { | |
| 321 | + if( builtin.nSent+1<builtin.nReq ){ | |
| 322 | + Blob aList; | |
| 323 | + blob_init(&aList,0,0); | |
| 324 | + while( builtin.nSent<builtin.nReq ){ | |
| 325 | + blob_appendf(&aList, ",%d", builtin.aReq[builtin.nSent++]+1); | |
| 326 | + } | |
| 327 | + CX("<script src='%R/builtin?m=%s&id=%.8s'></script>\n", | |
| 328 | + blob_str(&aList)+1, fossil_exe_id()); | |
| 329 | + blob_reset(&aList); | |
| 330 | + break; | |
| 331 | + } | |
| 332 | + /* If there is only one JS file, fall through into the | |
| 333 | + ** JS_SEPARATE case below. */ | |
| 334 | + /*FALLTHROUGH*/ | |
| 335 | + } | |
| 336 | + case JS_SEPARATE: { | |
| 337 | + /* Each JS file as a separate resource */ | |
| 338 | + while( builtin.nSent<builtin.nReq ){ | |
| 339 | + int i = builtin.aReq[builtin.nSent++]; | |
| 340 | + CX("<script src='%R/builtin?name=%t&id=%.8s'></script>\n", | |
| 341 | + aBuiltinFiles[i].zName, fossil_exe_id()); | |
| 342 | + } | |
| 343 | + break; | |
| 344 | + } | |
| 345 | + } | |
| 346 | +} | |
| 347 | + | |
| 348 | +/***************************************************************************** | |
| 349 | +** A virtual table for accessing the information in aBuiltinFiles[]. | |
| 350 | +*/ | |
| 351 | + | |
| 352 | +/* builtinVtab_vtab is a subclass of sqlite3_vtab which is | |
| 353 | +** underlying representation of the virtual table | |
| 354 | +*/ | |
| 355 | +typedef struct builtinVtab_vtab builtinVtab_vtab; | |
| 356 | +struct builtinVtab_vtab { | |
| 357 | + sqlite3_vtab base; /* Base class - must be first */ | |
| 358 | + /* Add new fields here, as necessary */ | |
| 359 | +}; | |
| 360 | + | |
| 361 | +/* builtinVtab_cursor is a subclass of sqlite3_vtab_cursor which will | |
| 362 | +** serve as the underlying representation of a cursor that scans | |
| 363 | +** over rows of the result | |
| 364 | +*/ | |
| 365 | +typedef struct builtinVtab_cursor builtinVtab_cursor; | |
| 366 | +struct builtinVtab_cursor { | |
| 367 | + sqlite3_vtab_cursor base; /* Base class - must be first */ | |
| 368 | + /* Insert new fields here. For this builtinVtab we only keep track | |
| 369 | + ** of the rowid */ | |
| 370 | + sqlite3_int64 iRowid; /* The rowid */ | |
| 371 | +}; | |
| 372 | + | |
| 373 | +/* | |
| 374 | +** The builtinVtabConnect() method is invoked to create a new | |
| 375 | +** builtin virtual table. | |
| 376 | +** | |
| 377 | +** Think of this routine as the constructor for builtinVtab_vtab objects. | |
| 378 | +** | |
| 379 | +** All this routine needs to do is: | |
| 380 | +** | |
| 381 | +** (1) Allocate the builtinVtab_vtab object and initialize all fields. | |
| 382 | +** | |
| 383 | +** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the | |
| 384 | +** result set of queries against the virtual table will look like. | |
| 385 | +*/ | |
| 386 | +static int builtinVtabConnect( | |
| 387 | + sqlite3 *db, | |
| 388 | + void *pAux, | |
| 389 | + int argc, const char *const*argv, | |
| 390 | + sqlite3_vtab **ppVtab, | |
| 391 | + char **pzErr | |
| 392 | +){ | |
| 393 | + builtinVtab_vtab *pNew; | |
| 394 | + int rc; | |
| 395 | + | |
| 396 | + rc = sqlite3_declare_vtab(db, | |
| 397 | + "CREATE TABLE x(name,size,data)" | |
| 398 | + ); | |
| 399 | + if( rc==SQLITE_OK ){ | |
| 400 | + pNew = sqlite3_malloc( sizeof(*pNew) ); | |
| 401 | + *ppVtab = (sqlite3_vtab*)pNew; | |
| 402 | + if( pNew==0 ) return SQLITE_NOMEM; | |
| 403 | + memset(pNew, 0, sizeof(*pNew)); | |
| 404 | + } | |
| 405 | + return rc; | |
| 406 | +} | |
| 407 | + | |
| 408 | +/* | |
| 409 | +** This method is the destructor for builtinVtab_vtab objects. | |
| 410 | +*/ | |
| 411 | +static int builtinVtabDisconnect(sqlite3_vtab *pVtab){ | |
| 412 | + builtinVtab_vtab *p = (builtinVtab_vtab*)pVtab; | |
| 413 | + sqlite3_free(p); | |
| 414 | + return SQLITE_OK; | |
| 415 | +} | |
| 416 | + | |
| 417 | +/* | |
| 418 | +** Constructor for a new builtinVtab_cursor object. | |
| 419 | +*/ | |
| 420 | +static int builtinVtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ | |
| 421 | + builtinVtab_cursor *pCur; | |
| 422 | + pCur = sqlite3_malloc( sizeof(*pCur) ); | |
| 423 | + if( pCur==0 ) return SQLITE_NOMEM; | |
| 424 | + memset(pCur, 0, sizeof(*pCur)); | |
| 425 | + *ppCursor = &pCur->base; | |
| 426 | + return SQLITE_OK; | |
| 427 | +} | |
| 428 | + | |
| 429 | +/* | |
| 430 | +** Destructor for a builtinVtab_cursor. | |
| 431 | +*/ | |
| 432 | +static int builtinVtabClose(sqlite3_vtab_cursor *cur){ | |
| 433 | + builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; | |
| 434 | + sqlite3_free(pCur); | |
| 435 | + return SQLITE_OK; | |
| 436 | +} | |
| 437 | + | |
| 438 | + | |
| 439 | +/* | |
| 440 | +** Advance a builtinVtab_cursor to its next row of output. | |
| 441 | +*/ | |
| 442 | +static int builtinVtabNext(sqlite3_vtab_cursor *cur){ | |
| 443 | + builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; | |
| 444 | + pCur->iRowid++; | |
| 445 | + return SQLITE_OK; | |
| 446 | +} | |
| 447 | + | |
| 448 | +/* | |
| 449 | +** Return values of columns for the row at which the builtinVtab_cursor | |
| 450 | +** is currently pointing. | |
| 451 | +*/ | |
| 452 | +static int builtinVtabColumn( | |
| 453 | + sqlite3_vtab_cursor *cur, /* The cursor */ | |
| 454 | + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ | |
| 455 | + int i /* Which column to return */ | |
| 456 | +){ | |
| 457 | + builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; | |
| 458 | + const struct BuiltinFileTable *pFile = aBuiltinFiles + pCur->iRowid; | |
| 459 | + switch( i ){ | |
| 460 | + case 0: /* name */ | |
| 461 | + sqlite3_result_text(ctx, pFile->zName, -1, SQLITE_STATIC); | |
| 462 | + break; | |
| 463 | + case 1: /* size */ | |
| 464 | + sqlite3_result_int(ctx, pFile->nByte); | |
| 465 | + break; | |
| 466 | + case 2: /* data */ | |
| 467 | + sqlite3_result_blob(ctx, pFile->pData, pFile->nByte, SQLITE_STATIC); | |
| 468 | + break; | |
| 469 | + } | |
| 470 | + return SQLITE_OK; | |
| 471 | +} | |
| 472 | + | |
| 473 | +/* | |
| 474 | +** Return the rowid for the current row. In this implementation, the | |
| 475 | +** rowid is the same as the output value. | |
| 476 | +*/ | |
| 477 | +static int builtinVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | |
| 478 | + builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; | |
| 479 | + *pRowid = pCur->iRowid; | |
| 480 | + return SQLITE_OK; | |
| 481 | +} | |
| 482 | + | |
| 483 | +/* | |
| 484 | +** Return TRUE if the cursor has been moved off of the last | |
| 485 | +** row of output. | |
| 486 | +*/ | |
| 487 | +static int builtinVtabEof(sqlite3_vtab_cursor *cur){ | |
| 488 | + builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; | |
| 489 | + return pCur->iRowid>=count(aBuiltinFiles); | |
| 490 | +} | |
| 491 | + | |
| 492 | +/* | |
| 493 | +** This method is called to "rewind" the builtinVtab_cursor object back | |
| 494 | +** to the first row of output. This method is always called at least | |
| 495 | +** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or | |
| 496 | +** builtinVtabEof(). | |
| 497 | +*/ | |
| 498 | +static int builtinVtabFilter( | |
| 499 | + sqlite3_vtab_cursor *pVtabCursor, | |
| 500 | + int idxNum, const char *idxStr, | |
| 501 | + int argc, sqlite3_value **argv | |
| 502 | +){ | |
| 503 | + builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor; | |
| 504 | + pCur->iRowid = 1; | |
| 505 | + return SQLITE_OK; | |
| 506 | +} | |
| 507 | + | |
| 508 | +/* | |
| 509 | +** SQLite will invoke this method one or more times while planning a query | |
| 510 | +** that uses the virtual table. This routine needs to create | |
| 511 | +** a query plan for each invocation and compute an estimated cost for that | |
| 512 | +** plan. | |
| 513 | +*/ | |
| 514 | +static int builtinVtabBestIndex( | |
| 515 | + sqlite3_vtab *tab, | |
| 516 | + sqlite3_index_info *pIdxInfo | |
| 517 | +){ | |
| 518 | + pIdxInfo->estimatedCost = (double)count(aBuiltinFiles); | |
| 519 | + pIdxInfo->estimatedRows = count(aBuiltinFiles); | |
| 520 | + return SQLITE_OK; | |
| 521 | +} | |
| 522 | + | |
| 523 | +/* | |
| 524 | +** This following structure defines all the methods for the | |
| 525 | +** virtual table. | |
| 526 | +*/ | |
| 527 | +static sqlite3_module builtinVtabModule = { | |
| 528 | + /* iVersion */ 0, | |
| 529 | + /* xCreate */ 0, /* The builtin vtab is eponymous and read-only */ | |
| 530 | + /* xConnect */ builtinVtabConnect, | |
| 531 | + /* xBestIndex */ builtinVtabBestIndex, | |
| 532 | + /* xDisconnect */ builtinVtabDisconnect, | |
| 533 | + /* xDestroy */ 0, | |
| 534 | + /* xOpen */ builtinVtabOpen, | |
| 535 | + /* xClose */ builtinVtabClose, | |
| 536 | + /* xFilter */ builtinVtabFilter, | |
| 537 | + /* xNext */ builtinVtabNext, | |
| 538 | + /* xEof */ builtinVtabEof, | |
| 539 | + /* xColumn */ builtinVtabColumn, | |
| 540 | + /* xRowid */ builtinVtabRowid, | |
| 541 | + /* xUpdate */ 0, | |
| 542 | + /* xBegin */ 0, | |
| 543 | + /* xSync */ 0, | |
| 544 | + /* xCommit */ 0, | |
| 545 | + /* xRollback */ 0, | |
| 546 | + /* xFindMethod */ 0, | |
| 547 | + /* xRename */ 0, | |
| 548 | + /* xSavepoint */ 0, | |
| 549 | + /* xRelease */ 0, | |
| 550 | + /* xRollbackTo */ 0, | |
| 551 | + /* xShadowName */ 0 | |
| 552 | +}; | |
| 553 | + | |
| 554 | + | |
| 555 | +/* | |
| 556 | +** Register the builtin virtual table | |
| 557 | +*/ | |
| 558 | +int builtin_vtab_register(sqlite3 *db){ | |
| 559 | + int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0); | |
| 560 | + return rc; | |
| 561 | +} | |
| 562 | +/* End of the builtin virtual table | |
| 563 | +******************************************************************************/ | |
| 115 | 564 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -28,13 +28,15 @@ | |
| 28 | ** builtin_data.h file. Include that information here: |
| 29 | */ |
| 30 | #include "builtin_data.h" |
| 31 | |
| 32 | /* |
| 33 | ** Return a pointer to built-in content |
| 34 | */ |
| 35 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 36 | int lwr, upr, i, c; |
| 37 | lwr = 0; |
| 38 | upr = count(aBuiltinFiles) - 1; |
| 39 | while( upr>=lwr ){ |
| 40 | i = (upr+lwr)/2; |
| @@ -42,16 +44,28 @@ | |
| 42 | if( c<0 ){ |
| 43 | lwr = i+1; |
| 44 | }else if( c>0 ){ |
| 45 | upr = i-1; |
| 46 | }else{ |
| 47 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 48 | return aBuiltinFiles[i].pData; |
| 49 | } |
| 50 | } |
| 51 | if( piSize ) *piSize = 0; |
| 52 | return 0; |
| 53 | } |
| 54 | const char *builtin_text(const char *zFilename){ |
| 55 | return (char*)builtin_file(zFilename, 0); |
| 56 | } |
| 57 | |
| @@ -65,11 +79,11 @@ | |
| 65 | */ |
| 66 | void test_builtin_list(void){ |
| 67 | int i, size = 0;; |
| 68 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 69 | const int n = aBuiltinFiles[i].nByte; |
| 70 | fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n); |
| 71 | size += n; |
| 72 | } |
| 73 | if(find_option("verbose","v",0)!=0){ |
| 74 | fossil_print("%d entries totaling %d bytes\n", i, size); |
| 75 | } |
| @@ -81,16 +95,18 @@ | |
| 81 | ** Show all built-in text files. |
| 82 | */ |
| 83 | void test_builtin_list_page(void){ |
| 84 | int i; |
| 85 | style_header("Built-in Text Files"); |
| 86 | @ <ul> |
| 87 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 88 | const char *z = aBuiltinFiles[i].zName; |
| 89 | @ <li>%z(href("%R/builtin?name=%T&id=%S",z,MANIFEST_UUID))%h(z)</a> |
| 90 | } |
| 91 | @ </ul> |
| 92 | style_footer(); |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | ** COMMAND: test-builtin-get |
| @@ -110,5 +126,438 @@ | |
| 110 | } |
| 111 | blob_init(&x, (const char*)pData, nByte); |
| 112 | blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); |
| 113 | blob_reset(&x); |
| 114 | } |
| 115 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -28,13 +28,15 @@ | |
| 28 | ** builtin_data.h file. Include that information here: |
| 29 | */ |
| 30 | #include "builtin_data.h" |
| 31 | |
| 32 | /* |
| 33 | ** Return the index in the aBuiltinFiles[] array for the file |
| 34 | ** whose name is zFilename. Or return -1 if the file is not |
| 35 | ** found. |
| 36 | */ |
| 37 | static int builtin_file_index(const char *zFilename){ |
| 38 | int lwr, upr, i, c; |
| 39 | lwr = 0; |
| 40 | upr = count(aBuiltinFiles) - 1; |
| 41 | while( upr>=lwr ){ |
| 42 | i = (upr+lwr)/2; |
| @@ -42,16 +44,28 @@ | |
| 44 | if( c<0 ){ |
| 45 | lwr = i+1; |
| 46 | }else if( c>0 ){ |
| 47 | upr = i-1; |
| 48 | }else{ |
| 49 | return i; |
| 50 | } |
| 51 | } |
| 52 | return -1; |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** Return a pointer to built-in content |
| 57 | */ |
| 58 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 59 | int i = builtin_file_index(zFilename); |
| 60 | if( i>=0 ){ |
| 61 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 62 | return aBuiltinFiles[i].pData; |
| 63 | }else{ |
| 64 | if( piSize ) *piSize = 0; |
| 65 | return 0; |
| 66 | } |
| 67 | } |
| 68 | const char *builtin_text(const char *zFilename){ |
| 69 | return (char*)builtin_file(zFilename, 0); |
| 70 | } |
| 71 | |
| @@ -65,11 +79,11 @@ | |
| 79 | */ |
| 80 | void test_builtin_list(void){ |
| 81 | int i, size = 0;; |
| 82 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 83 | const int n = aBuiltinFiles[i].nByte; |
| 84 | fossil_print("%3d. %-45s %6d\n", i+1, aBuiltinFiles[i].zName,n); |
| 85 | size += n; |
| 86 | } |
| 87 | if(find_option("verbose","v",0)!=0){ |
| 88 | fossil_print("%d entries totaling %d bytes\n", i, size); |
| 89 | } |
| @@ -81,16 +95,18 @@ | |
| 95 | ** Show all built-in text files. |
| 96 | */ |
| 97 | void test_builtin_list_page(void){ |
| 98 | int i; |
| 99 | style_header("Built-in Text Files"); |
| 100 | @ <ol> |
| 101 | for(i=0; i<count(aBuiltinFiles); i++){ |
| 102 | const char *z = aBuiltinFiles[i].zName; |
| 103 | char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain", |
| 104 | z,fossil_exe_id()); |
| 105 | @ <li>%z(zUrl)%h(z)</a> |
| 106 | } |
| 107 | @ </ol> |
| 108 | style_footer(); |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | ** COMMAND: test-builtin-get |
| @@ -110,5 +126,438 @@ | |
| 126 | } |
| 127 | blob_init(&x, (const char*)pData, nByte); |
| 128 | blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); |
| 129 | blob_reset(&x); |
| 130 | } |
| 131 | |
| 132 | /* |
| 133 | ** Input zList is a list of numeric identifiers for files in |
| 134 | ** aBuiltinFiles[]. Return the concatenation of all of those |
| 135 | ** files using mimetype zType, or as application/javascript if |
| 136 | ** zType is 0. |
| 137 | */ |
| 138 | static void builtin_deliver_multiple_js_files( |
| 139 | const char *zList, /* List of numeric identifiers */ |
| 140 | const char *zType /* Override mimetype */ |
| 141 | ){ |
| 142 | Blob *pOut; |
| 143 | if( zType==0 ) zType = "application/javascript"; |
| 144 | cgi_set_content_type(zType); |
| 145 | pOut = cgi_output_blob(); |
| 146 | while( zList[0] ){ |
| 147 | int i = atoi(zList); |
| 148 | if( i>0 && i<=count(aBuiltinFiles) ){ |
| 149 | blob_appendf(pOut, "/* %s */\n", aBuiltinFiles[i-1].zName); |
| 150 | blob_append(pOut, (const char*)aBuiltinFiles[i-1].pData, |
| 151 | aBuiltinFiles[i-1].nByte); |
| 152 | } |
| 153 | while( fossil_isdigit(zList[0]) ) zList++; |
| 154 | if( zList[0]==',' ) zList++; |
| 155 | } |
| 156 | return; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** WEBPAGE: builtin |
| 161 | ** |
| 162 | ** Return one of many built-in content files. Query parameters: |
| 163 | ** |
| 164 | ** name=FILENAME Return the single file whose name is FILENAME. |
| 165 | ** mimetype=TYPE Override the mimetype in the returned file to |
| 166 | ** be TYPE. If this query parameter is omitted |
| 167 | ** (the usual case) then the mimetype is inferred |
| 168 | ** from the suffix on FILENAME |
| 169 | ** m=IDLIST IDLIST is a comma-separated list of integers |
| 170 | ** that specify multiple javascript files to be |
| 171 | ** concatenated and returned all at once. |
| 172 | ** id=UNIQUEID Version number of the "builtin" files. Used |
| 173 | ** for cache control only. |
| 174 | ** |
| 175 | ** At least one of the name= or m= query parameters must be present. |
| 176 | ** |
| 177 | ** If the id= query parameter is present, then Fossil assumes that the |
| 178 | ** result is immutable and sets a very large cache retention time (1 year). |
| 179 | */ |
| 180 | void builtin_webpage(void){ |
| 181 | Blob out; |
| 182 | const char *zName = P("name"); |
| 183 | const char *zTxt = 0; |
| 184 | const char *zId = P("id"); |
| 185 | const char *zType = P("mimetype"); |
| 186 | int nId; |
| 187 | if( zName ) zTxt = builtin_text(zName); |
| 188 | if( zTxt==0 ){ |
| 189 | const char *zM = P("m"); |
| 190 | if( zM ){ |
| 191 | if( zId && (nId = (int)strlen(zId))>=8 |
| 192 | && strncmp(zId,fossil_exe_id(),nId)==0 |
| 193 | ){ |
| 194 | g.isConst = 1; |
| 195 | } |
| 196 | etag_check(0,0); |
| 197 | builtin_deliver_multiple_js_files(zM, zType); |
| 198 | return; |
| 199 | } |
| 200 | cgi_set_status(404, "Not Found"); |
| 201 | @ File "%h(zName)" not found |
| 202 | return; |
| 203 | } |
| 204 | if( zType==0 ){ |
| 205 | if( sqlite3_strglob("*.js", zName)==0 ){ |
| 206 | zType = "application/javascript"; |
| 207 | }else{ |
| 208 | zType = mimetype_from_name(zName); |
| 209 | } |
| 210 | } |
| 211 | cgi_set_content_type(zType); |
| 212 | if( zId |
| 213 | && (nId = (int)strlen(zId))>=8 |
| 214 | && strncmp(zId,fossil_exe_id(),nId)==0 |
| 215 | ){ |
| 216 | g.isConst = 1; |
| 217 | } |
| 218 | etag_check(0,0); |
| 219 | blob_init(&out, zTxt, -1); |
| 220 | cgi_set_content(&out); |
| 221 | } |
| 222 | |
| 223 | /* Variables controlling the JS cache. |
| 224 | */ |
| 225 | static struct { |
| 226 | int aReq[30]; /* Indexes of all requested built-in JS files */ |
| 227 | int nReq; /* Number of slots in aReq[] currently used */ |
| 228 | int nSent; /* Number of slots in aReq[] fulfilled */ |
| 229 | int eDelivery; /* Delivery mechanism */ |
| 230 | } builtin; |
| 231 | |
| 232 | #if INTERFACE |
| 233 | /* Various delivery mechanisms. The 0 option is the default. |
| 234 | */ |
| 235 | #define JS_INLINE 0 /* inline, batched together at end of file */ |
| 236 | #define JS_SEPARATE 1 /* Separate HTTP request for each JS file */ |
| 237 | #define JS_BUNDLED 2 /* One HTTP request to load all JS files */ |
| 238 | /* concatenated together into a bundle */ |
| 239 | #endif /* INTERFACE */ |
| 240 | |
| 241 | /* |
| 242 | ** The argument is a request to change the javascript delivery mode. |
| 243 | ** The argument is a string which is a command-line option or CGI |
| 244 | ** parameter. Try to match it against one of the delivery options |
| 245 | ** and set things up accordingly. Throw an error if no match unless |
| 246 | ** bSilent is true. |
| 247 | */ |
| 248 | void builtin_set_js_delivery_mode(const char *zMode, int bSilent){ |
| 249 | if( zMode==0 ) return; |
| 250 | if( strcmp(zMode, "inline")==0 ){ |
| 251 | builtin.eDelivery = JS_INLINE; |
| 252 | }else |
| 253 | if( strcmp(zMode, "separate")==0 ){ |
| 254 | builtin.eDelivery = JS_SEPARATE; |
| 255 | }else |
| 256 | if( strcmp(zMode, "bundled")==0 ){ |
| 257 | builtin.eDelivery = JS_BUNDLED; |
| 258 | }else if( !bSilent ){ |
| 259 | fossil_fatal("unknown javascript delivery mode \"%s\" - should be" |
| 260 | " one of: inline separate bundled", zMode); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | ** The caller wants the Javascript file named by zFilename to be |
| 266 | ** included in the generated page. Add the file to the queue of |
| 267 | ** requested javascript resources, if it is not there already. |
| 268 | ** |
| 269 | ** The current implementation queues the file to be included in the |
| 270 | ** output later. However, the caller should not depend on that |
| 271 | ** behavior. In the future, this routine might decide to insert |
| 272 | ** the requested javascript inline, immedaitely, or to insert |
| 273 | ** a <script src=..> element to reference the javascript as a |
| 274 | ** separate resource. The exact behavior might change in the future |
| 275 | ** so pages that use this interface must not rely on any particular |
| 276 | ** behavior. |
| 277 | ** |
| 278 | ** All this routine guarantees is that the named javascript file |
| 279 | ** will be requested by the browser at some point. This routine |
| 280 | ** does not guarantee when the javascript will be included, and it |
| 281 | ** does not guarantee whether the javascript will be added inline or |
| 282 | ** delivered as a separate resource. |
| 283 | */ |
| 284 | void builtin_request_js(const char *zFilename){ |
| 285 | int i = builtin_file_index(zFilename); |
| 286 | int j; |
| 287 | if( i<0 ){ |
| 288 | fossil_panic("unknown javascript file: \"%s\"", zFilename); |
| 289 | } |
| 290 | for(j=0; j<builtin.nReq; j++){ |
| 291 | if( builtin.aReq[j]==i ) return; /* Already queued or sent */ |
| 292 | } |
| 293 | if( builtin.nReq>=count(builtin.aReq) ){ |
| 294 | fossil_panic("too many javascript files requested"); |
| 295 | } |
| 296 | builtin.aReq[builtin.nReq++] = i; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | ** Fulfill all pending requests for javascript files. |
| 301 | ** |
| 302 | ** The current implementation delivers all javascript in-line. However, |
| 303 | ** the caller should not depend on this. Future changes to this routine |
| 304 | ** might choose to deliver javascript as separate resources. |
| 305 | */ |
| 306 | void builtin_fulfill_js_requests(void){ |
| 307 | if( builtin.nSent>=builtin.nReq ) return; /* nothing to do */ |
| 308 | switch( builtin.eDelivery ){ |
| 309 | case JS_INLINE: { |
| 310 | CX("<script nonce='%h'>\n",style_nonce()); |
| 311 | do{ |
| 312 | int i = builtin.aReq[builtin.nSent++]; |
| 313 | CX("/* %s */\n", aBuiltinFiles[i].zName); |
| 314 | cgi_append_content((const char*)aBuiltinFiles[i].pData, |
| 315 | aBuiltinFiles[i].nByte); |
| 316 | }while( builtin.nSent<builtin.nReq ); |
| 317 | CX("</script>\n"); |
| 318 | break; |
| 319 | } |
| 320 | case JS_BUNDLED: { |
| 321 | if( builtin.nSent+1<builtin.nReq ){ |
| 322 | Blob aList; |
| 323 | blob_init(&aList,0,0); |
| 324 | while( builtin.nSent<builtin.nReq ){ |
| 325 | blob_appendf(&aList, ",%d", builtin.aReq[builtin.nSent++]+1); |
| 326 | } |
| 327 | CX("<script src='%R/builtin?m=%s&id=%.8s'></script>\n", |
| 328 | blob_str(&aList)+1, fossil_exe_id()); |
| 329 | blob_reset(&aList); |
| 330 | break; |
| 331 | } |
| 332 | /* If there is only one JS file, fall through into the |
| 333 | ** JS_SEPARATE case below. */ |
| 334 | /*FALLTHROUGH*/ |
| 335 | } |
| 336 | case JS_SEPARATE: { |
| 337 | /* Each JS file as a separate resource */ |
| 338 | while( builtin.nSent<builtin.nReq ){ |
| 339 | int i = builtin.aReq[builtin.nSent++]; |
| 340 | CX("<script src='%R/builtin?name=%t&id=%.8s'></script>\n", |
| 341 | aBuiltinFiles[i].zName, fossil_exe_id()); |
| 342 | } |
| 343 | break; |
| 344 | } |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | /***************************************************************************** |
| 349 | ** A virtual table for accessing the information in aBuiltinFiles[]. |
| 350 | */ |
| 351 | |
| 352 | /* builtinVtab_vtab is a subclass of sqlite3_vtab which is |
| 353 | ** underlying representation of the virtual table |
| 354 | */ |
| 355 | typedef struct builtinVtab_vtab builtinVtab_vtab; |
| 356 | struct builtinVtab_vtab { |
| 357 | sqlite3_vtab base; /* Base class - must be first */ |
| 358 | /* Add new fields here, as necessary */ |
| 359 | }; |
| 360 | |
| 361 | /* builtinVtab_cursor is a subclass of sqlite3_vtab_cursor which will |
| 362 | ** serve as the underlying representation of a cursor that scans |
| 363 | ** over rows of the result |
| 364 | */ |
| 365 | typedef struct builtinVtab_cursor builtinVtab_cursor; |
| 366 | struct builtinVtab_cursor { |
| 367 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 368 | /* Insert new fields here. For this builtinVtab we only keep track |
| 369 | ** of the rowid */ |
| 370 | sqlite3_int64 iRowid; /* The rowid */ |
| 371 | }; |
| 372 | |
| 373 | /* |
| 374 | ** The builtinVtabConnect() method is invoked to create a new |
| 375 | ** builtin virtual table. |
| 376 | ** |
| 377 | ** Think of this routine as the constructor for builtinVtab_vtab objects. |
| 378 | ** |
| 379 | ** All this routine needs to do is: |
| 380 | ** |
| 381 | ** (1) Allocate the builtinVtab_vtab object and initialize all fields. |
| 382 | ** |
| 383 | ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the |
| 384 | ** result set of queries against the virtual table will look like. |
| 385 | */ |
| 386 | static int builtinVtabConnect( |
| 387 | sqlite3 *db, |
| 388 | void *pAux, |
| 389 | int argc, const char *const*argv, |
| 390 | sqlite3_vtab **ppVtab, |
| 391 | char **pzErr |
| 392 | ){ |
| 393 | builtinVtab_vtab *pNew; |
| 394 | int rc; |
| 395 | |
| 396 | rc = sqlite3_declare_vtab(db, |
| 397 | "CREATE TABLE x(name,size,data)" |
| 398 | ); |
| 399 | if( rc==SQLITE_OK ){ |
| 400 | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 401 | *ppVtab = (sqlite3_vtab*)pNew; |
| 402 | if( pNew==0 ) return SQLITE_NOMEM; |
| 403 | memset(pNew, 0, sizeof(*pNew)); |
| 404 | } |
| 405 | return rc; |
| 406 | } |
| 407 | |
| 408 | /* |
| 409 | ** This method is the destructor for builtinVtab_vtab objects. |
| 410 | */ |
| 411 | static int builtinVtabDisconnect(sqlite3_vtab *pVtab){ |
| 412 | builtinVtab_vtab *p = (builtinVtab_vtab*)pVtab; |
| 413 | sqlite3_free(p); |
| 414 | return SQLITE_OK; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** Constructor for a new builtinVtab_cursor object. |
| 419 | */ |
| 420 | static int builtinVtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
| 421 | builtinVtab_cursor *pCur; |
| 422 | pCur = sqlite3_malloc( sizeof(*pCur) ); |
| 423 | if( pCur==0 ) return SQLITE_NOMEM; |
| 424 | memset(pCur, 0, sizeof(*pCur)); |
| 425 | *ppCursor = &pCur->base; |
| 426 | return SQLITE_OK; |
| 427 | } |
| 428 | |
| 429 | /* |
| 430 | ** Destructor for a builtinVtab_cursor. |
| 431 | */ |
| 432 | static int builtinVtabClose(sqlite3_vtab_cursor *cur){ |
| 433 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; |
| 434 | sqlite3_free(pCur); |
| 435 | return SQLITE_OK; |
| 436 | } |
| 437 | |
| 438 | |
| 439 | /* |
| 440 | ** Advance a builtinVtab_cursor to its next row of output. |
| 441 | */ |
| 442 | static int builtinVtabNext(sqlite3_vtab_cursor *cur){ |
| 443 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; |
| 444 | pCur->iRowid++; |
| 445 | return SQLITE_OK; |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | ** Return values of columns for the row at which the builtinVtab_cursor |
| 450 | ** is currently pointing. |
| 451 | */ |
| 452 | static int builtinVtabColumn( |
| 453 | sqlite3_vtab_cursor *cur, /* The cursor */ |
| 454 | sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ |
| 455 | int i /* Which column to return */ |
| 456 | ){ |
| 457 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; |
| 458 | const struct BuiltinFileTable *pFile = aBuiltinFiles + pCur->iRowid; |
| 459 | switch( i ){ |
| 460 | case 0: /* name */ |
| 461 | sqlite3_result_text(ctx, pFile->zName, -1, SQLITE_STATIC); |
| 462 | break; |
| 463 | case 1: /* size */ |
| 464 | sqlite3_result_int(ctx, pFile->nByte); |
| 465 | break; |
| 466 | case 2: /* data */ |
| 467 | sqlite3_result_blob(ctx, pFile->pData, pFile->nByte, SQLITE_STATIC); |
| 468 | break; |
| 469 | } |
| 470 | return SQLITE_OK; |
| 471 | } |
| 472 | |
| 473 | /* |
| 474 | ** Return the rowid for the current row. In this implementation, the |
| 475 | ** rowid is the same as the output value. |
| 476 | */ |
| 477 | static int builtinVtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| 478 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; |
| 479 | *pRowid = pCur->iRowid; |
| 480 | return SQLITE_OK; |
| 481 | } |
| 482 | |
| 483 | /* |
| 484 | ** Return TRUE if the cursor has been moved off of the last |
| 485 | ** row of output. |
| 486 | */ |
| 487 | static int builtinVtabEof(sqlite3_vtab_cursor *cur){ |
| 488 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; |
| 489 | return pCur->iRowid>=count(aBuiltinFiles); |
| 490 | } |
| 491 | |
| 492 | /* |
| 493 | ** This method is called to "rewind" the builtinVtab_cursor object back |
| 494 | ** to the first row of output. This method is always called at least |
| 495 | ** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or |
| 496 | ** builtinVtabEof(). |
| 497 | */ |
| 498 | static int builtinVtabFilter( |
| 499 | sqlite3_vtab_cursor *pVtabCursor, |
| 500 | int idxNum, const char *idxStr, |
| 501 | int argc, sqlite3_value **argv |
| 502 | ){ |
| 503 | builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor; |
| 504 | pCur->iRowid = 1; |
| 505 | return SQLITE_OK; |
| 506 | } |
| 507 | |
| 508 | /* |
| 509 | ** SQLite will invoke this method one or more times while planning a query |
| 510 | ** that uses the virtual table. This routine needs to create |
| 511 | ** a query plan for each invocation and compute an estimated cost for that |
| 512 | ** plan. |
| 513 | */ |
| 514 | static int builtinVtabBestIndex( |
| 515 | sqlite3_vtab *tab, |
| 516 | sqlite3_index_info *pIdxInfo |
| 517 | ){ |
| 518 | pIdxInfo->estimatedCost = (double)count(aBuiltinFiles); |
| 519 | pIdxInfo->estimatedRows = count(aBuiltinFiles); |
| 520 | return SQLITE_OK; |
| 521 | } |
| 522 | |
| 523 | /* |
| 524 | ** This following structure defines all the methods for the |
| 525 | ** virtual table. |
| 526 | */ |
| 527 | static sqlite3_module builtinVtabModule = { |
| 528 | /* iVersion */ 0, |
| 529 | /* xCreate */ 0, /* The builtin vtab is eponymous and read-only */ |
| 530 | /* xConnect */ builtinVtabConnect, |
| 531 | /* xBestIndex */ builtinVtabBestIndex, |
| 532 | /* xDisconnect */ builtinVtabDisconnect, |
| 533 | /* xDestroy */ 0, |
| 534 | /* xOpen */ builtinVtabOpen, |
| 535 | /* xClose */ builtinVtabClose, |
| 536 | /* xFilter */ builtinVtabFilter, |
| 537 | /* xNext */ builtinVtabNext, |
| 538 | /* xEof */ builtinVtabEof, |
| 539 | /* xColumn */ builtinVtabColumn, |
| 540 | /* xRowid */ builtinVtabRowid, |
| 541 | /* xUpdate */ 0, |
| 542 | /* xBegin */ 0, |
| 543 | /* xSync */ 0, |
| 544 | /* xCommit */ 0, |
| 545 | /* xRollback */ 0, |
| 546 | /* xFindMethod */ 0, |
| 547 | /* xRename */ 0, |
| 548 | /* xSavepoint */ 0, |
| 549 | /* xRelease */ 0, |
| 550 | /* xRollbackTo */ 0, |
| 551 | /* xShadowName */ 0 |
| 552 | }; |
| 553 | |
| 554 | |
| 555 | /* |
| 556 | ** Register the builtin virtual table |
| 557 | */ |
| 558 | int builtin_vtab_register(sqlite3 *db){ |
| 559 | int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0); |
| 560 | return rc; |
| 561 | } |
| 562 | /* End of the builtin virtual table |
| 563 | ******************************************************************************/ |
| 564 |
+2
-16
| --- src/bundle.c | ||
| +++ src/bundle.c | ||
| @@ -716,11 +716,11 @@ | ||
| 716 | 716 | } |
| 717 | 717 | db_end_transaction(0); |
| 718 | 718 | } |
| 719 | 719 | |
| 720 | 720 | /* |
| 721 | -** COMMAND: bundle | |
| 721 | +** COMMAND: bundle* | |
| 722 | 722 | ** |
| 723 | 723 | ** Usage: %fossil bundle SUBCOMMAND ARGS... |
| 724 | 724 | ** |
| 725 | 725 | ** > fossil bundle append BUNDLE FILE... |
| 726 | 726 | ** |
| @@ -768,25 +768,11 @@ | ||
| 768 | 768 | ** |
| 769 | 769 | ** Remove from the repository all files that are used exclusively |
| 770 | 770 | ** by check-ins in BUNDLE. This has the effect of undoing a |
| 771 | 771 | ** "fossil bundle import". |
| 772 | 772 | ** |
| 773 | -** SUMMARY: | |
| 774 | -** fossil bundle append BUNDLE FILE... Add files to BUNDLE | |
| 775 | -** fossil bundle cat BUNDLE HASH... Extract file from BUNDLE | |
| 776 | -** fossil bundle export BUNDLE ?OPTIONS? Create a new BUNDLE | |
| 777 | -** --branch BRANCH --from TAG1 --to TAG2 Check-ins to include | |
| 778 | -** --checkin TAG Use only check-in TAG | |
| 779 | -** --standalone Omit dependencies | |
| 780 | -** fossil bundle extend BUNDLE Update with newer content | |
| 781 | -** fossil bundle import BUNDLE ?OPTIONS? Import a bundle | |
| 782 | -** --publish Publish the import | |
| 783 | -** --force Cross-repo import | |
| 784 | -** fossil bundle ls BUNDLE List content of a bundle | |
| 785 | -** fossil bundle purge BUNDLE Undo an import | |
| 786 | -** | |
| 787 | -** See also: publish | |
| 773 | +** See also: [[publish]] | |
| 788 | 774 | */ |
| 789 | 775 | void bundle_cmd(void){ |
| 790 | 776 | const char *zSubcmd; |
| 791 | 777 | int n; |
| 792 | 778 | if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?OPTIONS?"); |
| 793 | 779 |
| --- src/bundle.c | |
| +++ src/bundle.c | |
| @@ -716,11 +716,11 @@ | |
| 716 | } |
| 717 | db_end_transaction(0); |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** COMMAND: bundle |
| 722 | ** |
| 723 | ** Usage: %fossil bundle SUBCOMMAND ARGS... |
| 724 | ** |
| 725 | ** > fossil bundle append BUNDLE FILE... |
| 726 | ** |
| @@ -768,25 +768,11 @@ | |
| 768 | ** |
| 769 | ** Remove from the repository all files that are used exclusively |
| 770 | ** by check-ins in BUNDLE. This has the effect of undoing a |
| 771 | ** "fossil bundle import". |
| 772 | ** |
| 773 | ** SUMMARY: |
| 774 | ** fossil bundle append BUNDLE FILE... Add files to BUNDLE |
| 775 | ** fossil bundle cat BUNDLE HASH... Extract file from BUNDLE |
| 776 | ** fossil bundle export BUNDLE ?OPTIONS? Create a new BUNDLE |
| 777 | ** --branch BRANCH --from TAG1 --to TAG2 Check-ins to include |
| 778 | ** --checkin TAG Use only check-in TAG |
| 779 | ** --standalone Omit dependencies |
| 780 | ** fossil bundle extend BUNDLE Update with newer content |
| 781 | ** fossil bundle import BUNDLE ?OPTIONS? Import a bundle |
| 782 | ** --publish Publish the import |
| 783 | ** --force Cross-repo import |
| 784 | ** fossil bundle ls BUNDLE List content of a bundle |
| 785 | ** fossil bundle purge BUNDLE Undo an import |
| 786 | ** |
| 787 | ** See also: publish |
| 788 | */ |
| 789 | void bundle_cmd(void){ |
| 790 | const char *zSubcmd; |
| 791 | int n; |
| 792 | if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?OPTIONS?"); |
| 793 |
| --- src/bundle.c | |
| +++ src/bundle.c | |
| @@ -716,11 +716,11 @@ | |
| 716 | } |
| 717 | db_end_transaction(0); |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** COMMAND: bundle* |
| 722 | ** |
| 723 | ** Usage: %fossil bundle SUBCOMMAND ARGS... |
| 724 | ** |
| 725 | ** > fossil bundle append BUNDLE FILE... |
| 726 | ** |
| @@ -768,25 +768,11 @@ | |
| 768 | ** |
| 769 | ** Remove from the repository all files that are used exclusively |
| 770 | ** by check-ins in BUNDLE. This has the effect of undoing a |
| 771 | ** "fossil bundle import". |
| 772 | ** |
| 773 | ** See also: [[publish]] |
| 774 | */ |
| 775 | void bundle_cmd(void){ |
| 776 | const char *zSubcmd; |
| 777 | int n; |
| 778 | if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?OPTIONS?"); |
| 779 |
+8
-8
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -303,24 +303,24 @@ | ||
| 303 | 303 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 304 | 304 | }else{ |
| 305 | 305 | assert( rangeEnd==0 ); |
| 306 | 306 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 307 | 307 | } |
| 308 | - if( g.isConst ){ | |
| 308 | + if( etag_tag()[0]!=0 ){ | |
| 309 | + fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); | |
| 310 | + fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); | |
| 311 | + if( etag_mtime()>0 ){ | |
| 312 | + fprintf(g.httpOut, "Last-Modified: %s\r\n", | |
| 313 | + cgi_rfc822_datestamp(etag_mtime())); | |
| 314 | + } | |
| 315 | + }else if( g.isConst ){ | |
| 309 | 316 | /* isConst means that the reply is guaranteed to be invariant, even |
| 310 | 317 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 311 | 318 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 312 | - }else if( etag_tag()[0]!=0 ){ | |
| 313 | - fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); | |
| 314 | - fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); | |
| 315 | 319 | }else{ |
| 316 | 320 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 317 | 321 | } |
| 318 | - if( etag_mtime()>0 ){ | |
| 319 | - fprintf(g.httpOut, "Last-Modified: %s\r\n", | |
| 320 | - cgi_rfc822_datestamp(etag_mtime())); | |
| 321 | - } | |
| 322 | 322 | |
| 323 | 323 | if( blob_size(&extraHeader)>0 ){ |
| 324 | 324 | fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); |
| 325 | 325 | } |
| 326 | 326 | |
| 327 | 327 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -303,24 +303,24 @@ | |
| 303 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 304 | }else{ |
| 305 | assert( rangeEnd==0 ); |
| 306 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 307 | } |
| 308 | if( g.isConst ){ |
| 309 | /* isConst means that the reply is guaranteed to be invariant, even |
| 310 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 311 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 312 | }else if( etag_tag()[0]!=0 ){ |
| 313 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 314 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 315 | }else{ |
| 316 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 317 | } |
| 318 | if( etag_mtime()>0 ){ |
| 319 | fprintf(g.httpOut, "Last-Modified: %s\r\n", |
| 320 | cgi_rfc822_datestamp(etag_mtime())); |
| 321 | } |
| 322 | |
| 323 | if( blob_size(&extraHeader)>0 ){ |
| 324 | fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); |
| 325 | } |
| 326 | |
| 327 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -303,24 +303,24 @@ | |
| 303 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 304 | }else{ |
| 305 | assert( rangeEnd==0 ); |
| 306 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 307 | } |
| 308 | if( etag_tag()[0]!=0 ){ |
| 309 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 310 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 311 | if( etag_mtime()>0 ){ |
| 312 | fprintf(g.httpOut, "Last-Modified: %s\r\n", |
| 313 | cgi_rfc822_datestamp(etag_mtime())); |
| 314 | } |
| 315 | }else if( g.isConst ){ |
| 316 | /* isConst means that the reply is guaranteed to be invariant, even |
| 317 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 318 | fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n"); |
| 319 | }else{ |
| 320 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 321 | } |
| 322 | |
| 323 | if( blob_size(&extraHeader)>0 ){ |
| 324 | fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); |
| 325 | } |
| 326 | |
| 327 |
+5
-5
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -440,11 +440,11 @@ | ||
| 440 | 440 | ** --extra Display unmanaged files. |
| 441 | 441 | ** --differ Display modified and extra files. |
| 442 | 442 | ** --merge Display merge contributors. |
| 443 | 443 | ** --no-merge Do not display merge contributors. |
| 444 | 444 | ** |
| 445 | -** See also: extras, ls | |
| 445 | +** See also: [[extras]], [[ls]] | |
| 446 | 446 | */ |
| 447 | 447 | void status_cmd(void){ |
| 448 | 448 | /* Affirmative and negative flag option tables. */ |
| 449 | 449 | static const struct { |
| 450 | 450 | const char *option; /* Flag name. */ |
| @@ -676,11 +676,11 @@ | ||
| 676 | 676 | ** -v|--verbose Provide extra information about each file. |
| 677 | 677 | ** -t Sort output in time order. |
| 678 | 678 | ** -r VERSION The specific check-in to list. |
| 679 | 679 | ** -R|--repository FILE Extract info from repository FILE. |
| 680 | 680 | ** |
| 681 | -** See also: changes, extras, status | |
| 681 | +** See also: [[changes]], [[extras]], [[status]] | |
| 682 | 682 | */ |
| 683 | 683 | void ls_cmd(void){ |
| 684 | 684 | int vid; |
| 685 | 685 | Stmt q; |
| 686 | 686 | int verboseFlag; |
| @@ -830,11 +830,11 @@ | ||
| 830 | 830 | ** --header Identify the repository if there are extras |
| 831 | 831 | ** --ignore CSG Ignore files matching patterns from the argument |
| 832 | 832 | ** --rel-paths Display pathnames relative to the current working |
| 833 | 833 | ** directory. |
| 834 | 834 | ** |
| 835 | -** See also: changes, clean, status | |
| 835 | +** See also: [[changes]], [[clean]], [[status]] | |
| 836 | 836 | */ |
| 837 | 837 | void extras_cmd(void){ |
| 838 | 838 | Blob report = BLOB_INITIALIZER; |
| 839 | 839 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 840 | 840 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| @@ -954,11 +954,11 @@ | ||
| 954 | 954 | ** --no-prompt This option disables prompting the user for input |
| 955 | 955 | ** and assumes an answer of 'No' for every question. |
| 956 | 956 | ** --temp Remove only Fossil-generated temporary files. |
| 957 | 957 | ** -v|--verbose Show all files as they are removed. |
| 958 | 958 | ** |
| 959 | -** See also: addremove, extras, status | |
| 959 | +** See also: [[addremove]], [[extras]], [[status]] | |
| 960 | 960 | */ |
| 961 | 961 | void clean_cmd(void){ |
| 962 | 962 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 963 | 963 | int emptyDirsFlag, dirsOnlyFlag; |
| 964 | 964 | int disableUndo, noPrompt; |
| @@ -2091,11 +2091,11 @@ | ||
| 2091 | 2091 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2092 | 2092 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2093 | 2093 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2094 | 2094 | ** means UTC. |
| 2095 | 2095 | ** |
| 2096 | -** See also: branch, changes, checkout, extras, sync | |
| 2096 | +** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] | |
| 2097 | 2097 | */ |
| 2098 | 2098 | void commit_cmd(void){ |
| 2099 | 2099 | int hasChanges; /* True if unsaved changes exist */ |
| 2100 | 2100 | int vid; /* blob-id of parent version */ |
| 2101 | 2101 | int nrid; /* blob-id of a modified file */ |
| 2102 | 2102 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -440,11 +440,11 @@ | |
| 440 | ** --extra Display unmanaged files. |
| 441 | ** --differ Display modified and extra files. |
| 442 | ** --merge Display merge contributors. |
| 443 | ** --no-merge Do not display merge contributors. |
| 444 | ** |
| 445 | ** See also: extras, ls |
| 446 | */ |
| 447 | void status_cmd(void){ |
| 448 | /* Affirmative and negative flag option tables. */ |
| 449 | static const struct { |
| 450 | const char *option; /* Flag name. */ |
| @@ -676,11 +676,11 @@ | |
| 676 | ** -v|--verbose Provide extra information about each file. |
| 677 | ** -t Sort output in time order. |
| 678 | ** -r VERSION The specific check-in to list. |
| 679 | ** -R|--repository FILE Extract info from repository FILE. |
| 680 | ** |
| 681 | ** See also: changes, extras, status |
| 682 | */ |
| 683 | void ls_cmd(void){ |
| 684 | int vid; |
| 685 | Stmt q; |
| 686 | int verboseFlag; |
| @@ -830,11 +830,11 @@ | |
| 830 | ** --header Identify the repository if there are extras |
| 831 | ** --ignore CSG Ignore files matching patterns from the argument |
| 832 | ** --rel-paths Display pathnames relative to the current working |
| 833 | ** directory. |
| 834 | ** |
| 835 | ** See also: changes, clean, status |
| 836 | */ |
| 837 | void extras_cmd(void){ |
| 838 | Blob report = BLOB_INITIALIZER; |
| 839 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 840 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| @@ -954,11 +954,11 @@ | |
| 954 | ** --no-prompt This option disables prompting the user for input |
| 955 | ** and assumes an answer of 'No' for every question. |
| 956 | ** --temp Remove only Fossil-generated temporary files. |
| 957 | ** -v|--verbose Show all files as they are removed. |
| 958 | ** |
| 959 | ** See also: addremove, extras, status |
| 960 | */ |
| 961 | void clean_cmd(void){ |
| 962 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 963 | int emptyDirsFlag, dirsOnlyFlag; |
| 964 | int disableUndo, noPrompt; |
| @@ -2091,11 +2091,11 @@ | |
| 2091 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2092 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2093 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2094 | ** means UTC. |
| 2095 | ** |
| 2096 | ** See also: branch, changes, checkout, extras, sync |
| 2097 | */ |
| 2098 | void commit_cmd(void){ |
| 2099 | int hasChanges; /* True if unsaved changes exist */ |
| 2100 | int vid; /* blob-id of parent version */ |
| 2101 | int nrid; /* blob-id of a modified file */ |
| 2102 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -440,11 +440,11 @@ | |
| 440 | ** --extra Display unmanaged files. |
| 441 | ** --differ Display modified and extra files. |
| 442 | ** --merge Display merge contributors. |
| 443 | ** --no-merge Do not display merge contributors. |
| 444 | ** |
| 445 | ** See also: [[extras]], [[ls]] |
| 446 | */ |
| 447 | void status_cmd(void){ |
| 448 | /* Affirmative and negative flag option tables. */ |
| 449 | static const struct { |
| 450 | const char *option; /* Flag name. */ |
| @@ -676,11 +676,11 @@ | |
| 676 | ** -v|--verbose Provide extra information about each file. |
| 677 | ** -t Sort output in time order. |
| 678 | ** -r VERSION The specific check-in to list. |
| 679 | ** -R|--repository FILE Extract info from repository FILE. |
| 680 | ** |
| 681 | ** See also: [[changes]], [[extras]], [[status]] |
| 682 | */ |
| 683 | void ls_cmd(void){ |
| 684 | int vid; |
| 685 | Stmt q; |
| 686 | int verboseFlag; |
| @@ -830,11 +830,11 @@ | |
| 830 | ** --header Identify the repository if there are extras |
| 831 | ** --ignore CSG Ignore files matching patterns from the argument |
| 832 | ** --rel-paths Display pathnames relative to the current working |
| 833 | ** directory. |
| 834 | ** |
| 835 | ** See also: [[changes]], [[clean]], [[status]] |
| 836 | */ |
| 837 | void extras_cmd(void){ |
| 838 | Blob report = BLOB_INITIALIZER; |
| 839 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 840 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| @@ -954,11 +954,11 @@ | |
| 954 | ** --no-prompt This option disables prompting the user for input |
| 955 | ** and assumes an answer of 'No' for every question. |
| 956 | ** --temp Remove only Fossil-generated temporary files. |
| 957 | ** -v|--verbose Show all files as they are removed. |
| 958 | ** |
| 959 | ** See also: [[addremove]], [[extras]], [[status]] |
| 960 | */ |
| 961 | void clean_cmd(void){ |
| 962 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 963 | int emptyDirsFlag, dirsOnlyFlag; |
| 964 | int disableUndo, noPrompt; |
| @@ -2091,11 +2091,11 @@ | |
| 2091 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2092 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2093 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2094 | ** means UTC. |
| 2095 | ** |
| 2096 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2097 | */ |
| 2098 | void commit_cmd(void){ |
| 2099 | int hasChanges; /* True if unsaved changes exist */ |
| 2100 | int vid; /* blob-id of parent version */ |
| 2101 | int nrid; /* blob-id of a modified file */ |
| 2102 |
+3
-3
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -279,11 +279,11 @@ | ||
| 279 | 279 | ** --force-missing Force checkout even if content is missing |
| 280 | 280 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 281 | 281 | ** times (the timestamp of the last checkin which modified |
| 282 | 282 | ** them). |
| 283 | 283 | ** |
| 284 | -** See also: update | |
| 284 | +** See also: [[update]] | |
| 285 | 285 | */ |
| 286 | 286 | void checkout_cmd(void){ |
| 287 | 287 | int forceFlag; /* Force checkout even if edits exist */ |
| 288 | 288 | int forceMissingFlag; /* Force checkout even if missing content */ |
| 289 | 289 | int keepFlag; /* Do not change any files on disk */ |
| @@ -387,18 +387,18 @@ | ||
| 387 | 387 | /* |
| 388 | 388 | ** COMMAND: close* |
| 389 | 389 | ** |
| 390 | 390 | ** Usage: %fossil close ?OPTIONS? |
| 391 | 391 | ** |
| 392 | -** The opposite of "open". Close the current database connection. | |
| 392 | +** The opposite of "[[open]]". Close the current database connection. | |
| 393 | 393 | ** Require a -f or --force flag if there are unsaved changes in the |
| 394 | 394 | ** current check-out or if there is non-empty stash. |
| 395 | 395 | ** |
| 396 | 396 | ** Options: |
| 397 | 397 | ** --force|-f necessary to close a check out with uncommitted changes |
| 398 | 398 | ** |
| 399 | -** See also: open | |
| 399 | +** See also: [[open]] | |
| 400 | 400 | */ |
| 401 | 401 | void close_cmd(void){ |
| 402 | 402 | int forceFlag = find_option("force","f",0)!=0; |
| 403 | 403 | db_must_be_within_tree(); |
| 404 | 404 | |
| 405 | 405 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -279,11 +279,11 @@ | |
| 279 | ** --force-missing Force checkout even if content is missing |
| 280 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 281 | ** times (the timestamp of the last checkin which modified |
| 282 | ** them). |
| 283 | ** |
| 284 | ** See also: update |
| 285 | */ |
| 286 | void checkout_cmd(void){ |
| 287 | int forceFlag; /* Force checkout even if edits exist */ |
| 288 | int forceMissingFlag; /* Force checkout even if missing content */ |
| 289 | int keepFlag; /* Do not change any files on disk */ |
| @@ -387,18 +387,18 @@ | |
| 387 | /* |
| 388 | ** COMMAND: close* |
| 389 | ** |
| 390 | ** Usage: %fossil close ?OPTIONS? |
| 391 | ** |
| 392 | ** The opposite of "open". Close the current database connection. |
| 393 | ** Require a -f or --force flag if there are unsaved changes in the |
| 394 | ** current check-out or if there is non-empty stash. |
| 395 | ** |
| 396 | ** Options: |
| 397 | ** --force|-f necessary to close a check out with uncommitted changes |
| 398 | ** |
| 399 | ** See also: open |
| 400 | */ |
| 401 | void close_cmd(void){ |
| 402 | int forceFlag = find_option("force","f",0)!=0; |
| 403 | db_must_be_within_tree(); |
| 404 | |
| 405 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -279,11 +279,11 @@ | |
| 279 | ** --force-missing Force checkout even if content is missing |
| 280 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 281 | ** times (the timestamp of the last checkin which modified |
| 282 | ** them). |
| 283 | ** |
| 284 | ** See also: [[update]] |
| 285 | */ |
| 286 | void checkout_cmd(void){ |
| 287 | int forceFlag; /* Force checkout even if edits exist */ |
| 288 | int forceMissingFlag; /* Force checkout even if missing content */ |
| 289 | int keepFlag; /* Do not change any files on disk */ |
| @@ -387,18 +387,18 @@ | |
| 387 | /* |
| 388 | ** COMMAND: close* |
| 389 | ** |
| 390 | ** Usage: %fossil close ?OPTIONS? |
| 391 | ** |
| 392 | ** The opposite of "[[open]]". Close the current database connection. |
| 393 | ** Require a -f or --force flag if there are unsaved changes in the |
| 394 | ** current check-out or if there is non-empty stash. |
| 395 | ** |
| 396 | ** Options: |
| 397 | ** --force|-f necessary to close a check out with uncommitted changes |
| 398 | ** |
| 399 | ** See also: [[open]] |
| 400 | */ |
| 401 | void close_cmd(void){ |
| 402 | int forceFlag = find_option("force","f",0)!=0; |
| 403 | db_must_be_within_tree(); |
| 404 | |
| 405 |
+9
-7
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -83,13 +83,11 @@ | ||
| 83 | 83 | ** COMMAND: clone |
| 84 | 84 | ** |
| 85 | 85 | ** Usage: %fossil clone ?OPTIONS? URI FILENAME |
| 86 | 86 | ** |
| 87 | 87 | ** Make a clone of a repository specified by URI in the local |
| 88 | -** file named FILENAME. | |
| 89 | -** | |
| 90 | -** URI may be one of the following forms: | |
| 88 | +** file named FILENAME. URI may be one of the following forms: | |
| 91 | 89 | ** ([...] denotes optional elements): |
| 92 | 90 | ** |
| 93 | 91 | ** * HTTP/HTTPS protocol: |
| 94 | 92 | ** |
| 95 | 93 | ** http[s]://[userid[:password]@]host[:port][/path] |
| @@ -100,18 +98,22 @@ | ||
| 100 | 98 | ** |
| 101 | 99 | ** * Filesystem: |
| 102 | 100 | ** |
| 103 | 101 | ** [file://]path/to/repo.fossil |
| 104 | 102 | ** |
| 105 | -** Note 1: For ssh and filesystem, path must have an extra leading | |
| 103 | +** Note that in Fossil (in contrast to some other DVCSes) a repository | |
| 104 | +** is distinct from a checkout. This command create a clone of a repository. | |
| 105 | +** Use the separate [[open]] command to open a checkout from that repository. | |
| 106 | +** | |
| 107 | +** For ssh and filesystem, path must have an extra leading | |
| 106 | 108 | ** '/' to use an absolute path. |
| 107 | 109 | ** |
| 108 | -** Note 2: Use %HH escapes for special characters in the userid and | |
| 110 | +** Use %HH escapes for special characters in the userid and | |
| 109 | 111 | ** password. For example "%40" in place of "@", "%2f" in place |
| 110 | 112 | ** of "/", and "%3a" in place of ":". |
| 111 | 113 | ** |
| 112 | -** By default, your current login name is used to create the default | |
| 114 | +** By default, the current login name is used to create the default | |
| 113 | 115 | ** admin user. This can be overridden using the -A|--admin-user |
| 114 | 116 | ** parameter. |
| 115 | 117 | ** |
| 116 | 118 | ** Options: |
| 117 | 119 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| @@ -123,11 +125,11 @@ | ||
| 123 | 125 | ** --ssh-command|-c SSH Use SSH as the "ssh" command |
| 124 | 126 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 125 | 127 | ** -u|--unversioned Also sync unversioned content |
| 126 | 128 | ** -v|--verbose Show more statistics in output |
| 127 | 129 | ** |
| 128 | -** See also: init | |
| 130 | +** See also: [[init]], [[open]] | |
| 129 | 131 | */ |
| 130 | 132 | void clone_cmd(void){ |
| 131 | 133 | char *zPassword; |
| 132 | 134 | const char *zDefaultUser; /* Optional name of the default user */ |
| 133 | 135 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 134 | 136 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -83,13 +83,11 @@ | |
| 83 | ** COMMAND: clone |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URI FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URI in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** URI may be one of the following forms: |
| 91 | ** ([...] denotes optional elements): |
| 92 | ** |
| 93 | ** * HTTP/HTTPS protocol: |
| 94 | ** |
| 95 | ** http[s]://[userid[:password]@]host[:port][/path] |
| @@ -100,18 +98,22 @@ | |
| 100 | ** |
| 101 | ** * Filesystem: |
| 102 | ** |
| 103 | ** [file://]path/to/repo.fossil |
| 104 | ** |
| 105 | ** Note 1: For ssh and filesystem, path must have an extra leading |
| 106 | ** '/' to use an absolute path. |
| 107 | ** |
| 108 | ** Note 2: Use %HH escapes for special characters in the userid and |
| 109 | ** password. For example "%40" in place of "@", "%2f" in place |
| 110 | ** of "/", and "%3a" in place of ":". |
| 111 | ** |
| 112 | ** By default, your current login name is used to create the default |
| 113 | ** admin user. This can be overridden using the -A|--admin-user |
| 114 | ** parameter. |
| 115 | ** |
| 116 | ** Options: |
| 117 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| @@ -123,11 +125,11 @@ | |
| 123 | ** --ssh-command|-c SSH Use SSH as the "ssh" command |
| 124 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 125 | ** -u|--unversioned Also sync unversioned content |
| 126 | ** -v|--verbose Show more statistics in output |
| 127 | ** |
| 128 | ** See also: init |
| 129 | */ |
| 130 | void clone_cmd(void){ |
| 131 | char *zPassword; |
| 132 | const char *zDefaultUser; /* Optional name of the default user */ |
| 133 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 134 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -83,13 +83,11 @@ | |
| 83 | ** COMMAND: clone |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URI FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URI in the local |
| 88 | ** file named FILENAME. URI may be one of the following forms: |
| 89 | ** ([...] denotes optional elements): |
| 90 | ** |
| 91 | ** * HTTP/HTTPS protocol: |
| 92 | ** |
| 93 | ** http[s]://[userid[:password]@]host[:port][/path] |
| @@ -100,18 +98,22 @@ | |
| 98 | ** |
| 99 | ** * Filesystem: |
| 100 | ** |
| 101 | ** [file://]path/to/repo.fossil |
| 102 | ** |
| 103 | ** Note that in Fossil (in contrast to some other DVCSes) a repository |
| 104 | ** is distinct from a checkout. This command create a clone of a repository. |
| 105 | ** Use the separate [[open]] command to open a checkout from that repository. |
| 106 | ** |
| 107 | ** For ssh and filesystem, path must have an extra leading |
| 108 | ** '/' to use an absolute path. |
| 109 | ** |
| 110 | ** Use %HH escapes for special characters in the userid and |
| 111 | ** password. For example "%40" in place of "@", "%2f" in place |
| 112 | ** of "/", and "%3a" in place of ":". |
| 113 | ** |
| 114 | ** By default, the current login name is used to create the default |
| 115 | ** admin user. This can be overridden using the -A|--admin-user |
| 116 | ** parameter. |
| 117 | ** |
| 118 | ** Options: |
| 119 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| @@ -123,11 +125,11 @@ | |
| 125 | ** --ssh-command|-c SSH Use SSH as the "ssh" command |
| 126 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 127 | ** -u|--unversioned Also sync unversioned content |
| 128 | ** -v|--verbose Show more statistics in output |
| 129 | ** |
| 130 | ** See also: [[init]], [[open]] |
| 131 | */ |
| 132 | void clone_cmd(void){ |
| 133 | char *zPassword; |
| 134 | const char *zDefaultUser; /* Optional name of the default user */ |
| 135 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 136 |
-1
| --- src/codecheck1.c | ||
| +++ src/codecheck1.c | ||
| @@ -407,11 +407,10 @@ | ||
| 407 | 407 | { "smtp_send_line", 2, FMT_SAFE }, |
| 408 | 408 | { "smtp_server_send", 2, FMT_SAFE }, |
| 409 | 409 | { "socket_set_errmsg", 1, FMT_SAFE }, |
| 410 | 410 | { "ssl_set_errmsg", 1, FMT_SAFE }, |
| 411 | 411 | { "style_header", 1, FMT_HTML }, |
| 412 | - { "style_js_onload", 1, FMT_HTML }, | |
| 413 | 412 | { "style_set_current_page", 1, FMT_URL }, |
| 414 | 413 | { "style_submenu_element", 2, FMT_URL }, |
| 415 | 414 | { "style_submenu_sql", 3, FMT_SQL }, |
| 416 | 415 | { "webpage_error", 1, FMT_SAFE }, |
| 417 | 416 | { "xhref", 2, FMT_URL }, |
| 418 | 417 |
| --- src/codecheck1.c | |
| +++ src/codecheck1.c | |
| @@ -407,11 +407,10 @@ | |
| 407 | { "smtp_send_line", 2, FMT_SAFE }, |
| 408 | { "smtp_server_send", 2, FMT_SAFE }, |
| 409 | { "socket_set_errmsg", 1, FMT_SAFE }, |
| 410 | { "ssl_set_errmsg", 1, FMT_SAFE }, |
| 411 | { "style_header", 1, FMT_HTML }, |
| 412 | { "style_js_onload", 1, FMT_HTML }, |
| 413 | { "style_set_current_page", 1, FMT_URL }, |
| 414 | { "style_submenu_element", 2, FMT_URL }, |
| 415 | { "style_submenu_sql", 3, FMT_SQL }, |
| 416 | { "webpage_error", 1, FMT_SAFE }, |
| 417 | { "xhref", 2, FMT_URL }, |
| 418 |
| --- src/codecheck1.c | |
| +++ src/codecheck1.c | |
| @@ -407,11 +407,10 @@ | |
| 407 | { "smtp_send_line", 2, FMT_SAFE }, |
| 408 | { "smtp_server_send", 2, FMT_SAFE }, |
| 409 | { "socket_set_errmsg", 1, FMT_SAFE }, |
| 410 | { "ssl_set_errmsg", 1, FMT_SAFE }, |
| 411 | { "style_header", 1, FMT_HTML }, |
| 412 | { "style_set_current_page", 1, FMT_URL }, |
| 413 | { "style_submenu_element", 2, FMT_URL }, |
| 414 | { "style_submenu_sql", 3, FMT_SQL }, |
| 415 | { "webpage_error", 1, FMT_SAFE }, |
| 416 | { "xhref", 2, FMT_URL }, |
| 417 |
+4
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -94,10 +94,12 @@ | ||
| 94 | 94 | { "js", CONFIGSET_SKIN }, |
| 95 | 95 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 96 | 96 | { "logo-image", CONFIGSET_SKIN }, |
| 97 | 97 | { "background-mimetype", CONFIGSET_SKIN }, |
| 98 | 98 | { "background-image", CONFIGSET_SKIN }, |
| 99 | + { "icon-mimetype", CONFIGSET_SKIN }, | |
| 100 | + { "icon-image", CONFIGSET_SKIN }, | |
| 99 | 101 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 100 | 102 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 101 | 103 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 102 | 104 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 103 | 105 | { "timeline-closetime", CONFIGSET_SKIN }, |
| @@ -112,10 +114,11 @@ | ||
| 112 | 114 | { "default-csp", CONFIGSET_SKIN }, |
| 113 | 115 | { "sitemap-docidx", CONFIGSET_SKIN }, |
| 114 | 116 | { "sitemap-download", CONFIGSET_SKIN }, |
| 115 | 117 | { "sitemap-license", CONFIGSET_SKIN }, |
| 116 | 118 | { "sitemap-contact", CONFIGSET_SKIN }, |
| 119 | + { "safe-html", CONFIGSET_SKIN }, | |
| 117 | 120 | |
| 118 | 121 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 119 | 122 | { "th1-docs", CONFIGSET_TH1 }, |
| 120 | 123 | #endif |
| 121 | 124 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| @@ -744,11 +747,11 @@ | ||
| 744 | 747 | ** the remote repository at URL. |
| 745 | 748 | ** |
| 746 | 749 | ** Options: |
| 747 | 750 | ** -R|--repository FILE Extract info from repository FILE |
| 748 | 751 | ** |
| 749 | -** See also: settings, unset | |
| 752 | +** See also: [[settings]], [[unset]] | |
| 750 | 753 | */ |
| 751 | 754 | void configuration_cmd(void){ |
| 752 | 755 | int n; |
| 753 | 756 | const char *zMethod; |
| 754 | 757 | db_find_and_open_repository(0, 0); |
| 755 | 758 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -94,10 +94,12 @@ | |
| 94 | { "js", CONFIGSET_SKIN }, |
| 95 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 96 | { "logo-image", CONFIGSET_SKIN }, |
| 97 | { "background-mimetype", CONFIGSET_SKIN }, |
| 98 | { "background-image", CONFIGSET_SKIN }, |
| 99 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 100 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 101 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 102 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 103 | { "timeline-closetime", CONFIGSET_SKIN }, |
| @@ -112,10 +114,11 @@ | |
| 112 | { "default-csp", CONFIGSET_SKIN }, |
| 113 | { "sitemap-docidx", CONFIGSET_SKIN }, |
| 114 | { "sitemap-download", CONFIGSET_SKIN }, |
| 115 | { "sitemap-license", CONFIGSET_SKIN }, |
| 116 | { "sitemap-contact", CONFIGSET_SKIN }, |
| 117 | |
| 118 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 119 | { "th1-docs", CONFIGSET_TH1 }, |
| 120 | #endif |
| 121 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| @@ -744,11 +747,11 @@ | |
| 744 | ** the remote repository at URL. |
| 745 | ** |
| 746 | ** Options: |
| 747 | ** -R|--repository FILE Extract info from repository FILE |
| 748 | ** |
| 749 | ** See also: settings, unset |
| 750 | */ |
| 751 | void configuration_cmd(void){ |
| 752 | int n; |
| 753 | const char *zMethod; |
| 754 | db_find_and_open_repository(0, 0); |
| 755 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -94,10 +94,12 @@ | |
| 94 | { "js", CONFIGSET_SKIN }, |
| 95 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 96 | { "logo-image", CONFIGSET_SKIN }, |
| 97 | { "background-mimetype", CONFIGSET_SKIN }, |
| 98 | { "background-image", CONFIGSET_SKIN }, |
| 99 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 100 | { "icon-image", CONFIGSET_SKIN }, |
| 101 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 102 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 103 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 104 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 105 | { "timeline-closetime", CONFIGSET_SKIN }, |
| @@ -112,10 +114,11 @@ | |
| 114 | { "default-csp", CONFIGSET_SKIN }, |
| 115 | { "sitemap-docidx", CONFIGSET_SKIN }, |
| 116 | { "sitemap-download", CONFIGSET_SKIN }, |
| 117 | { "sitemap-license", CONFIGSET_SKIN }, |
| 118 | { "sitemap-contact", CONFIGSET_SKIN }, |
| 119 | { "safe-html", CONFIGSET_SKIN }, |
| 120 | |
| 121 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 122 | { "th1-docs", CONFIGSET_TH1 }, |
| 123 | #endif |
| 124 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| @@ -744,11 +747,11 @@ | |
| 747 | ** the remote repository at URL. |
| 748 | ** |
| 749 | ** Options: |
| 750 | ** -R|--repository FILE Extract info from repository FILE |
| 751 | ** |
| 752 | ** See also: [[settings]], [[unset]] |
| 753 | */ |
| 754 | void configuration_cmd(void){ |
| 755 | int n; |
| 756 | const char *zMethod; |
| 757 | db_find_and_open_repository(0, 0); |
| 758 |
+1
-1
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -326,11 +326,11 @@ | ||
| 326 | 326 | ** the named output file. |
| 327 | 327 | ** |
| 328 | 328 | ** Options: |
| 329 | 329 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 330 | 330 | ** |
| 331 | -** See also: finfo | |
| 331 | +** See also: [[finfo]] | |
| 332 | 332 | */ |
| 333 | 333 | void artifact_cmd(void){ |
| 334 | 334 | int rid; |
| 335 | 335 | Blob content; |
| 336 | 336 | const char *zFile; |
| 337 | 337 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | ** the named output file. |
| 327 | ** |
| 328 | ** Options: |
| 329 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 330 | ** |
| 331 | ** See also: finfo |
| 332 | */ |
| 333 | void artifact_cmd(void){ |
| 334 | int rid; |
| 335 | Blob content; |
| 336 | const char *zFile; |
| 337 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | ** the named output file. |
| 327 | ** |
| 328 | ** Options: |
| 329 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 330 | ** |
| 331 | ** See also: [[finfo]] |
| 332 | */ |
| 333 | void artifact_cmd(void){ |
| 334 | int rid; |
| 335 | Blob content; |
| 336 | const char *zFile; |
| 337 |
M
src/db.c
+131
-19
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -116,10 +116,11 @@ | ||
| 116 | 116 | */ |
| 117 | 117 | static struct DbLocalData { |
| 118 | 118 | int nBegin; /* Nesting depth of BEGIN */ |
| 119 | 119 | int doRollback; /* True to force a rollback */ |
| 120 | 120 | int nCommitHook; /* Number of commit hooks */ |
| 121 | + int wrTxn; /* Outer-most TNX is a write */ | |
| 121 | 122 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| 122 | 123 | int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ |
| 123 | 124 | int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ |
| 124 | 125 | struct sCommitHook { |
| 125 | 126 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| @@ -195,10 +196,11 @@ | ||
| 195 | 196 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 196 | 197 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 197 | 198 | db.doRollback = 0; |
| 198 | 199 | db.zStartFile = zStartFile; |
| 199 | 200 | db.iStartLine = iStartLine; |
| 201 | + db.wrTxn = 0; | |
| 200 | 202 | } |
| 201 | 203 | db.nBegin++; |
| 202 | 204 | } |
| 203 | 205 | /* |
| 204 | 206 | ** Begin a new transaction for writing. |
| @@ -209,11 +211,12 @@ | ||
| 209 | 211 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 210 | 212 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 211 | 213 | db.doRollback = 0; |
| 212 | 214 | db.zStartFile = zStartFile; |
| 213 | 215 | db.iStartLine = iStartLine; |
| 214 | - }else{ | |
| 216 | + db.wrTxn = 1; | |
| 217 | + }else if( !db.wrTxn ){ | |
| 215 | 218 | fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " |
| 216 | 219 | "for the write txn at %s:%d", |
| 217 | 220 | db.zStartFile, db.iStartLine, zStartFile, iStartLine); |
| 218 | 221 | } |
| 219 | 222 | db.nBegin++; |
| @@ -1328,11 +1331,11 @@ | ||
| 1328 | 1331 | ); |
| 1329 | 1332 | if( rc!=SQLITE_OK ){ |
| 1330 | 1333 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1331 | 1334 | } |
| 1332 | 1335 | db_maybe_set_encryption_key(db, zDbName); |
| 1333 | - sqlite3_busy_timeout(db, 5000); | |
| 1336 | + sqlite3_busy_timeout(db, 15000); | |
| 1334 | 1337 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1335 | 1338 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1336 | 1339 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1337 | 1340 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1338 | 1341 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| @@ -1344,11 +1347,11 @@ | ||
| 1344 | 1347 | ); |
| 1345 | 1348 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1346 | 1349 | db_add_aux_functions(db); |
| 1347 | 1350 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1348 | 1351 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1349 | - sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 1352 | + sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); | |
| 1350 | 1353 | return db; |
| 1351 | 1354 | } |
| 1352 | 1355 | |
| 1353 | 1356 | |
| 1354 | 1357 | /* |
| @@ -1714,16 +1717,20 @@ | ||
| 1714 | 1717 | ** |
| 1715 | 1718 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1716 | 1719 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1717 | 1720 | ** to the root of the repository tree and this routine returns 1. If |
| 1718 | 1721 | ** no database is found, then this routine return 0. |
| 1722 | +** | |
| 1723 | +** In db_open_local_v2(), if the bRootOnly flag is true, then only | |
| 1724 | +** look in the CWD for the checkout database. Do not scan upwards in | |
| 1725 | +** the file hierarchy. | |
| 1719 | 1726 | ** |
| 1720 | 1727 | ** This routine always opens the user database regardless of whether or |
| 1721 | 1728 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1722 | 1729 | ** is found, it is attached to the open database connection too. |
| 1723 | 1730 | */ |
| 1724 | -int db_open_local(const char *zDbName){ | |
| 1731 | +int db_open_local_v2(const char *zDbName, int bRootOnly){ | |
| 1725 | 1732 | int i, n; |
| 1726 | 1733 | char zPwd[2000]; |
| 1727 | 1734 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1728 | 1735 | |
| 1729 | 1736 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1754,22 @@ | ||
| 1747 | 1754 | g.localOpen = 1; |
| 1748 | 1755 | db_open_repository(zDbName); |
| 1749 | 1756 | return 1; |
| 1750 | 1757 | } |
| 1751 | 1758 | } |
| 1759 | + if( bRootOnly ) break; | |
| 1752 | 1760 | n--; |
| 1753 | 1761 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1754 | 1762 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1755 | 1763 | zPwd[n] = 0; |
| 1756 | 1764 | } |
| 1757 | 1765 | |
| 1758 | 1766 | /* A checkout database file could not be found */ |
| 1759 | 1767 | return 0; |
| 1768 | +} | |
| 1769 | +int db_open_local(const char *zDbName){ | |
| 1770 | + return db_open_local_v2(zDbName, 0); | |
| 1760 | 1771 | } |
| 1761 | 1772 | |
| 1762 | 1773 | /* |
| 1763 | 1774 | ** Get the full pathname to the repository database file. The |
| 1764 | 1775 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -2388,11 +2399,11 @@ | ||
| 2388 | 2399 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2389 | 2400 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2390 | 2401 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2391 | 2402 | ** means UTC. |
| 2392 | 2403 | ** |
| 2393 | -** See also: clone | |
| 2404 | +** See also: [[clone]] | |
| 2394 | 2405 | */ |
| 2395 | 2406 | void create_repository_cmd(void){ |
| 2396 | 2407 | char *zPassword; |
| 2397 | 2408 | const char *zTemplate; /* Repository from which to copy settings */ |
| 2398 | 2409 | const char *zDate; /* Date of the initial check-in */ |
| @@ -3070,57 +3081,158 @@ | ||
| 3070 | 3081 | } |
| 3071 | 3082 | |
| 3072 | 3083 | /* |
| 3073 | 3084 | ** COMMAND: open |
| 3074 | 3085 | ** |
| 3075 | -** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS? | |
| 3086 | +** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS? | |
| 3087 | +** | |
| 3088 | +** Open a new connection to the repository name REPOSITORY. A checkout | |
| 3089 | +** for the repository is created with its root at the current working | |
| 3090 | +** directory, or in DIR if the "--workdir DIR" is used. If VERSION is | |
| 3091 | +** specified then that version is checked out. Otherwise the most recent | |
| 3092 | +** check-in on the main branch (usually "trunk") is used. | |
| 3093 | +** | |
| 3094 | +** REPOSITORY can be the filename for a repository that already exists on the | |
| 3095 | +** local machine or it can be a URI for a remote repository. If REPOSITORY | |
| 3096 | +** is a URI in one of the formats recognized by the [[clone]] command, then | |
| 3097 | +** remote repo is first cloned, then the clone is opened. The clone will be | |
| 3098 | +** stored in the current directory, or in DIR if the "--repodir DIR" option | |
| 3099 | +** is used. The name of the clone will be taken from the last term of the URI. | |
| 3100 | +** For "http:" and "https:" URIs, you can append an extra term to the end of | |
| 3101 | +** the URI to get any repository name you like. For example: | |
| 3102 | +** | |
| 3103 | +** fossil open https://fossil-scm.org/home/new-name | |
| 3076 | 3104 | ** |
| 3077 | -** Open a connection to the local repository in FILENAME. A checkout | |
| 3078 | -** for the repository is created with its root at the working directory. | |
| 3079 | -** If VERSION is specified then that version is checked out. Otherwise | |
| 3080 | -** the latest version is checked out. No files other than "manifest" | |
| 3081 | -** and "manifest.uuid" are modified if the --keep option is present. | |
| 3105 | +** The base URI for cloning is "https://fossil-scm.org/home". The extra | |
| 3106 | +** "new-name" term means that the cloned repository will be called | |
| 3107 | +** "new-name.fossil". | |
| 3082 | 3108 | ** |
| 3083 | 3109 | ** Options: |
| 3084 | 3110 | ** --empty Initialize checkout as being empty, but still connected |
| 3085 | 3111 | ** with the local repository. If you commit this checkout, |
| 3086 | 3112 | ** it will become a new "initial" commit in the repository. |
| 3113 | +** --force Continue with the open even if the working directory is | |
| 3114 | +** not empty. | |
| 3115 | +** --force-missing Force opening a repository with missing content | |
| 3087 | 3116 | ** --keep Only modify the manifest and manifest.uuid files |
| 3088 | 3117 | ** --nested Allow opening a repository inside an opened checkout |
| 3089 | -** --force-missing Force opening a repository with missing content | |
| 3118 | +** --repodir DIR If REPOSITORY is a URI that will be cloned, store | |
| 3119 | +** the clone in DIR rather than in "." | |
| 3090 | 3120 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 3091 | 3121 | ** times (the timestamp of the last checkin which modified |
| 3092 | 3122 | ** them). |
| 3123 | +** --workdir DIR Use DIR as the working directory instead of ".". The DIR | |
| 3124 | +** directory is created if it does not exist. | |
| 3093 | 3125 | ** |
| 3094 | -** See also: close | |
| 3126 | +** See also: [[close]], [[clone]] | |
| 3095 | 3127 | */ |
| 3096 | 3128 | void cmd_open(void){ |
| 3097 | 3129 | int emptyFlag; |
| 3098 | 3130 | int keepFlag; |
| 3099 | 3131 | int forceMissingFlag; |
| 3100 | 3132 | int allowNested; |
| 3101 | 3133 | int allowSymlinks; |
| 3102 | 3134 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3135 | + int bForce = 0; /* --force. Open even if non-empty dir */ | |
| 3103 | 3136 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3137 | + const char *zWorkDir; /* --workdir value */ | |
| 3138 | + const char *zRepo = 0; /* Name of the repository file */ | |
| 3139 | + const char *zRepoDir = 0; /* --repodir value */ | |
| 3140 | + char *zPwd; /* Initial working directory */ | |
| 3141 | + int isUri = 0; /* True if REPOSITORY is a URI */ | |
| 3104 | 3142 | |
| 3105 | 3143 | url_proxy_options(); |
| 3106 | 3144 | emptyFlag = find_option("empty",0,0)!=0; |
| 3107 | 3145 | keepFlag = find_option("keep",0,0)!=0; |
| 3108 | 3146 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 3109 | 3147 | allowNested = find_option("nested",0,0)!=0; |
| 3110 | 3148 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 3149 | + zWorkDir = find_option("workdir",0,1); | |
| 3150 | + zRepoDir = find_option("repodir",0,1); | |
| 3151 | + bForce = find_option("force",0,0)!=0; | |
| 3152 | + zPwd = file_getcwd(0,0); | |
| 3153 | + | |
| 3111 | 3154 | |
| 3112 | 3155 | /* We should be done with options.. */ |
| 3113 | 3156 | verify_all_options(); |
| 3114 | 3157 | |
| 3115 | 3158 | if( g.argc!=3 && g.argc!=4 ){ |
| 3116 | 3159 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3117 | 3160 | } |
| 3118 | - if( !allowNested && db_open_local(0) ){ | |
| 3119 | - fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot); | |
| 3161 | + zRepo = g.argv[2]; | |
| 3162 | + if( sqlite3_strglob("http://*", zRepo)==0 | |
| 3163 | + || sqlite3_strglob("https://*", zRepo)==0 | |
| 3164 | + || sqlite3_strglob("ssh:*", zRepo)==0 | |
| 3165 | + || sqlite3_strglob("file:*", zRepo)==0 | |
| 3166 | + ){ | |
| 3167 | + isUri = 1; | |
| 3168 | + } | |
| 3169 | + | |
| 3170 | + /* If --workdir is specified, change to the requested working directory */ | |
| 3171 | + if( zWorkDir ){ | |
| 3172 | + if( !isUri ){ | |
| 3173 | + zRepo = file_canonical_name_dup(zRepo); | |
| 3174 | + } | |
| 3175 | + if( zRepoDir ){ | |
| 3176 | + zRepoDir = file_canonical_name_dup(zRepoDir); | |
| 3177 | + } | |
| 3178 | + if( file_isdir(zWorkDir, ExtFILE)!=1 ){ | |
| 3179 | + file_mkfolder(zWorkDir, ExtFILE, 0, 0); | |
| 3180 | + if( file_mkdir(zWorkDir, ExtFILE, 0) ){ | |
| 3181 | + fossil_fatal("cannot create directory %s", zWorkDir); | |
| 3182 | + } | |
| 3183 | + } | |
| 3184 | + if( file_chdir(zWorkDir, 0) ){ | |
| 3185 | + fossil_fatal("unable to make %s the working directory", zWorkDir); | |
| 3186 | + } | |
| 3187 | + } | |
| 3188 | + if( keepFlag==0 && bForce==0 && file_directory_size(".", 0, 1)>0 ){ | |
| 3189 | + fossil_fatal("directory %s is not empty\n" | |
| 3190 | + "use the --force option to override", file_getcwd(0,0)); | |
| 3191 | + } | |
| 3192 | + | |
| 3193 | + if( db_open_local_v2(0, allowNested) ){ | |
| 3194 | + fossil_fatal("there is already an open tree at %s", g.zLocalRoot); | |
| 3195 | + } | |
| 3196 | + | |
| 3197 | + /* If REPOSITORY looks like a URI, then try to clone it first */ | |
| 3198 | + if( isUri ){ | |
| 3199 | + char *zNewBase; /* Base name of the cloned repository file */ | |
| 3200 | + const char *zUri; /* URI to clone */ | |
| 3201 | + int i; /* Loop counter */ | |
| 3202 | + int rc; /* Result code from fossil_system() */ | |
| 3203 | + Blob cmd; /* Clone command to be run */ | |
| 3204 | + char *zCmd; /* String version of the clone command */ | |
| 3205 | + | |
| 3206 | + zUri = zRepo; | |
| 3207 | + zNewBase = fossil_strdup(file_tail(zUri)); | |
| 3208 | + for(i=(int)strlen(zNewBase)-1; i>1 && zNewBase[i]!='.'; i--){} | |
| 3209 | + if( zNewBase[i]=='.' ) zNewBase[i] = 0; | |
| 3210 | + if( zRepoDir==0 ) zRepoDir = zPwd; | |
| 3211 | + zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase); | |
| 3212 | + fossil_free(zNewBase); | |
| 3213 | + blob_init(&cmd, 0, 0); | |
| 3214 | + blob_append_escaped_arg(&cmd, g.nameOfExe); | |
| 3215 | + blob_append(&cmd, " clone", -1); | |
| 3216 | + blob_append_escaped_arg(&cmd, zUri); | |
| 3217 | + blob_append_escaped_arg(&cmd, zRepo); | |
| 3218 | + zCmd = blob_str(&cmd); | |
| 3219 | + fossil_print("%s\n", zCmd); | |
| 3220 | + if( zWorkDir ) file_chdir(zPwd, 0); | |
| 3221 | + rc = fossil_system(zCmd); | |
| 3222 | + if( rc ){ | |
| 3223 | + fossil_fatal("clone of %s failed", zUri); | |
| 3224 | + } | |
| 3225 | + blob_reset(&cmd); | |
| 3226 | + if( zWorkDir ) file_chdir(zWorkDir, 0); | |
| 3227 | + }else if( zRepoDir ){ | |
| 3228 | + fossil_fatal("the --repodir option only makes sense if the REPOSITORY " | |
| 3229 | + "argument is a URI that begins with http:, https:, ssh:, " | |
| 3230 | + "or file:"); | |
| 3120 | 3231 | } |
| 3121 | - db_open_repository(g.argv[2]); | |
| 3232 | + | |
| 3233 | + db_open_repository(zRepo); | |
| 3122 | 3234 | |
| 3123 | 3235 | /* Figure out which revision to open. */ |
| 3124 | 3236 | if( !emptyFlag ){ |
| 3125 | 3237 | if( g.argc==4 ){ |
| 3126 | 3238 | g.zOpenRevision = g.argv[3]; |
| @@ -3169,12 +3281,12 @@ | ||
| 3169 | 3281 | ** point, this will probably be the setting value from the |
| 3170 | 3282 | ** repository or global configuration databases. */ |
| 3171 | 3283 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 3172 | 3284 | db_allow_symlinks_by_default()); |
| 3173 | 3285 | } |
| 3174 | - db_lset("repository", g.argv[2]); | |
| 3175 | - db_record_repository_filename(g.argv[2]); | |
| 3286 | + db_lset("repository", zRepo); | |
| 3287 | + db_record_repository_filename(zRepo); | |
| 3176 | 3288 | db_set_checkout(0); |
| 3177 | 3289 | azNewArgv[0] = g.argv[0]; |
| 3178 | 3290 | g.argv = azNewArgv; |
| 3179 | 3291 | if( !emptyFlag ){ |
| 3180 | 3292 | g.argc = 3; |
| @@ -3887,11 +3999,11 @@ | ||
| 3887 | 3999 | ** --global set or unset the given property globally instead of |
| 3888 | 4000 | ** setting or unsetting it for the open repository only. |
| 3889 | 4001 | ** |
| 3890 | 4002 | ** --exact only consider exact name matches. |
| 3891 | 4003 | ** |
| 3892 | -** See also: configuration | |
| 4004 | +** See also: [[configuration]] | |
| 3893 | 4005 | */ |
| 3894 | 4006 | void setting_cmd(void){ |
| 3895 | 4007 | int i; |
| 3896 | 4008 | int globalFlag = find_option("global","g",0)!=0; |
| 3897 | 4009 | int exactFlag = find_option("exact",0,0)!=0; |
| 3898 | 4010 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -116,10 +116,11 @@ | |
| 116 | */ |
| 117 | static struct DbLocalData { |
| 118 | int nBegin; /* Nesting depth of BEGIN */ |
| 119 | int doRollback; /* True to force a rollback */ |
| 120 | int nCommitHook; /* Number of commit hooks */ |
| 121 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| 122 | int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ |
| 123 | int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ |
| 124 | struct sCommitHook { |
| 125 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| @@ -195,10 +196,11 @@ | |
| 195 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 196 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 197 | db.doRollback = 0; |
| 198 | db.zStartFile = zStartFile; |
| 199 | db.iStartLine = iStartLine; |
| 200 | } |
| 201 | db.nBegin++; |
| 202 | } |
| 203 | /* |
| 204 | ** Begin a new transaction for writing. |
| @@ -209,11 +211,12 @@ | |
| 209 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 210 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 211 | db.doRollback = 0; |
| 212 | db.zStartFile = zStartFile; |
| 213 | db.iStartLine = iStartLine; |
| 214 | }else{ |
| 215 | fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " |
| 216 | "for the write txn at %s:%d", |
| 217 | db.zStartFile, db.iStartLine, zStartFile, iStartLine); |
| 218 | } |
| 219 | db.nBegin++; |
| @@ -1328,11 +1331,11 @@ | |
| 1328 | ); |
| 1329 | if( rc!=SQLITE_OK ){ |
| 1330 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1331 | } |
| 1332 | db_maybe_set_encryption_key(db, zDbName); |
| 1333 | sqlite3_busy_timeout(db, 5000); |
| 1334 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1335 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1336 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1337 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1338 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| @@ -1344,11 +1347,11 @@ | |
| 1344 | ); |
| 1345 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1346 | db_add_aux_functions(db); |
| 1347 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1348 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1349 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 1350 | return db; |
| 1351 | } |
| 1352 | |
| 1353 | |
| 1354 | /* |
| @@ -1714,16 +1717,20 @@ | |
| 1714 | ** |
| 1715 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1716 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1717 | ** to the root of the repository tree and this routine returns 1. If |
| 1718 | ** no database is found, then this routine return 0. |
| 1719 | ** |
| 1720 | ** This routine always opens the user database regardless of whether or |
| 1721 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1722 | ** is found, it is attached to the open database connection too. |
| 1723 | */ |
| 1724 | int db_open_local(const char *zDbName){ |
| 1725 | int i, n; |
| 1726 | char zPwd[2000]; |
| 1727 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1728 | |
| 1729 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1754,22 @@ | |
| 1747 | g.localOpen = 1; |
| 1748 | db_open_repository(zDbName); |
| 1749 | return 1; |
| 1750 | } |
| 1751 | } |
| 1752 | n--; |
| 1753 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1754 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1755 | zPwd[n] = 0; |
| 1756 | } |
| 1757 | |
| 1758 | /* A checkout database file could not be found */ |
| 1759 | return 0; |
| 1760 | } |
| 1761 | |
| 1762 | /* |
| 1763 | ** Get the full pathname to the repository database file. The |
| 1764 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -2388,11 +2399,11 @@ | |
| 2388 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2389 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2390 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2391 | ** means UTC. |
| 2392 | ** |
| 2393 | ** See also: clone |
| 2394 | */ |
| 2395 | void create_repository_cmd(void){ |
| 2396 | char *zPassword; |
| 2397 | const char *zTemplate; /* Repository from which to copy settings */ |
| 2398 | const char *zDate; /* Date of the initial check-in */ |
| @@ -3070,57 +3081,158 @@ | |
| 3070 | } |
| 3071 | |
| 3072 | /* |
| 3073 | ** COMMAND: open |
| 3074 | ** |
| 3075 | ** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS? |
| 3076 | ** |
| 3077 | ** Open a connection to the local repository in FILENAME. A checkout |
| 3078 | ** for the repository is created with its root at the working directory. |
| 3079 | ** If VERSION is specified then that version is checked out. Otherwise |
| 3080 | ** the latest version is checked out. No files other than "manifest" |
| 3081 | ** and "manifest.uuid" are modified if the --keep option is present. |
| 3082 | ** |
| 3083 | ** Options: |
| 3084 | ** --empty Initialize checkout as being empty, but still connected |
| 3085 | ** with the local repository. If you commit this checkout, |
| 3086 | ** it will become a new "initial" commit in the repository. |
| 3087 | ** --keep Only modify the manifest and manifest.uuid files |
| 3088 | ** --nested Allow opening a repository inside an opened checkout |
| 3089 | ** --force-missing Force opening a repository with missing content |
| 3090 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 3091 | ** times (the timestamp of the last checkin which modified |
| 3092 | ** them). |
| 3093 | ** |
| 3094 | ** See also: close |
| 3095 | */ |
| 3096 | void cmd_open(void){ |
| 3097 | int emptyFlag; |
| 3098 | int keepFlag; |
| 3099 | int forceMissingFlag; |
| 3100 | int allowNested; |
| 3101 | int allowSymlinks; |
| 3102 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3103 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3104 | |
| 3105 | url_proxy_options(); |
| 3106 | emptyFlag = find_option("empty",0,0)!=0; |
| 3107 | keepFlag = find_option("keep",0,0)!=0; |
| 3108 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 3109 | allowNested = find_option("nested",0,0)!=0; |
| 3110 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 3111 | |
| 3112 | /* We should be done with options.. */ |
| 3113 | verify_all_options(); |
| 3114 | |
| 3115 | if( g.argc!=3 && g.argc!=4 ){ |
| 3116 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3117 | } |
| 3118 | if( !allowNested && db_open_local(0) ){ |
| 3119 | fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot); |
| 3120 | } |
| 3121 | db_open_repository(g.argv[2]); |
| 3122 | |
| 3123 | /* Figure out which revision to open. */ |
| 3124 | if( !emptyFlag ){ |
| 3125 | if( g.argc==4 ){ |
| 3126 | g.zOpenRevision = g.argv[3]; |
| @@ -3169,12 +3281,12 @@ | |
| 3169 | ** point, this will probably be the setting value from the |
| 3170 | ** repository or global configuration databases. */ |
| 3171 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 3172 | db_allow_symlinks_by_default()); |
| 3173 | } |
| 3174 | db_lset("repository", g.argv[2]); |
| 3175 | db_record_repository_filename(g.argv[2]); |
| 3176 | db_set_checkout(0); |
| 3177 | azNewArgv[0] = g.argv[0]; |
| 3178 | g.argv = azNewArgv; |
| 3179 | if( !emptyFlag ){ |
| 3180 | g.argc = 3; |
| @@ -3887,11 +3999,11 @@ | |
| 3887 | ** --global set or unset the given property globally instead of |
| 3888 | ** setting or unsetting it for the open repository only. |
| 3889 | ** |
| 3890 | ** --exact only consider exact name matches. |
| 3891 | ** |
| 3892 | ** See also: configuration |
| 3893 | */ |
| 3894 | void setting_cmd(void){ |
| 3895 | int i; |
| 3896 | int globalFlag = find_option("global","g",0)!=0; |
| 3897 | int exactFlag = find_option("exact",0,0)!=0; |
| 3898 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -116,10 +116,11 @@ | |
| 116 | */ |
| 117 | static struct DbLocalData { |
| 118 | int nBegin; /* Nesting depth of BEGIN */ |
| 119 | int doRollback; /* True to force a rollback */ |
| 120 | int nCommitHook; /* Number of commit hooks */ |
| 121 | int wrTxn; /* Outer-most TNX is a write */ |
| 122 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| 123 | int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ |
| 124 | int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ |
| 125 | struct sCommitHook { |
| 126 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| @@ -195,10 +196,11 @@ | |
| 196 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 197 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 198 | db.doRollback = 0; |
| 199 | db.zStartFile = zStartFile; |
| 200 | db.iStartLine = iStartLine; |
| 201 | db.wrTxn = 0; |
| 202 | } |
| 203 | db.nBegin++; |
| 204 | } |
| 205 | /* |
| 206 | ** Begin a new transaction for writing. |
| @@ -209,11 +211,12 @@ | |
| 211 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 212 | db.nPriorChanges = sqlite3_total_changes(g.db); |
| 213 | db.doRollback = 0; |
| 214 | db.zStartFile = zStartFile; |
| 215 | db.iStartLine = iStartLine; |
| 216 | db.wrTxn = 1; |
| 217 | }else if( !db.wrTxn ){ |
| 218 | fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " |
| 219 | "for the write txn at %s:%d", |
| 220 | db.zStartFile, db.iStartLine, zStartFile, iStartLine); |
| 221 | } |
| 222 | db.nBegin++; |
| @@ -1328,11 +1331,11 @@ | |
| 1331 | ); |
| 1332 | if( rc!=SQLITE_OK ){ |
| 1333 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 1334 | } |
| 1335 | db_maybe_set_encryption_key(db, zDbName); |
| 1336 | sqlite3_busy_timeout(db, 15000); |
| 1337 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 1338 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 1339 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1340 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 1341 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| @@ -1344,11 +1347,11 @@ | |
| 1347 | ); |
| 1348 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1349 | db_add_aux_functions(db); |
| 1350 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1351 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1352 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1353 | return db; |
| 1354 | } |
| 1355 | |
| 1356 | |
| 1357 | /* |
| @@ -1714,16 +1717,20 @@ | |
| 1717 | ** |
| 1718 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1719 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1720 | ** to the root of the repository tree and this routine returns 1. If |
| 1721 | ** no database is found, then this routine return 0. |
| 1722 | ** |
| 1723 | ** In db_open_local_v2(), if the bRootOnly flag is true, then only |
| 1724 | ** look in the CWD for the checkout database. Do not scan upwards in |
| 1725 | ** the file hierarchy. |
| 1726 | ** |
| 1727 | ** This routine always opens the user database regardless of whether or |
| 1728 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1729 | ** is found, it is attached to the open database connection too. |
| 1730 | */ |
| 1731 | int db_open_local_v2(const char *zDbName, int bRootOnly){ |
| 1732 | int i, n; |
| 1733 | char zPwd[2000]; |
| 1734 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1735 | |
| 1736 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1754,22 @@ | |
| 1754 | g.localOpen = 1; |
| 1755 | db_open_repository(zDbName); |
| 1756 | return 1; |
| 1757 | } |
| 1758 | } |
| 1759 | if( bRootOnly ) break; |
| 1760 | n--; |
| 1761 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1762 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1763 | zPwd[n] = 0; |
| 1764 | } |
| 1765 | |
| 1766 | /* A checkout database file could not be found */ |
| 1767 | return 0; |
| 1768 | } |
| 1769 | int db_open_local(const char *zDbName){ |
| 1770 | return db_open_local_v2(zDbName, 0); |
| 1771 | } |
| 1772 | |
| 1773 | /* |
| 1774 | ** Get the full pathname to the repository database file. The |
| 1775 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -2388,11 +2399,11 @@ | |
| 2399 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2400 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2401 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2402 | ** means UTC. |
| 2403 | ** |
| 2404 | ** See also: [[clone]] |
| 2405 | */ |
| 2406 | void create_repository_cmd(void){ |
| 2407 | char *zPassword; |
| 2408 | const char *zTemplate; /* Repository from which to copy settings */ |
| 2409 | const char *zDate; /* Date of the initial check-in */ |
| @@ -3070,57 +3081,158 @@ | |
| 3081 | } |
| 3082 | |
| 3083 | /* |
| 3084 | ** COMMAND: open |
| 3085 | ** |
| 3086 | ** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS? |
| 3087 | ** |
| 3088 | ** Open a new connection to the repository name REPOSITORY. A checkout |
| 3089 | ** for the repository is created with its root at the current working |
| 3090 | ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is |
| 3091 | ** specified then that version is checked out. Otherwise the most recent |
| 3092 | ** check-in on the main branch (usually "trunk") is used. |
| 3093 | ** |
| 3094 | ** REPOSITORY can be the filename for a repository that already exists on the |
| 3095 | ** local machine or it can be a URI for a remote repository. If REPOSITORY |
| 3096 | ** is a URI in one of the formats recognized by the [[clone]] command, then |
| 3097 | ** remote repo is first cloned, then the clone is opened. The clone will be |
| 3098 | ** stored in the current directory, or in DIR if the "--repodir DIR" option |
| 3099 | ** is used. The name of the clone will be taken from the last term of the URI. |
| 3100 | ** For "http:" and "https:" URIs, you can append an extra term to the end of |
| 3101 | ** the URI to get any repository name you like. For example: |
| 3102 | ** |
| 3103 | ** fossil open https://fossil-scm.org/home/new-name |
| 3104 | ** |
| 3105 | ** The base URI for cloning is "https://fossil-scm.org/home". The extra |
| 3106 | ** "new-name" term means that the cloned repository will be called |
| 3107 | ** "new-name.fossil". |
| 3108 | ** |
| 3109 | ** Options: |
| 3110 | ** --empty Initialize checkout as being empty, but still connected |
| 3111 | ** with the local repository. If you commit this checkout, |
| 3112 | ** it will become a new "initial" commit in the repository. |
| 3113 | ** --force Continue with the open even if the working directory is |
| 3114 | ** not empty. |
| 3115 | ** --force-missing Force opening a repository with missing content |
| 3116 | ** --keep Only modify the manifest and manifest.uuid files |
| 3117 | ** --nested Allow opening a repository inside an opened checkout |
| 3118 | ** --repodir DIR If REPOSITORY is a URI that will be cloned, store |
| 3119 | ** the clone in DIR rather than in "." |
| 3120 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 3121 | ** times (the timestamp of the last checkin which modified |
| 3122 | ** them). |
| 3123 | ** --workdir DIR Use DIR as the working directory instead of ".". The DIR |
| 3124 | ** directory is created if it does not exist. |
| 3125 | ** |
| 3126 | ** See also: [[close]], [[clone]] |
| 3127 | */ |
| 3128 | void cmd_open(void){ |
| 3129 | int emptyFlag; |
| 3130 | int keepFlag; |
| 3131 | int forceMissingFlag; |
| 3132 | int allowNested; |
| 3133 | int allowSymlinks; |
| 3134 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3135 | int bForce = 0; /* --force. Open even if non-empty dir */ |
| 3136 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3137 | const char *zWorkDir; /* --workdir value */ |
| 3138 | const char *zRepo = 0; /* Name of the repository file */ |
| 3139 | const char *zRepoDir = 0; /* --repodir value */ |
| 3140 | char *zPwd; /* Initial working directory */ |
| 3141 | int isUri = 0; /* True if REPOSITORY is a URI */ |
| 3142 | |
| 3143 | url_proxy_options(); |
| 3144 | emptyFlag = find_option("empty",0,0)!=0; |
| 3145 | keepFlag = find_option("keep",0,0)!=0; |
| 3146 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 3147 | allowNested = find_option("nested",0,0)!=0; |
| 3148 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 3149 | zWorkDir = find_option("workdir",0,1); |
| 3150 | zRepoDir = find_option("repodir",0,1); |
| 3151 | bForce = find_option("force",0,0)!=0; |
| 3152 | zPwd = file_getcwd(0,0); |
| 3153 | |
| 3154 | |
| 3155 | /* We should be done with options.. */ |
| 3156 | verify_all_options(); |
| 3157 | |
| 3158 | if( g.argc!=3 && g.argc!=4 ){ |
| 3159 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3160 | } |
| 3161 | zRepo = g.argv[2]; |
| 3162 | if( sqlite3_strglob("http://*", zRepo)==0 |
| 3163 | || sqlite3_strglob("https://*", zRepo)==0 |
| 3164 | || sqlite3_strglob("ssh:*", zRepo)==0 |
| 3165 | || sqlite3_strglob("file:*", zRepo)==0 |
| 3166 | ){ |
| 3167 | isUri = 1; |
| 3168 | } |
| 3169 | |
| 3170 | /* If --workdir is specified, change to the requested working directory */ |
| 3171 | if( zWorkDir ){ |
| 3172 | if( !isUri ){ |
| 3173 | zRepo = file_canonical_name_dup(zRepo); |
| 3174 | } |
| 3175 | if( zRepoDir ){ |
| 3176 | zRepoDir = file_canonical_name_dup(zRepoDir); |
| 3177 | } |
| 3178 | if( file_isdir(zWorkDir, ExtFILE)!=1 ){ |
| 3179 | file_mkfolder(zWorkDir, ExtFILE, 0, 0); |
| 3180 | if( file_mkdir(zWorkDir, ExtFILE, 0) ){ |
| 3181 | fossil_fatal("cannot create directory %s", zWorkDir); |
| 3182 | } |
| 3183 | } |
| 3184 | if( file_chdir(zWorkDir, 0) ){ |
| 3185 | fossil_fatal("unable to make %s the working directory", zWorkDir); |
| 3186 | } |
| 3187 | } |
| 3188 | if( keepFlag==0 && bForce==0 && file_directory_size(".", 0, 1)>0 ){ |
| 3189 | fossil_fatal("directory %s is not empty\n" |
| 3190 | "use the --force option to override", file_getcwd(0,0)); |
| 3191 | } |
| 3192 | |
| 3193 | if( db_open_local_v2(0, allowNested) ){ |
| 3194 | fossil_fatal("there is already an open tree at %s", g.zLocalRoot); |
| 3195 | } |
| 3196 | |
| 3197 | /* If REPOSITORY looks like a URI, then try to clone it first */ |
| 3198 | if( isUri ){ |
| 3199 | char *zNewBase; /* Base name of the cloned repository file */ |
| 3200 | const char *zUri; /* URI to clone */ |
| 3201 | int i; /* Loop counter */ |
| 3202 | int rc; /* Result code from fossil_system() */ |
| 3203 | Blob cmd; /* Clone command to be run */ |
| 3204 | char *zCmd; /* String version of the clone command */ |
| 3205 | |
| 3206 | zUri = zRepo; |
| 3207 | zNewBase = fossil_strdup(file_tail(zUri)); |
| 3208 | for(i=(int)strlen(zNewBase)-1; i>1 && zNewBase[i]!='.'; i--){} |
| 3209 | if( zNewBase[i]=='.' ) zNewBase[i] = 0; |
| 3210 | if( zRepoDir==0 ) zRepoDir = zPwd; |
| 3211 | zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase); |
| 3212 | fossil_free(zNewBase); |
| 3213 | blob_init(&cmd, 0, 0); |
| 3214 | blob_append_escaped_arg(&cmd, g.nameOfExe); |
| 3215 | blob_append(&cmd, " clone", -1); |
| 3216 | blob_append_escaped_arg(&cmd, zUri); |
| 3217 | blob_append_escaped_arg(&cmd, zRepo); |
| 3218 | zCmd = blob_str(&cmd); |
| 3219 | fossil_print("%s\n", zCmd); |
| 3220 | if( zWorkDir ) file_chdir(zPwd, 0); |
| 3221 | rc = fossil_system(zCmd); |
| 3222 | if( rc ){ |
| 3223 | fossil_fatal("clone of %s failed", zUri); |
| 3224 | } |
| 3225 | blob_reset(&cmd); |
| 3226 | if( zWorkDir ) file_chdir(zWorkDir, 0); |
| 3227 | }else if( zRepoDir ){ |
| 3228 | fossil_fatal("the --repodir option only makes sense if the REPOSITORY " |
| 3229 | "argument is a URI that begins with http:, https:, ssh:, " |
| 3230 | "or file:"); |
| 3231 | } |
| 3232 | |
| 3233 | db_open_repository(zRepo); |
| 3234 | |
| 3235 | /* Figure out which revision to open. */ |
| 3236 | if( !emptyFlag ){ |
| 3237 | if( g.argc==4 ){ |
| 3238 | g.zOpenRevision = g.argv[3]; |
| @@ -3169,12 +3281,12 @@ | |
| 3281 | ** point, this will probably be the setting value from the |
| 3282 | ** repository or global configuration databases. */ |
| 3283 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 3284 | db_allow_symlinks_by_default()); |
| 3285 | } |
| 3286 | db_lset("repository", zRepo); |
| 3287 | db_record_repository_filename(zRepo); |
| 3288 | db_set_checkout(0); |
| 3289 | azNewArgv[0] = g.argv[0]; |
| 3290 | g.argv = azNewArgv; |
| 3291 | if( !emptyFlag ){ |
| 3292 | g.argc = 3; |
| @@ -3887,11 +3999,11 @@ | |
| 3999 | ** --global set or unset the given property globally instead of |
| 4000 | ** setting or unsetting it for the open repository only. |
| 4001 | ** |
| 4002 | ** --exact only consider exact name matches. |
| 4003 | ** |
| 4004 | ** See also: [[configuration]] |
| 4005 | */ |
| 4006 | void setting_cmd(void){ |
| 4007 | int i; |
| 4008 | int globalFlag = find_option("global","g",0)!=0; |
| 4009 | int exactFlag = find_option("exact",0,0)!=0; |
| 4010 |
+375
-21
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -440,16 +440,10 @@ | ||
| 440 | 440 | content:"'"; |
| 441 | 441 | } |
| 442 | 442 | span.usertype:after { |
| 443 | 443 | content:"'"; |
| 444 | 444 | } |
| 445 | -div.selectedText { | |
| 446 | - font-weight: bold; | |
| 447 | - color: blue; | |
| 448 | - background-color: #d5d5ff; | |
| 449 | - border: 1px blue solid; | |
| 450 | -} | |
| 451 | 445 | p.missingPriv { |
| 452 | 446 | color: blue; |
| 453 | 447 | } |
| 454 | 448 | span.wikiruleHead { |
| 455 | 449 | font-weight: bold; |
| @@ -774,44 +768,93 @@ | ||
| 774 | 768 | div.forumTime > div > form, |
| 775 | 769 | div.forumHierRoot > div > form { |
| 776 | 770 | margin: 0.5em 0; |
| 777 | 771 | } |
| 778 | 772 | .forum-post-collapser { |
| 773 | + /* Common style for the bottom-of-post and right-of-post | |
| 774 | + expand/collapse widgets. */ | |
| 779 | 775 | font-size: 0.8em; |
| 780 | - margin-top: 0.2em; | |
| 781 | 776 | padding: 0; |
| 777 | + border: 1px solid rgba(0, 0, 0, 0.2); | |
| 778 | + border-radius: 0 0 0.5em 0.5em; | |
| 779 | + background-color: rgba(0, 0, 0, 0.05); | |
| 780 | + opacity: 0.8; | |
| 781 | + cursor: pointer; | |
| 782 | +} | |
| 783 | +.forum-post-collapser.bottom { | |
| 784 | + margin: 0 0 0.4em 0; | |
| 782 | 785 | height: 1.75em; |
| 783 | 786 | line-height: 1.75em; |
| 784 | 787 | /* ^^^ Those sizes are finely tuned for the current selection of |
| 785 | 788 | arrow characters. If those change, these should, too. Remember that |
| 786 | 789 | FF/Chrome simply do not agree on alignment with most values :/. */ |
| 787 | - border-width: 1px; | |
| 788 | - border-style: solid; | |
| 789 | - border-radius: 0.25em; | |
| 790 | - opacity: 0.8; | |
| 791 | - cursor: pointer; | |
| 792 | 790 | display: flex; |
| 793 | 791 | flex-direction: row; |
| 794 | 792 | justify-content: space-between; |
| 795 | 793 | } |
| 796 | -.forum-post-collapser > span { | |
| 794 | +.forum-post-collapser.bottom > span { | |
| 797 | 795 | margin: 0 1em 0 1em; |
| 798 | 796 | vertical-align: middle; |
| 799 | 797 | } |
| 800 | -.forum-post-collapser.expanded > span::before { | |
| 801 | - content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; | |
| 802 | -} | |
| 803 | -.forum-post-collapser:not(.expanded) > span::before { | |
| 798 | +.forum-post-collapser.bottom > span::before { | |
| 804 | 799 | content: "⇣⇣⇣"; |
| 805 | 800 | } |
| 801 | +.forum-post-collapser.bottom.expanded > span::before { | |
| 802 | + content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; | |
| 803 | +} | |
| 806 | 804 | div.forumPostBody{ |
| 807 | 805 | max-height: 50em; |
| 808 | 806 | overflow: auto; |
| 807 | +} | |
| 808 | +div.forumPostBody.with-expander { | |
| 809 | + display: flex; | |
| 810 | + flex-direction: row; | |
| 811 | + overflow: auto; | |
| 812 | +} | |
| 813 | +div.forumPostBody.with-expander:not(.expanded) > :first-child { | |
| 814 | + overflow-y: hidden; | |
| 815 | +} | |
| 816 | +div.forumPostBody.with-expander > *:first-child { | |
| 817 | + /* Main content DIV/PRE */ | |
| 818 | + overflow: auto; | |
| 819 | + flex: 10 1 auto; | |
| 820 | +} | |
| 821 | +div.forumPostBody.with-expander.expanded > *:first-child { | |
| 822 | + margin-bottom: 0.5em /* try to suppress scroll bar */; | |
| 823 | +} | |
| 824 | +div.forumPostBody.with-expander .forum-post-collapser.right { | |
| 825 | + /* "Tap zone" for expansion of the post, sits to the right of the | |
| 826 | + post's content. */ | |
| 827 | + flex: 1 10 auto; | |
| 828 | + min-width: 1.25em; | |
| 829 | + max-width: 1.25em; | |
| 830 | + margin: 0 0 0 0.2em; | |
| 831 | + overflow: hidden; | |
| 832 | + display: flex; | |
| 833 | + flex-direction: column; | |
| 834 | + justify-content: space-around; | |
| 835 | + align-items: center; | |
| 836 | + border-radius: 0.1em; | |
| 837 | + cursor: pointer; | |
| 838 | + border-bottom: 0; | |
| 839 | + border-radius: 0 0.5em 0 0; | |
| 840 | +} | |
| 841 | +div.forumPostBody.with-expander .forum-post-collapser.right > span:before { | |
| 842 | + content: "⇣"; | |
| 843 | +} | |
| 844 | +div.forumPostBody.with-expander.expanded .forum-post-collapser.right > span:before { | |
| 845 | + content: "⇡"; | |
| 809 | 846 | } |
| 810 | 847 | div.forumPostBody.expanded { |
| 811 | 848 | max-height: initial; |
| 812 | 849 | } |
| 850 | +div.forumPostBody.shrunken { | |
| 851 | + /* When an expandable post is un-expanded, it is shrunkend down | |
| 852 | + to this size instead of its original size. */ | |
| 853 | + max-height: 8em; | |
| 854 | +} | |
| 855 | + | |
| 813 | 856 | div.forumSel { |
| 814 | 857 | background-color: #cef; |
| 815 | 858 | } |
| 816 | 859 | div.forumObs { |
| 817 | 860 | color: #bbb; |
| @@ -904,14 +947,19 @@ | ||
| 904 | 947 | color: darkred; |
| 905 | 948 | background: yellow; |
| 906 | 949 | opacity: 0.7; |
| 907 | 950 | } |
| 908 | 951 | .hidden { |
| 909 | - position: absolute; | |
| 910 | - opacity: 0; | |
| 911 | - pointer-events: none; | |
| 912 | - display: none; | |
| 952 | + /* The framework-wide way of hiding elements is to assign them this | |
| 953 | + CSS class. To make them visible again, remove it. The !important | |
| 954 | + qualifiers are unfortunate but sometimes necessary when hidden | |
| 955 | + element has other classes which specify visibility-related | |
| 956 | + options. */ | |
| 957 | + position: absolute !important; | |
| 958 | + opacity: 0 !important; | |
| 959 | + pointer-events: none !important; | |
| 960 | + display: none !important; | |
| 913 | 961 | } |
| 914 | 962 | input { |
| 915 | 963 | max-width: 95%; |
| 916 | 964 | } |
| 917 | 965 | textarea { |
| @@ -919,5 +967,311 @@ | ||
| 919 | 967 | } |
| 920 | 968 | img { |
| 921 | 969 | max-width: 100%; |
| 922 | 970 | height: auto; |
| 923 | 971 | } |
| 972 | +hr { | |
| 973 | + /* Needed to keep /dir README.txt from floating right in some skins */ | |
| 974 | + clear: both; | |
| 975 | +} | |
| 976 | + | |
| 977 | +/** | |
| 978 | + .tab-xxx: styles for fossil.tabs.js. | |
| 979 | +*/ | |
| 980 | +.tab-container { | |
| 981 | + width: 100%; | |
| 982 | + display: flex; | |
| 983 | + flex-direction: column; | |
| 984 | + align-items: stretch; | |
| 985 | +} | |
| 986 | +.tab-container > #fossil-status-bar { | |
| 987 | + margin-top: 0; | |
| 988 | +} | |
| 989 | +.tab-container > .tabs { | |
| 990 | + padding: 0.25em; | |
| 991 | + margin: 0; | |
| 992 | + display: flex; | |
| 993 | + flex-direction: column; | |
| 994 | + border-width: 1px; | |
| 995 | + border-style: outset; | |
| 996 | + border-color: inherit; | |
| 997 | +} | |
| 998 | +.tab-container > .tabs > .tab-panel { | |
| 999 | + align-self: stretch; | |
| 1000 | + flex: 10 1 auto; | |
| 1001 | + display: block; | |
| 1002 | + border: 0; | |
| 1003 | + padding: 0; | |
| 1004 | + margin: 0; | |
| 1005 | +} | |
| 1006 | +.tab-container > .tab-bar { | |
| 1007 | + display: flex; | |
| 1008 | + flex-direction: row; | |
| 1009 | + flex: 1 10 auto; | |
| 1010 | + align-self: stretch; | |
| 1011 | + flex-wrap: wrap; | |
| 1012 | +} | |
| 1013 | +.tab-container > .tab-bar > .tab-button { | |
| 1014 | + display: inline-block; | |
| 1015 | + border-radius: 0.25em 0.25em 0 0; | |
| 1016 | + margin: 0 0.1em; | |
| 1017 | + padding: 0.25em 0.75em; | |
| 1018 | + align-self: baseline; | |
| 1019 | + border-color: inherit; | |
| 1020 | + border-width: 1px; | |
| 1021 | + border-bottom: none; | |
| 1022 | + border-top-style: inset; | |
| 1023 | + border-left-style: inset; | |
| 1024 | + border-right-style: inset; | |
| 1025 | + cursor: pointer; | |
| 1026 | + opacity: 0.6; | |
| 1027 | +} | |
| 1028 | +.tab-container > .tab-bar > .tab-button.selected { | |
| 1029 | + text-decoration: underline; | |
| 1030 | + opacity: 1.0; | |
| 1031 | + border-top-style: outset; | |
| 1032 | + border-left-style: outset; | |
| 1033 | + border-right-style: outset; | |
| 1034 | +} | |
| 1035 | + | |
| 1036 | +/** | |
| 1037 | + The flex-xxx classes can be used to create basic flexbox layouts | |
| 1038 | + through the application of classes to the containing/contained | |
| 1039 | + objects. | |
| 1040 | +*/ | |
| 1041 | +.flex-container { | |
| 1042 | + display: flex; | |
| 1043 | +} | |
| 1044 | +.flex-container.flex-row { | |
| 1045 | + flex-direction: row; | |
| 1046 | + flex-wrap: wrap; | |
| 1047 | + justify-content: center; | |
| 1048 | + align-items: center; | |
| 1049 | +} | |
| 1050 | +.flex-container .flex-grow { | |
| 1051 | + flex-grow: 10; | |
| 1052 | + flex-shrink: 0; | |
| 1053 | +} | |
| 1054 | +.flex-container .flex-shrink { | |
| 1055 | + flex-grow: 0; | |
| 1056 | + flex-shrink: 10; | |
| 1057 | +} | |
| 1058 | +.flex-container.flex-row.stretch { | |
| 1059 | + flex-wrap: wrap; | |
| 1060 | + align-items: baseline; | |
| 1061 | + justify-content: stretch; | |
| 1062 | + margin: 0; | |
| 1063 | +} | |
| 1064 | +.flex-container.flex-column { | |
| 1065 | + flex-direction: column; | |
| 1066 | + flex-wrap: wrap; | |
| 1067 | + justify-content: center; | |
| 1068 | + align-items: center; | |
| 1069 | +} | |
| 1070 | +.flex-container.flex-column.stretch { | |
| 1071 | + align-items: stretch; | |
| 1072 | + margin: 0; | |
| 1073 | +} | |
| 1074 | +.flex-container.child-gap-small > * { | |
| 1075 | + margin: 0.25em; | |
| 1076 | +} | |
| 1077 | +#fossil-status-bar { | |
| 1078 | + display: block; | |
| 1079 | + font-family: monospace; | |
| 1080 | + border-width: 1px; | |
| 1081 | + border-style: inset; | |
| 1082 | + border-color: inherit; | |
| 1083 | + min-height: 1.5em; | |
| 1084 | + font-size: 1.2em; | |
| 1085 | + padding: 0.2em; | |
| 1086 | + margin: 0.25em 0; | |
| 1087 | + flex: 0 0 auto; | |
| 1088 | +} | |
| 1089 | +.font-size-100 { | |
| 1090 | + font-size: 100%; | |
| 1091 | +} | |
| 1092 | +.font-size-125 { | |
| 1093 | + font-size: 125%; | |
| 1094 | +} | |
| 1095 | +.font-size-150 { | |
| 1096 | + font-size: 150%; | |
| 1097 | +} | |
| 1098 | +.font-size-175 { | |
| 1099 | + font-size: 175%; | |
| 1100 | +} | |
| 1101 | +.font-size-200 { | |
| 1102 | + font-size: 200%; | |
| 1103 | +} | |
| 1104 | + | |
| 1105 | +/** | |
| 1106 | + .input-with-label is intended to be a wrapper element which | |
| 1107 | + contain both a LABEL tag and an INPUT or SELECT control. | |
| 1108 | + The wrapper is "necessary", as opposed to placing the INPUT | |
| 1109 | + in the LABEL, so that we can include multiple INPUT | |
| 1110 | + elements (e.g. a set of radio buttons). | |
| 1111 | +*/ | |
| 1112 | +.input-with-label { | |
| 1113 | + border: 1px inset #808080; | |
| 1114 | + border-radius: 0.25em; | |
| 1115 | + padding: 0.25em 0.4em; | |
| 1116 | + margin: 0 0.5em; | |
| 1117 | + display: inline-block; | |
| 1118 | + cursor: default; | |
| 1119 | +} | |
| 1120 | +.input-with-label > * { | |
| 1121 | + vertical-align: middle; | |
| 1122 | +} | |
| 1123 | +.input-with-label > label { | |
| 1124 | + display: inline; /* some skins set label display to block! */ | |
| 1125 | +} | |
| 1126 | +.input-with-label > input { | |
| 1127 | + margin: 0; | |
| 1128 | +} | |
| 1129 | +.input-with-label > button { | |
| 1130 | + margin: 0; | |
| 1131 | +} | |
| 1132 | +.input-with-label > select { | |
| 1133 | + margin: 0; | |
| 1134 | +} | |
| 1135 | +.input-with-label > input[type=text] { | |
| 1136 | + margin: 0; | |
| 1137 | +} | |
| 1138 | +.input-with-label > textarea { | |
| 1139 | + margin: 0; | |
| 1140 | +} | |
| 1141 | +.input-with-label > input[type=checkbox] { | |
| 1142 | + vertical-align: sub; | |
| 1143 | +} | |
| 1144 | +.input-with-label > input[type=radio] { | |
| 1145 | + vertical-align: sub; | |
| 1146 | +} | |
| 1147 | +.input-with-label > label { | |
| 1148 | + font-weight: initial; | |
| 1149 | + margin: 0 0.25em 0 0.25em; | |
| 1150 | + vertical-align: middle; | |
| 1151 | +} | |
| 1152 | + | |
| 1153 | +table.numbered-lines { | |
| 1154 | + width: 100%; | |
| 1155 | + table-layout: fixed /* required to keep ultra-wide code from exceeding | |
| 1156 | + window width, and instead force a scrollbar | |
| 1157 | + on them. */; | |
| 1158 | +} | |
| 1159 | +table.numbered-lines > tbody > tr { | |
| 1160 | + font-family: monospace; | |
| 1161 | + font-size: 1.2em; | |
| 1162 | + line-height: 1.35; | |
| 1163 | + white-space: pre; | |
| 1164 | +} | |
| 1165 | +table.numbered-lines > tbody > tr > td { | |
| 1166 | + font-family: inherit; | |
| 1167 | + font-size: inherit; | |
| 1168 | + line-height: inherit; | |
| 1169 | + white-space: inherit; | |
| 1170 | + margin: 0; | |
| 1171 | + vertical-align: top; | |
| 1172 | + padding: 0.25em 0 0 0 /*prevents slight overlap at top */; | |
| 1173 | +} | |
| 1174 | +table.numbered-lines td.line-numbers { | |
| 1175 | + width: 4.5em; | |
| 1176 | +} | |
| 1177 | +table.numbered-lines td.line-numbers > span:first-of-type { | |
| 1178 | + margin-top: 0.25em/*must match top PADDING of | |
| 1179 | + td.file-content > pre > code*/; | |
| 1180 | +} | |
| 1181 | +table.numbered-lines td.line-numbers > span { | |
| 1182 | + display: block; | |
| 1183 | + margin: 0; | |
| 1184 | + padding: 0; | |
| 1185 | + line-height: inherit; | |
| 1186 | + font-size: inherit; | |
| 1187 | + font-family: inherit; | |
| 1188 | + cursor: pointer; | |
| 1189 | + white-space: pre; | |
| 1190 | + margin-right: 2px/*keep selection from nudging the right column */; | |
| 1191 | + text-align: right; | |
| 1192 | +} | |
| 1193 | +table.numbered-lines td.line-numbers > span:hover { | |
| 1194 | + background-color: rgba(112, 112, 112, 0.25); | |
| 1195 | +} | |
| 1196 | +table.numbered-lines td.file-content { | |
| 1197 | + padding-left: 0.25em; | |
| 1198 | +} | |
| 1199 | +table.numbered-lines td.file-content > pre, | |
| 1200 | +table.numbered-lines td.file-content > pre > code { | |
| 1201 | + margin: 0; | |
| 1202 | + padding: 0; | |
| 1203 | + line-height: inherit; | |
| 1204 | + font-size: inherit; | |
| 1205 | + font-family: inherit; | |
| 1206 | + white-space: pre; | |
| 1207 | + display: block/*necessary for certain skins!*/; | |
| 1208 | +} | |
| 1209 | +table.numbered-lines td.file-content > pre { | |
| 1210 | +} | |
| 1211 | +table.numbered-lines td.file-content > pre > code { | |
| 1212 | + overflow: auto; | |
| 1213 | + padding-left: 0.5em; | |
| 1214 | + padding-right: 0.5em; | |
| 1215 | + padding-top: 0.25em/*any top padding here must match the top MARGIN of | |
| 1216 | + td.line-numbers's first span child or the | |
| 1217 | + lines/code will get misaligned. */; | |
| 1218 | + padding-bottom: 0.25em/*prevents a slight overlap at bottom from | |
| 1219 | + triggering a scroller*/; | |
| 1220 | +} | |
| 1221 | +table.numbered-lines td.file-content > pre > code > * { | |
| 1222 | + /* Defense against syntax highlighters indirectly messing up these | |
| 1223 | + properties... */ | |
| 1224 | + line-height: inherit; | |
| 1225 | + font-size: inherit; | |
| 1226 | + font-family: inherit; | |
| 1227 | +} | |
| 1228 | +table.numbered-lines td.line-numbers span.selected-line/*replacement*/ { | |
| 1229 | + font-weight: bold; | |
| 1230 | + color: blue; | |
| 1231 | + background-color: #d5d5ff; | |
| 1232 | + border: 1px blue solid; | |
| 1233 | + border-top-width: 0; | |
| 1234 | + border-bottom-width: 0; | |
| 1235 | + padding: 0; | |
| 1236 | + margin: 0; | |
| 1237 | +} | |
| 1238 | +table.numbered-lines td.line-numbers span.selected-line.start { | |
| 1239 | + border-top-width: 1px; | |
| 1240 | + margin-top: -1px/*restore alignment*/; | |
| 1241 | +} | |
| 1242 | +table.numbered-lines td.line-numbers span.selected-line.end { | |
| 1243 | + border-bottom-width: 1px; | |
| 1244 | + margin-top: -1px/*restore alignment*/; | |
| 1245 | +} | |
| 1246 | +table.numbered-lines td.line-numbers span.selected-line.start.end { | |
| 1247 | + margin-top: -2px/*restore alignment*/; | |
| 1248 | +} | |
| 1249 | + | |
| 1250 | +.fossil-tooltip { | |
| 1251 | + text-align: center; | |
| 1252 | + padding: 0.2em 1em; | |
| 1253 | + border: 1px solid black; | |
| 1254 | + border-radius: 0.25em; | |
| 1255 | + position: absolute; | |
| 1256 | + display: inline-block; | |
| 1257 | + z-index: 100; | |
| 1258 | + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75); | |
| 1259 | + background-color: inherit; | |
| 1260 | + font-size: 80%; | |
| 1261 | +} | |
| 1262 | + | |
| 1263 | +.fossil-toast {/* "toast"-style popup message */ | |
| 1264 | + padding: 0.25em 0.5em; | |
| 1265 | + margin: 0; | |
| 1266 | + border-radius: 0.25em; | |
| 1267 | + font-size: 1em; | |
| 1268 | + opacity: 0.8; | |
| 1269 | + border-size: 1px; | |
| 1270 | + border-style: dotted; | |
| 1271 | + border-color: rgb( 127, 127, 127, 0.5 ); | |
| 1272 | +} | |
| 1273 | + | |
| 1274 | +blockquote.file-content { | |
| 1275 | + /* file content block in the /file page */ | |
| 1276 | + margin: 0 1em; | |
| 1277 | +} | |
| 924 | 1278 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -440,16 +440,10 @@ | |
| 440 | content:"'"; |
| 441 | } |
| 442 | span.usertype:after { |
| 443 | content:"'"; |
| 444 | } |
| 445 | div.selectedText { |
| 446 | font-weight: bold; |
| 447 | color: blue; |
| 448 | background-color: #d5d5ff; |
| 449 | border: 1px blue solid; |
| 450 | } |
| 451 | p.missingPriv { |
| 452 | color: blue; |
| 453 | } |
| 454 | span.wikiruleHead { |
| 455 | font-weight: bold; |
| @@ -774,44 +768,93 @@ | |
| 774 | div.forumTime > div > form, |
| 775 | div.forumHierRoot > div > form { |
| 776 | margin: 0.5em 0; |
| 777 | } |
| 778 | .forum-post-collapser { |
| 779 | font-size: 0.8em; |
| 780 | margin-top: 0.2em; |
| 781 | padding: 0; |
| 782 | height: 1.75em; |
| 783 | line-height: 1.75em; |
| 784 | /* ^^^ Those sizes are finely tuned for the current selection of |
| 785 | arrow characters. If those change, these should, too. Remember that |
| 786 | FF/Chrome simply do not agree on alignment with most values :/. */ |
| 787 | border-width: 1px; |
| 788 | border-style: solid; |
| 789 | border-radius: 0.25em; |
| 790 | opacity: 0.8; |
| 791 | cursor: pointer; |
| 792 | display: flex; |
| 793 | flex-direction: row; |
| 794 | justify-content: space-between; |
| 795 | } |
| 796 | .forum-post-collapser > span { |
| 797 | margin: 0 1em 0 1em; |
| 798 | vertical-align: middle; |
| 799 | } |
| 800 | .forum-post-collapser.expanded > span::before { |
| 801 | content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; |
| 802 | } |
| 803 | .forum-post-collapser:not(.expanded) > span::before { |
| 804 | content: "⇣⇣⇣"; |
| 805 | } |
| 806 | div.forumPostBody{ |
| 807 | max-height: 50em; |
| 808 | overflow: auto; |
| 809 | } |
| 810 | div.forumPostBody.expanded { |
| 811 | max-height: initial; |
| 812 | } |
| 813 | div.forumSel { |
| 814 | background-color: #cef; |
| 815 | } |
| 816 | div.forumObs { |
| 817 | color: #bbb; |
| @@ -904,14 +947,19 @@ | |
| 904 | color: darkred; |
| 905 | background: yellow; |
| 906 | opacity: 0.7; |
| 907 | } |
| 908 | .hidden { |
| 909 | position: absolute; |
| 910 | opacity: 0; |
| 911 | pointer-events: none; |
| 912 | display: none; |
| 913 | } |
| 914 | input { |
| 915 | max-width: 95%; |
| 916 | } |
| 917 | textarea { |
| @@ -919,5 +967,311 @@ | |
| 919 | } |
| 920 | img { |
| 921 | max-width: 100%; |
| 922 | height: auto; |
| 923 | } |
| 924 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -440,16 +440,10 @@ | |
| 440 | content:"'"; |
| 441 | } |
| 442 | span.usertype:after { |
| 443 | content:"'"; |
| 444 | } |
| 445 | p.missingPriv { |
| 446 | color: blue; |
| 447 | } |
| 448 | span.wikiruleHead { |
| 449 | font-weight: bold; |
| @@ -774,44 +768,93 @@ | |
| 768 | div.forumTime > div > form, |
| 769 | div.forumHierRoot > div > form { |
| 770 | margin: 0.5em 0; |
| 771 | } |
| 772 | .forum-post-collapser { |
| 773 | /* Common style for the bottom-of-post and right-of-post |
| 774 | expand/collapse widgets. */ |
| 775 | font-size: 0.8em; |
| 776 | padding: 0; |
| 777 | border: 1px solid rgba(0, 0, 0, 0.2); |
| 778 | border-radius: 0 0 0.5em 0.5em; |
| 779 | background-color: rgba(0, 0, 0, 0.05); |
| 780 | opacity: 0.8; |
| 781 | cursor: pointer; |
| 782 | } |
| 783 | .forum-post-collapser.bottom { |
| 784 | margin: 0 0 0.4em 0; |
| 785 | height: 1.75em; |
| 786 | line-height: 1.75em; |
| 787 | /* ^^^ Those sizes are finely tuned for the current selection of |
| 788 | arrow characters. If those change, these should, too. Remember that |
| 789 | FF/Chrome simply do not agree on alignment with most values :/. */ |
| 790 | display: flex; |
| 791 | flex-direction: row; |
| 792 | justify-content: space-between; |
| 793 | } |
| 794 | .forum-post-collapser.bottom > span { |
| 795 | margin: 0 1em 0 1em; |
| 796 | vertical-align: middle; |
| 797 | } |
| 798 | .forum-post-collapser.bottom > span::before { |
| 799 | content: "⇣⇣⇣"; |
| 800 | } |
| 801 | .forum-post-collapser.bottom.expanded > span::before { |
| 802 | content: "⇡⇡⇡" /*reminder: FF/Chrome cannot agree on alignment of ⮝*/; |
| 803 | } |
| 804 | div.forumPostBody{ |
| 805 | max-height: 50em; |
| 806 | overflow: auto; |
| 807 | } |
| 808 | div.forumPostBody.with-expander { |
| 809 | display: flex; |
| 810 | flex-direction: row; |
| 811 | overflow: auto; |
| 812 | } |
| 813 | div.forumPostBody.with-expander:not(.expanded) > :first-child { |
| 814 | overflow-y: hidden; |
| 815 | } |
| 816 | div.forumPostBody.with-expander > *:first-child { |
| 817 | /* Main content DIV/PRE */ |
| 818 | overflow: auto; |
| 819 | flex: 10 1 auto; |
| 820 | } |
| 821 | div.forumPostBody.with-expander.expanded > *:first-child { |
| 822 | margin-bottom: 0.5em /* try to suppress scroll bar */; |
| 823 | } |
| 824 | div.forumPostBody.with-expander .forum-post-collapser.right { |
| 825 | /* "Tap zone" for expansion of the post, sits to the right of the |
| 826 | post's content. */ |
| 827 | flex: 1 10 auto; |
| 828 | min-width: 1.25em; |
| 829 | max-width: 1.25em; |
| 830 | margin: 0 0 0 0.2em; |
| 831 | overflow: hidden; |
| 832 | display: flex; |
| 833 | flex-direction: column; |
| 834 | justify-content: space-around; |
| 835 | align-items: center; |
| 836 | border-radius: 0.1em; |
| 837 | cursor: pointer; |
| 838 | border-bottom: 0; |
| 839 | border-radius: 0 0.5em 0 0; |
| 840 | } |
| 841 | div.forumPostBody.with-expander .forum-post-collapser.right > span:before { |
| 842 | content: "⇣"; |
| 843 | } |
| 844 | div.forumPostBody.with-expander.expanded .forum-post-collapser.right > span:before { |
| 845 | content: "⇡"; |
| 846 | } |
| 847 | div.forumPostBody.expanded { |
| 848 | max-height: initial; |
| 849 | } |
| 850 | div.forumPostBody.shrunken { |
| 851 | /* When an expandable post is un-expanded, it is shrunkend down |
| 852 | to this size instead of its original size. */ |
| 853 | max-height: 8em; |
| 854 | } |
| 855 | |
| 856 | div.forumSel { |
| 857 | background-color: #cef; |
| 858 | } |
| 859 | div.forumObs { |
| 860 | color: #bbb; |
| @@ -904,14 +947,19 @@ | |
| 947 | color: darkred; |
| 948 | background: yellow; |
| 949 | opacity: 0.7; |
| 950 | } |
| 951 | .hidden { |
| 952 | /* The framework-wide way of hiding elements is to assign them this |
| 953 | CSS class. To make them visible again, remove it. The !important |
| 954 | qualifiers are unfortunate but sometimes necessary when hidden |
| 955 | element has other classes which specify visibility-related |
| 956 | options. */ |
| 957 | position: absolute !important; |
| 958 | opacity: 0 !important; |
| 959 | pointer-events: none !important; |
| 960 | display: none !important; |
| 961 | } |
| 962 | input { |
| 963 | max-width: 95%; |
| 964 | } |
| 965 | textarea { |
| @@ -919,5 +967,311 @@ | |
| 967 | } |
| 968 | img { |
| 969 | max-width: 100%; |
| 970 | height: auto; |
| 971 | } |
| 972 | hr { |
| 973 | /* Needed to keep /dir README.txt from floating right in some skins */ |
| 974 | clear: both; |
| 975 | } |
| 976 | |
| 977 | /** |
| 978 | .tab-xxx: styles for fossil.tabs.js. |
| 979 | */ |
| 980 | .tab-container { |
| 981 | width: 100%; |
| 982 | display: flex; |
| 983 | flex-direction: column; |
| 984 | align-items: stretch; |
| 985 | } |
| 986 | .tab-container > #fossil-status-bar { |
| 987 | margin-top: 0; |
| 988 | } |
| 989 | .tab-container > .tabs { |
| 990 | padding: 0.25em; |
| 991 | margin: 0; |
| 992 | display: flex; |
| 993 | flex-direction: column; |
| 994 | border-width: 1px; |
| 995 | border-style: outset; |
| 996 | border-color: inherit; |
| 997 | } |
| 998 | .tab-container > .tabs > .tab-panel { |
| 999 | align-self: stretch; |
| 1000 | flex: 10 1 auto; |
| 1001 | display: block; |
| 1002 | border: 0; |
| 1003 | padding: 0; |
| 1004 | margin: 0; |
| 1005 | } |
| 1006 | .tab-container > .tab-bar { |
| 1007 | display: flex; |
| 1008 | flex-direction: row; |
| 1009 | flex: 1 10 auto; |
| 1010 | align-self: stretch; |
| 1011 | flex-wrap: wrap; |
| 1012 | } |
| 1013 | .tab-container > .tab-bar > .tab-button { |
| 1014 | display: inline-block; |
| 1015 | border-radius: 0.25em 0.25em 0 0; |
| 1016 | margin: 0 0.1em; |
| 1017 | padding: 0.25em 0.75em; |
| 1018 | align-self: baseline; |
| 1019 | border-color: inherit; |
| 1020 | border-width: 1px; |
| 1021 | border-bottom: none; |
| 1022 | border-top-style: inset; |
| 1023 | border-left-style: inset; |
| 1024 | border-right-style: inset; |
| 1025 | cursor: pointer; |
| 1026 | opacity: 0.6; |
| 1027 | } |
| 1028 | .tab-container > .tab-bar > .tab-button.selected { |
| 1029 | text-decoration: underline; |
| 1030 | opacity: 1.0; |
| 1031 | border-top-style: outset; |
| 1032 | border-left-style: outset; |
| 1033 | border-right-style: outset; |
| 1034 | } |
| 1035 | |
| 1036 | /** |
| 1037 | The flex-xxx classes can be used to create basic flexbox layouts |
| 1038 | through the application of classes to the containing/contained |
| 1039 | objects. |
| 1040 | */ |
| 1041 | .flex-container { |
| 1042 | display: flex; |
| 1043 | } |
| 1044 | .flex-container.flex-row { |
| 1045 | flex-direction: row; |
| 1046 | flex-wrap: wrap; |
| 1047 | justify-content: center; |
| 1048 | align-items: center; |
| 1049 | } |
| 1050 | .flex-container .flex-grow { |
| 1051 | flex-grow: 10; |
| 1052 | flex-shrink: 0; |
| 1053 | } |
| 1054 | .flex-container .flex-shrink { |
| 1055 | flex-grow: 0; |
| 1056 | flex-shrink: 10; |
| 1057 | } |
| 1058 | .flex-container.flex-row.stretch { |
| 1059 | flex-wrap: wrap; |
| 1060 | align-items: baseline; |
| 1061 | justify-content: stretch; |
| 1062 | margin: 0; |
| 1063 | } |
| 1064 | .flex-container.flex-column { |
| 1065 | flex-direction: column; |
| 1066 | flex-wrap: wrap; |
| 1067 | justify-content: center; |
| 1068 | align-items: center; |
| 1069 | } |
| 1070 | .flex-container.flex-column.stretch { |
| 1071 | align-items: stretch; |
| 1072 | margin: 0; |
| 1073 | } |
| 1074 | .flex-container.child-gap-small > * { |
| 1075 | margin: 0.25em; |
| 1076 | } |
| 1077 | #fossil-status-bar { |
| 1078 | display: block; |
| 1079 | font-family: monospace; |
| 1080 | border-width: 1px; |
| 1081 | border-style: inset; |
| 1082 | border-color: inherit; |
| 1083 | min-height: 1.5em; |
| 1084 | font-size: 1.2em; |
| 1085 | padding: 0.2em; |
| 1086 | margin: 0.25em 0; |
| 1087 | flex: 0 0 auto; |
| 1088 | } |
| 1089 | .font-size-100 { |
| 1090 | font-size: 100%; |
| 1091 | } |
| 1092 | .font-size-125 { |
| 1093 | font-size: 125%; |
| 1094 | } |
| 1095 | .font-size-150 { |
| 1096 | font-size: 150%; |
| 1097 | } |
| 1098 | .font-size-175 { |
| 1099 | font-size: 175%; |
| 1100 | } |
| 1101 | .font-size-200 { |
| 1102 | font-size: 200%; |
| 1103 | } |
| 1104 | |
| 1105 | /** |
| 1106 | .input-with-label is intended to be a wrapper element which |
| 1107 | contain both a LABEL tag and an INPUT or SELECT control. |
| 1108 | The wrapper is "necessary", as opposed to placing the INPUT |
| 1109 | in the LABEL, so that we can include multiple INPUT |
| 1110 | elements (e.g. a set of radio buttons). |
| 1111 | */ |
| 1112 | .input-with-label { |
| 1113 | border: 1px inset #808080; |
| 1114 | border-radius: 0.25em; |
| 1115 | padding: 0.25em 0.4em; |
| 1116 | margin: 0 0.5em; |
| 1117 | display: inline-block; |
| 1118 | cursor: default; |
| 1119 | } |
| 1120 | .input-with-label > * { |
| 1121 | vertical-align: middle; |
| 1122 | } |
| 1123 | .input-with-label > label { |
| 1124 | display: inline; /* some skins set label display to block! */ |
| 1125 | } |
| 1126 | .input-with-label > input { |
| 1127 | margin: 0; |
| 1128 | } |
| 1129 | .input-with-label > button { |
| 1130 | margin: 0; |
| 1131 | } |
| 1132 | .input-with-label > select { |
| 1133 | margin: 0; |
| 1134 | } |
| 1135 | .input-with-label > input[type=text] { |
| 1136 | margin: 0; |
| 1137 | } |
| 1138 | .input-with-label > textarea { |
| 1139 | margin: 0; |
| 1140 | } |
| 1141 | .input-with-label > input[type=checkbox] { |
| 1142 | vertical-align: sub; |
| 1143 | } |
| 1144 | .input-with-label > input[type=radio] { |
| 1145 | vertical-align: sub; |
| 1146 | } |
| 1147 | .input-with-label > label { |
| 1148 | font-weight: initial; |
| 1149 | margin: 0 0.25em 0 0.25em; |
| 1150 | vertical-align: middle; |
| 1151 | } |
| 1152 | |
| 1153 | table.numbered-lines { |
| 1154 | width: 100%; |
| 1155 | table-layout: fixed /* required to keep ultra-wide code from exceeding |
| 1156 | window width, and instead force a scrollbar |
| 1157 | on them. */; |
| 1158 | } |
| 1159 | table.numbered-lines > tbody > tr { |
| 1160 | font-family: monospace; |
| 1161 | font-size: 1.2em; |
| 1162 | line-height: 1.35; |
| 1163 | white-space: pre; |
| 1164 | } |
| 1165 | table.numbered-lines > tbody > tr > td { |
| 1166 | font-family: inherit; |
| 1167 | font-size: inherit; |
| 1168 | line-height: inherit; |
| 1169 | white-space: inherit; |
| 1170 | margin: 0; |
| 1171 | vertical-align: top; |
| 1172 | padding: 0.25em 0 0 0 /*prevents slight overlap at top */; |
| 1173 | } |
| 1174 | table.numbered-lines td.line-numbers { |
| 1175 | width: 4.5em; |
| 1176 | } |
| 1177 | table.numbered-lines td.line-numbers > span:first-of-type { |
| 1178 | margin-top: 0.25em/*must match top PADDING of |
| 1179 | td.file-content > pre > code*/; |
| 1180 | } |
| 1181 | table.numbered-lines td.line-numbers > span { |
| 1182 | display: block; |
| 1183 | margin: 0; |
| 1184 | padding: 0; |
| 1185 | line-height: inherit; |
| 1186 | font-size: inherit; |
| 1187 | font-family: inherit; |
| 1188 | cursor: pointer; |
| 1189 | white-space: pre; |
| 1190 | margin-right: 2px/*keep selection from nudging the right column */; |
| 1191 | text-align: right; |
| 1192 | } |
| 1193 | table.numbered-lines td.line-numbers > span:hover { |
| 1194 | background-color: rgba(112, 112, 112, 0.25); |
| 1195 | } |
| 1196 | table.numbered-lines td.file-content { |
| 1197 | padding-left: 0.25em; |
| 1198 | } |
| 1199 | table.numbered-lines td.file-content > pre, |
| 1200 | table.numbered-lines td.file-content > pre > code { |
| 1201 | margin: 0; |
| 1202 | padding: 0; |
| 1203 | line-height: inherit; |
| 1204 | font-size: inherit; |
| 1205 | font-family: inherit; |
| 1206 | white-space: pre; |
| 1207 | display: block/*necessary for certain skins!*/; |
| 1208 | } |
| 1209 | table.numbered-lines td.file-content > pre { |
| 1210 | } |
| 1211 | table.numbered-lines td.file-content > pre > code { |
| 1212 | overflow: auto; |
| 1213 | padding-left: 0.5em; |
| 1214 | padding-right: 0.5em; |
| 1215 | padding-top: 0.25em/*any top padding here must match the top MARGIN of |
| 1216 | td.line-numbers's first span child or the |
| 1217 | lines/code will get misaligned. */; |
| 1218 | padding-bottom: 0.25em/*prevents a slight overlap at bottom from |
| 1219 | triggering a scroller*/; |
| 1220 | } |
| 1221 | table.numbered-lines td.file-content > pre > code > * { |
| 1222 | /* Defense against syntax highlighters indirectly messing up these |
| 1223 | properties... */ |
| 1224 | line-height: inherit; |
| 1225 | font-size: inherit; |
| 1226 | font-family: inherit; |
| 1227 | } |
| 1228 | table.numbered-lines td.line-numbers span.selected-line/*replacement*/ { |
| 1229 | font-weight: bold; |
| 1230 | color: blue; |
| 1231 | background-color: #d5d5ff; |
| 1232 | border: 1px blue solid; |
| 1233 | border-top-width: 0; |
| 1234 | border-bottom-width: 0; |
| 1235 | padding: 0; |
| 1236 | margin: 0; |
| 1237 | } |
| 1238 | table.numbered-lines td.line-numbers span.selected-line.start { |
| 1239 | border-top-width: 1px; |
| 1240 | margin-top: -1px/*restore alignment*/; |
| 1241 | } |
| 1242 | table.numbered-lines td.line-numbers span.selected-line.end { |
| 1243 | border-bottom-width: 1px; |
| 1244 | margin-top: -1px/*restore alignment*/; |
| 1245 | } |
| 1246 | table.numbered-lines td.line-numbers span.selected-line.start.end { |
| 1247 | margin-top: -2px/*restore alignment*/; |
| 1248 | } |
| 1249 | |
| 1250 | .fossil-tooltip { |
| 1251 | text-align: center; |
| 1252 | padding: 0.2em 1em; |
| 1253 | border: 1px solid black; |
| 1254 | border-radius: 0.25em; |
| 1255 | position: absolute; |
| 1256 | display: inline-block; |
| 1257 | z-index: 100; |
| 1258 | box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75); |
| 1259 | background-color: inherit; |
| 1260 | font-size: 80%; |
| 1261 | } |
| 1262 | |
| 1263 | .fossil-toast {/* "toast"-style popup message */ |
| 1264 | padding: 0.25em 0.5em; |
| 1265 | margin: 0; |
| 1266 | border-radius: 0.25em; |
| 1267 | font-size: 1em; |
| 1268 | opacity: 0.8; |
| 1269 | border-size: 1px; |
| 1270 | border-style: dotted; |
| 1271 | border-color: rgb( 127, 127, 127, 0.5 ); |
| 1272 | } |
| 1273 | |
| 1274 | blockquote.file-content { |
| 1275 | /* file content block in the /file page */ |
| 1276 | margin: 0 1em; |
| 1277 | } |
| 1278 |
+2
-2
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -348,11 +348,11 @@ | ||
| 348 | 348 | ** -R|--repository FILE Extract info from repository FILE |
| 349 | 349 | ** -W|--width <num> Width of lines (default is to auto-detect). |
| 350 | 350 | ** Must be >20 or 0 (= no limit, resulting in a |
| 351 | 351 | ** single line per entry). |
| 352 | 352 | ** |
| 353 | -** See also: finfo, info, leaves | |
| 353 | +** See also: [[finfo]], [[info]], [[leaves]] | |
| 354 | 354 | */ |
| 355 | 355 | void descendants_cmd(void){ |
| 356 | 356 | Stmt q; |
| 357 | 357 | int base, width; |
| 358 | 358 | const char *zWidth; |
| @@ -408,11 +408,11 @@ | ||
| 408 | 408 | ** --recompute recompute the "leaf" table in the repository DB |
| 409 | 409 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 410 | 410 | ** >39 or 0 (= no limit, resulting in a single line per |
| 411 | 411 | ** entry). |
| 412 | 412 | ** |
| 413 | -** See also: descendants, finfo, info, branch | |
| 413 | +** See also: [[descendants]], [[finfo]], [[info]], [[branch]] | |
| 414 | 414 | */ |
| 415 | 415 | void leaves_cmd(void){ |
| 416 | 416 | Stmt q; |
| 417 | 417 | Blob sql; |
| 418 | 418 | int showAll = find_option("all", "a", 0)!=0; |
| 419 | 419 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | ** -R|--repository FILE Extract info from repository FILE |
| 349 | ** -W|--width <num> Width of lines (default is to auto-detect). |
| 350 | ** Must be >20 or 0 (= no limit, resulting in a |
| 351 | ** single line per entry). |
| 352 | ** |
| 353 | ** See also: finfo, info, leaves |
| 354 | */ |
| 355 | void descendants_cmd(void){ |
| 356 | Stmt q; |
| 357 | int base, width; |
| 358 | const char *zWidth; |
| @@ -408,11 +408,11 @@ | |
| 408 | ** --recompute recompute the "leaf" table in the repository DB |
| 409 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 410 | ** >39 or 0 (= no limit, resulting in a single line per |
| 411 | ** entry). |
| 412 | ** |
| 413 | ** See also: descendants, finfo, info, branch |
| 414 | */ |
| 415 | void leaves_cmd(void){ |
| 416 | Stmt q; |
| 417 | Blob sql; |
| 418 | int showAll = find_option("all", "a", 0)!=0; |
| 419 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | ** -R|--repository FILE Extract info from repository FILE |
| 349 | ** -W|--width <num> Width of lines (default is to auto-detect). |
| 350 | ** Must be >20 or 0 (= no limit, resulting in a |
| 351 | ** single line per entry). |
| 352 | ** |
| 353 | ** See also: [[finfo]], [[info]], [[leaves]] |
| 354 | */ |
| 355 | void descendants_cmd(void){ |
| 356 | Stmt q; |
| 357 | int base, width; |
| 358 | const char *zWidth; |
| @@ -408,11 +408,11 @@ | |
| 408 | ** --recompute recompute the "leaf" table in the repository DB |
| 409 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 410 | ** >39 or 0 (= no limit, resulting in a single line per |
| 411 | ** entry). |
| 412 | ** |
| 413 | ** See also: [[descendants]], [[finfo]], [[info]], [[branch]] |
| 414 | */ |
| 415 | void leaves_cmd(void){ |
| 416 | Stmt q; |
| 417 | Blob sql; |
| 418 | int showAll = find_option("all", "a", 0)!=0; |
| 419 |
+3
-3
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -125,11 +125,11 @@ | ||
| 125 | 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | 127 | ** function, a string is considered empty if it contains no characters |
| 128 | 128 | ** -OR- it contains only NUL characters. |
| 129 | 129 | */ |
| 130 | -static int count_lines( | |
| 130 | +int count_lines( | |
| 131 | 131 | const char *z, |
| 132 | 132 | int n, |
| 133 | 133 | int *pnLine |
| 134 | 134 | ){ |
| 135 | 135 | int nLine; |
| @@ -2562,11 +2562,11 @@ | ||
| 2562 | 2562 | } |
| 2563 | 2563 | |
| 2564 | 2564 | /* |
| 2565 | 2565 | ** COMMAND: annotate |
| 2566 | 2566 | ** COMMAND: blame |
| 2567 | -** COMMAND: praise | |
| 2567 | +** COMMAND: praise* | |
| 2568 | 2568 | ** |
| 2569 | 2569 | ** Usage: %fossil annotate|blame|praise ?OPTIONS? FILENAME |
| 2570 | 2570 | ** |
| 2571 | 2571 | ** Output the text of a file with markings to show when each line of the file |
| 2572 | 2572 | ** was last modified. The version currently checked out is shown by default. |
| @@ -2596,11 +2596,11 @@ | ||
| 2596 | 2596 | ** root of the repository. Set to "trunk" or |
| 2597 | 2597 | ** similar for a reverse annotation. |
| 2598 | 2598 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 2599 | 2599 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 2600 | 2600 | ** |
| 2601 | -** See also: info, finfo, timeline | |
| 2601 | +** See also: [[info]], [[finfo]], [[timeline]] | |
| 2602 | 2602 | */ |
| 2603 | 2603 | void annotate_cmd(void){ |
| 2604 | 2604 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| 2605 | 2605 | Annotator ann; /* The annotation of the file */ |
| 2606 | 2606 | int i; /* Loop counter */ |
| 2607 | 2607 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -125,11 +125,11 @@ | |
| 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | ** function, a string is considered empty if it contains no characters |
| 128 | ** -OR- it contains only NUL characters. |
| 129 | */ |
| 130 | static int count_lines( |
| 131 | const char *z, |
| 132 | int n, |
| 133 | int *pnLine |
| 134 | ){ |
| 135 | int nLine; |
| @@ -2562,11 +2562,11 @@ | |
| 2562 | } |
| 2563 | |
| 2564 | /* |
| 2565 | ** COMMAND: annotate |
| 2566 | ** COMMAND: blame |
| 2567 | ** COMMAND: praise |
| 2568 | ** |
| 2569 | ** Usage: %fossil annotate|blame|praise ?OPTIONS? FILENAME |
| 2570 | ** |
| 2571 | ** Output the text of a file with markings to show when each line of the file |
| 2572 | ** was last modified. The version currently checked out is shown by default. |
| @@ -2596,11 +2596,11 @@ | |
| 2596 | ** root of the repository. Set to "trunk" or |
| 2597 | ** similar for a reverse annotation. |
| 2598 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 2599 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 2600 | ** |
| 2601 | ** See also: info, finfo, timeline |
| 2602 | */ |
| 2603 | void annotate_cmd(void){ |
| 2604 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| 2605 | Annotator ann; /* The annotation of the file */ |
| 2606 | int i; /* Loop counter */ |
| 2607 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -125,11 +125,11 @@ | |
| 125 | ** in the count even if it lacks the \n terminator. If an empty string |
| 126 | ** is specified, the number of lines is zero. For the purposes of this |
| 127 | ** function, a string is considered empty if it contains no characters |
| 128 | ** -OR- it contains only NUL characters. |
| 129 | */ |
| 130 | int count_lines( |
| 131 | const char *z, |
| 132 | int n, |
| 133 | int *pnLine |
| 134 | ){ |
| 135 | int nLine; |
| @@ -2562,11 +2562,11 @@ | |
| 2562 | } |
| 2563 | |
| 2564 | /* |
| 2565 | ** COMMAND: annotate |
| 2566 | ** COMMAND: blame |
| 2567 | ** COMMAND: praise* |
| 2568 | ** |
| 2569 | ** Usage: %fossil annotate|blame|praise ?OPTIONS? FILENAME |
| 2570 | ** |
| 2571 | ** Output the text of a file with markings to show when each line of the file |
| 2572 | ** was last modified. The version currently checked out is shown by default. |
| @@ -2596,11 +2596,11 @@ | |
| 2596 | ** root of the repository. Set to "trunk" or |
| 2597 | ** similar for a reverse annotation. |
| 2598 | ** -w|--ignore-all-space Ignore white space when comparing lines |
| 2599 | ** -Z|--ignore-trailing-space Ignore whitespace at line end |
| 2600 | ** |
| 2601 | ** See also: [[info]], [[finfo]], [[timeline]] |
| 2602 | */ |
| 2603 | void annotate_cmd(void){ |
| 2604 | const char *zRevision; /* Revision name, or NULL for current check-in */ |
| 2605 | Annotator ann; /* The annotation of the file */ |
| 2606 | int i; /* Loop counter */ |
| 2607 |
+80
-8
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -263,10 +263,48 @@ | ||
| 263 | 263 | if( zEnd[0] ) blob_append(pOut, zEnd, -1); |
| 264 | 264 | i = j; |
| 265 | 265 | } |
| 266 | 266 | } |
| 267 | 267 | } |
| 268 | + | |
| 269 | +/* | |
| 270 | +** Input string zIn starts with '['. If the content is a hyperlink of the | |
| 271 | +** form [[...]] then return the index of the closing ']'. Otherwise return 0. | |
| 272 | +*/ | |
| 273 | +static int help_is_link(const char *z, int n){ | |
| 274 | + int i; | |
| 275 | + char c; | |
| 276 | + if( n<5 ) return 0; | |
| 277 | + if( z[1]!='[' ) return 0; | |
| 278 | + for(i=3; i<n && (c = z[i])!=0; i++){ | |
| 279 | + if( c==']' && z[i-1]==']' ) return i; | |
| 280 | + } | |
| 281 | + return 0; | |
| 282 | +} | |
| 283 | + | |
| 284 | +/* | |
| 285 | +** Append text to pOut, adding hyperlink markup for [...]. | |
| 286 | +*/ | |
| 287 | +static void appendLinked(Blob *pOut, const char *z, int n){ | |
| 288 | + int i = 0; | |
| 289 | + int j; | |
| 290 | + while( i<n ){ | |
| 291 | + if( z[i]=='[' && (j = help_is_link(z+i, n-i))>0 ){ | |
| 292 | + if( i ) blob_append(pOut, z, i); | |
| 293 | + z += i+2; | |
| 294 | + n -= i+2; | |
| 295 | + blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>", | |
| 296 | + j-3, z, j-3, z); | |
| 297 | + z += j-1; | |
| 298 | + n -= j-1; | |
| 299 | + i = 0; | |
| 300 | + }else{ | |
| 301 | + i++; | |
| 302 | + } | |
| 303 | + } | |
| 304 | + blob_append(pOut, z, i); | |
| 305 | +} | |
| 268 | 306 | |
| 269 | 307 | /* |
| 270 | 308 | ** Attempt to reformat plain-text help into HTML for display on a webpage. |
| 271 | 309 | ** |
| 272 | 310 | ** The HTML output is appended to Blob pHtml, which should already be |
| @@ -305,17 +343,22 @@ | ||
| 305 | 343 | azEnd[0] = ""; |
| 306 | 344 | while( zHelp[0] ){ |
| 307 | 345 | i = 0; |
| 308 | 346 | while( (c = zHelp[i])!=0 |
| 309 | 347 | && c!='\n' |
| 348 | + && c!='<' | |
| 310 | 349 | && (c!='%' || strncmp(zHelp+i,"%fossil",7)!=0) |
| 311 | 350 | ){ i++; } |
| 312 | 351 | if( c=='%' ){ |
| 313 | 352 | if( i ) blob_appendf(pHtml, "%#h", i, zHelp); |
| 314 | 353 | zHelp += i + 1; |
| 315 | - i = 0; | |
| 316 | 354 | wantBR = 1; |
| 355 | + continue; | |
| 356 | + }else if( c=='<' ){ | |
| 357 | + if( i ) blob_appendf(pHtml, "%#h", i, zHelp); | |
| 358 | + blob_append(pHtml, "&", 5); | |
| 359 | + zHelp += i + 1; | |
| 317 | 360 | continue; |
| 318 | 361 | } |
| 319 | 362 | if( i>2 && zHelp[0]=='>' && zHelp[1]==' ' ){ |
| 320 | 363 | isDT = 1; |
| 321 | 364 | for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){} |
| @@ -394,11 +437,12 @@ | ||
| 394 | 437 | }else if( wantBR ){ |
| 395 | 438 | appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); |
| 396 | 439 | blob_append(pHtml, "<br>\n", 5); |
| 397 | 440 | wantBR = 0; |
| 398 | 441 | }else{ |
| 399 | - blob_appendf(pHtml, "%#h\n", i-nIndent, zHelp+nIndent); | |
| 442 | + appendLinked(pHtml, zHelp+nIndent, i-nIndent); | |
| 443 | + blob_append_char(pHtml, '\n'); | |
| 400 | 444 | } |
| 401 | 445 | zHelp += i+1; |
| 402 | 446 | i = 0; |
| 403 | 447 | if( c==0 ) break; |
| 404 | 448 | } |
| @@ -409,11 +453,11 @@ | ||
| 409 | 453 | |
| 410 | 454 | /* |
| 411 | 455 | ** Format help text for TTY display. |
| 412 | 456 | */ |
| 413 | 457 | static void help_to_text(const char *zHelp, Blob *pText){ |
| 414 | - int i; | |
| 458 | + int i, x; | |
| 415 | 459 | char c; |
| 416 | 460 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 417 | 461 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 418 | 462 | if( i>0 ) blob_append(pText, zHelp, i); |
| 419 | 463 | blob_append(pText, "fossil", 6); |
| @@ -426,10 +470,18 @@ | ||
| 426 | 470 | blob_append(pText, " ", 1); |
| 427 | 471 | zHelp += i+2; |
| 428 | 472 | i = -1; |
| 429 | 473 | continue; |
| 430 | 474 | } |
| 475 | + if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){ | |
| 476 | + if( i>0 ) blob_append(pText, zHelp, i); | |
| 477 | + zHelp += i+2; | |
| 478 | + blob_append(pText, zHelp, x-3); | |
| 479 | + zHelp += x-1; | |
| 480 | + i = -1; | |
| 481 | + continue; | |
| 482 | + } | |
| 431 | 483 | } |
| 432 | 484 | if( i>0 ){ |
| 433 | 485 | blob_append(pText, zHelp, i); |
| 434 | 486 | } |
| 435 | 487 | } |
| @@ -447,15 +499,17 @@ | ||
| 447 | 499 | ** -e|--everything Show all commands and pages. |
| 448 | 500 | ** -t|--test Include test- commands |
| 449 | 501 | ** -w|--www Show WWW pages. |
| 450 | 502 | ** -s|--settings Show settings. |
| 451 | 503 | ** -h|--html Transform output to HTML. |
| 504 | +** -r|--raw No output formatting. | |
| 452 | 505 | */ |
| 453 | 506 | void test_all_help_cmd(void){ |
| 454 | 507 | int i; |
| 455 | 508 | int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; |
| 456 | 509 | int useHtml = find_option("html","h",0)!=0; |
| 510 | + int rawOut = find_option("raw","r",0)!=0; | |
| 457 | 511 | |
| 458 | 512 | if( find_option("www","w",0) ){ |
| 459 | 513 | mask = CMDFLAG_WEBPAGE; |
| 460 | 514 | } |
| 461 | 515 | if( find_option("everything","e",0) ){ |
| @@ -488,10 +542,13 @@ | ||
| 488 | 542 | blob_init(&html, 0, 0); |
| 489 | 543 | help_to_html(aCommand[i].zHelp, &html); |
| 490 | 544 | fossil_print("<h1>%h</h1>\n", aCommand[i].zName); |
| 491 | 545 | fossil_print("%s\n<hr>\n", blob_str(&html)); |
| 492 | 546 | blob_reset(&html); |
| 547 | + }else if( rawOut ){ | |
| 548 | + fossil_print("# %s\n", aCommand[i].zName); | |
| 549 | + fossil_print("%s\n\n", aCommand[i].zHelp); | |
| 493 | 550 | }else{ |
| 494 | 551 | Blob txt; |
| 495 | 552 | blob_init(&txt, 0, 0); |
| 496 | 553 | help_to_text(aCommand[i].zHelp, &txt); |
| 497 | 554 | fossil_print("# %s\n", aCommand[i].zName); |
| @@ -665,11 +722,11 @@ | ||
| 665 | 722 | const CmdOrPage *pCmd = 0; |
| 666 | 723 | |
| 667 | 724 | style_header("Help: %s", zCmd); |
| 668 | 725 | |
| 669 | 726 | style_submenu_element("Command-List", "%s/help", g.zTop); |
| 670 | - rc = dispatch_name_search(zCmd, CMDFLAG_ANY, &pCmd); | |
| 727 | + rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); | |
| 671 | 728 | if( *zCmd=='/' ){ |
| 672 | 729 | /* Some of the webpages require query parameters in order to work. |
| 673 | 730 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 674 | 731 | @ <h1>The "%h(zCmd)" page:</h1> |
| 675 | 732 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| @@ -913,11 +970,11 @@ | ||
| 913 | 970 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 914 | 971 | ** topics is returned. |
| 915 | 972 | ** |
| 916 | 973 | ** The following options can be used when TOPIC is omitted: |
| 917 | 974 | ** |
| 918 | -** -a|--all List both command and auxiliary commands | |
| 975 | +** -a|--all List both common and auxiliary commands | |
| 919 | 976 | ** -o|--options List command-line options common to all commands |
| 920 | 977 | ** -s|--setting List setting names |
| 921 | 978 | ** -t|--test List unsupported "test" commands |
| 922 | 979 | ** -x|--aux List only auxiliary commands |
| 923 | 980 | ** -w|--www List all web pages |
| @@ -936,12 +993,13 @@ | ||
| 936 | 993 | Blob txt; |
| 937 | 994 | if( g.argc<3 ){ |
| 938 | 995 | z = g.argv[0]; |
| 939 | 996 | fossil_print( |
| 940 | 997 | "Usage: %s help TOPIC\n" |
| 941 | - "Common commands: (use \"%s help help\" for more options)\n", | |
| 942 | - z, z); | |
| 998 | + "Try \"%s help help\" or \"%s help -a\" for more options\n" | |
| 999 | + "Frequently used commands:\n", | |
| 1000 | + z, z, z); | |
| 943 | 1001 | command_list(0, CMDFLAG_1ST_TIER); |
| 944 | 1002 | version_cmd(); |
| 945 | 1003 | return; |
| 946 | 1004 | } |
| 947 | 1005 | if( find_option("options","o",0) ){ |
| @@ -1076,11 +1134,11 @@ | ||
| 1076 | 1134 | ){ |
| 1077 | 1135 | helptextVtab_vtab *pNew; |
| 1078 | 1136 | int rc; |
| 1079 | 1137 | |
| 1080 | 1138 | rc = sqlite3_declare_vtab(db, |
| 1081 | - "CREATE TABLE x(name,type,flags,helptext)" | |
| 1139 | + "CREATE TABLE x(name,type,flags,helptext,formatted,html)" | |
| 1082 | 1140 | ); |
| 1083 | 1141 | if( rc==SQLITE_OK ){ |
| 1084 | 1142 | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 1085 | 1143 | *ppVtab = (sqlite3_vtab*)pNew; |
| 1086 | 1144 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -1160,10 +1218,24 @@ | ||
| 1160 | 1218 | sqlite3_result_int(ctx, pPage->eCmdFlags); |
| 1161 | 1219 | break; |
| 1162 | 1220 | case 3: /* helptext */ |
| 1163 | 1221 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1164 | 1222 | break; |
| 1223 | + case 4: { /* formatted */ | |
| 1224 | + Blob txt; | |
| 1225 | + blob_init(&txt, 0, 0); | |
| 1226 | + help_to_text(pPage->zHelp, &txt); | |
| 1227 | + sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); | |
| 1228 | + break; | |
| 1229 | + } | |
| 1230 | + case 5: { /* formatted */ | |
| 1231 | + Blob txt; | |
| 1232 | + blob_init(&txt, 0, 0); | |
| 1233 | + help_to_html(pPage->zHelp, &txt); | |
| 1234 | + sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); | |
| 1235 | + break; | |
| 1236 | + } | |
| 1165 | 1237 | } |
| 1166 | 1238 | return SQLITE_OK; |
| 1167 | 1239 | } |
| 1168 | 1240 | |
| 1169 | 1241 | /* |
| 1170 | 1242 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -263,10 +263,48 @@ | |
| 263 | if( zEnd[0] ) blob_append(pOut, zEnd, -1); |
| 264 | i = j; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | ** Attempt to reformat plain-text help into HTML for display on a webpage. |
| 271 | ** |
| 272 | ** The HTML output is appended to Blob pHtml, which should already be |
| @@ -305,17 +343,22 @@ | |
| 305 | azEnd[0] = ""; |
| 306 | while( zHelp[0] ){ |
| 307 | i = 0; |
| 308 | while( (c = zHelp[i])!=0 |
| 309 | && c!='\n' |
| 310 | && (c!='%' || strncmp(zHelp+i,"%fossil",7)!=0) |
| 311 | ){ i++; } |
| 312 | if( c=='%' ){ |
| 313 | if( i ) blob_appendf(pHtml, "%#h", i, zHelp); |
| 314 | zHelp += i + 1; |
| 315 | i = 0; |
| 316 | wantBR = 1; |
| 317 | continue; |
| 318 | } |
| 319 | if( i>2 && zHelp[0]=='>' && zHelp[1]==' ' ){ |
| 320 | isDT = 1; |
| 321 | for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){} |
| @@ -394,11 +437,12 @@ | |
| 394 | }else if( wantBR ){ |
| 395 | appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); |
| 396 | blob_append(pHtml, "<br>\n", 5); |
| 397 | wantBR = 0; |
| 398 | }else{ |
| 399 | blob_appendf(pHtml, "%#h\n", i-nIndent, zHelp+nIndent); |
| 400 | } |
| 401 | zHelp += i+1; |
| 402 | i = 0; |
| 403 | if( c==0 ) break; |
| 404 | } |
| @@ -409,11 +453,11 @@ | |
| 409 | |
| 410 | /* |
| 411 | ** Format help text for TTY display. |
| 412 | */ |
| 413 | static void help_to_text(const char *zHelp, Blob *pText){ |
| 414 | int i; |
| 415 | char c; |
| 416 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 417 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 418 | if( i>0 ) blob_append(pText, zHelp, i); |
| 419 | blob_append(pText, "fossil", 6); |
| @@ -426,10 +470,18 @@ | |
| 426 | blob_append(pText, " ", 1); |
| 427 | zHelp += i+2; |
| 428 | i = -1; |
| 429 | continue; |
| 430 | } |
| 431 | } |
| 432 | if( i>0 ){ |
| 433 | blob_append(pText, zHelp, i); |
| 434 | } |
| 435 | } |
| @@ -447,15 +499,17 @@ | |
| 447 | ** -e|--everything Show all commands and pages. |
| 448 | ** -t|--test Include test- commands |
| 449 | ** -w|--www Show WWW pages. |
| 450 | ** -s|--settings Show settings. |
| 451 | ** -h|--html Transform output to HTML. |
| 452 | */ |
| 453 | void test_all_help_cmd(void){ |
| 454 | int i; |
| 455 | int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; |
| 456 | int useHtml = find_option("html","h",0)!=0; |
| 457 | |
| 458 | if( find_option("www","w",0) ){ |
| 459 | mask = CMDFLAG_WEBPAGE; |
| 460 | } |
| 461 | if( find_option("everything","e",0) ){ |
| @@ -488,10 +542,13 @@ | |
| 488 | blob_init(&html, 0, 0); |
| 489 | help_to_html(aCommand[i].zHelp, &html); |
| 490 | fossil_print("<h1>%h</h1>\n", aCommand[i].zName); |
| 491 | fossil_print("%s\n<hr>\n", blob_str(&html)); |
| 492 | blob_reset(&html); |
| 493 | }else{ |
| 494 | Blob txt; |
| 495 | blob_init(&txt, 0, 0); |
| 496 | help_to_text(aCommand[i].zHelp, &txt); |
| 497 | fossil_print("# %s\n", aCommand[i].zName); |
| @@ -665,11 +722,11 @@ | |
| 665 | const CmdOrPage *pCmd = 0; |
| 666 | |
| 667 | style_header("Help: %s", zCmd); |
| 668 | |
| 669 | style_submenu_element("Command-List", "%s/help", g.zTop); |
| 670 | rc = dispatch_name_search(zCmd, CMDFLAG_ANY, &pCmd); |
| 671 | if( *zCmd=='/' ){ |
| 672 | /* Some of the webpages require query parameters in order to work. |
| 673 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 674 | @ <h1>The "%h(zCmd)" page:</h1> |
| 675 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| @@ -913,11 +970,11 @@ | |
| 913 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 914 | ** topics is returned. |
| 915 | ** |
| 916 | ** The following options can be used when TOPIC is omitted: |
| 917 | ** |
| 918 | ** -a|--all List both command and auxiliary commands |
| 919 | ** -o|--options List command-line options common to all commands |
| 920 | ** -s|--setting List setting names |
| 921 | ** -t|--test List unsupported "test" commands |
| 922 | ** -x|--aux List only auxiliary commands |
| 923 | ** -w|--www List all web pages |
| @@ -936,12 +993,13 @@ | |
| 936 | Blob txt; |
| 937 | if( g.argc<3 ){ |
| 938 | z = g.argv[0]; |
| 939 | fossil_print( |
| 940 | "Usage: %s help TOPIC\n" |
| 941 | "Common commands: (use \"%s help help\" for more options)\n", |
| 942 | z, z); |
| 943 | command_list(0, CMDFLAG_1ST_TIER); |
| 944 | version_cmd(); |
| 945 | return; |
| 946 | } |
| 947 | if( find_option("options","o",0) ){ |
| @@ -1076,11 +1134,11 @@ | |
| 1076 | ){ |
| 1077 | helptextVtab_vtab *pNew; |
| 1078 | int rc; |
| 1079 | |
| 1080 | rc = sqlite3_declare_vtab(db, |
| 1081 | "CREATE TABLE x(name,type,flags,helptext)" |
| 1082 | ); |
| 1083 | if( rc==SQLITE_OK ){ |
| 1084 | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 1085 | *ppVtab = (sqlite3_vtab*)pNew; |
| 1086 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -1160,10 +1218,24 @@ | |
| 1160 | sqlite3_result_int(ctx, pPage->eCmdFlags); |
| 1161 | break; |
| 1162 | case 3: /* helptext */ |
| 1163 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1164 | break; |
| 1165 | } |
| 1166 | return SQLITE_OK; |
| 1167 | } |
| 1168 | |
| 1169 | /* |
| 1170 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -263,10 +263,48 @@ | |
| 263 | if( zEnd[0] ) blob_append(pOut, zEnd, -1); |
| 264 | i = j; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | ** Input string zIn starts with '['. If the content is a hyperlink of the |
| 271 | ** form [[...]] then return the index of the closing ']'. Otherwise return 0. |
| 272 | */ |
| 273 | static int help_is_link(const char *z, int n){ |
| 274 | int i; |
| 275 | char c; |
| 276 | if( n<5 ) return 0; |
| 277 | if( z[1]!='[' ) return 0; |
| 278 | for(i=3; i<n && (c = z[i])!=0; i++){ |
| 279 | if( c==']' && z[i-1]==']' ) return i; |
| 280 | } |
| 281 | return 0; |
| 282 | } |
| 283 | |
| 284 | /* |
| 285 | ** Append text to pOut, adding hyperlink markup for [...]. |
| 286 | */ |
| 287 | static void appendLinked(Blob *pOut, const char *z, int n){ |
| 288 | int i = 0; |
| 289 | int j; |
| 290 | while( i<n ){ |
| 291 | if( z[i]=='[' && (j = help_is_link(z+i, n-i))>0 ){ |
| 292 | if( i ) blob_append(pOut, z, i); |
| 293 | z += i+2; |
| 294 | n -= i+2; |
| 295 | blob_appendf(pOut, "<a href='%R/help?cmd=%.*s'>%.*s</a>", |
| 296 | j-3, z, j-3, z); |
| 297 | z += j-1; |
| 298 | n -= j-1; |
| 299 | i = 0; |
| 300 | }else{ |
| 301 | i++; |
| 302 | } |
| 303 | } |
| 304 | blob_append(pOut, z, i); |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** Attempt to reformat plain-text help into HTML for display on a webpage. |
| 309 | ** |
| 310 | ** The HTML output is appended to Blob pHtml, which should already be |
| @@ -305,17 +343,22 @@ | |
| 343 | azEnd[0] = ""; |
| 344 | while( zHelp[0] ){ |
| 345 | i = 0; |
| 346 | while( (c = zHelp[i])!=0 |
| 347 | && c!='\n' |
| 348 | && c!='<' |
| 349 | && (c!='%' || strncmp(zHelp+i,"%fossil",7)!=0) |
| 350 | ){ i++; } |
| 351 | if( c=='%' ){ |
| 352 | if( i ) blob_appendf(pHtml, "%#h", i, zHelp); |
| 353 | zHelp += i + 1; |
| 354 | wantBR = 1; |
| 355 | continue; |
| 356 | }else if( c=='<' ){ |
| 357 | if( i ) blob_appendf(pHtml, "%#h", i, zHelp); |
| 358 | blob_append(pHtml, "&", 5); |
| 359 | zHelp += i + 1; |
| 360 | continue; |
| 361 | } |
| 362 | if( i>2 && zHelp[0]=='>' && zHelp[1]==' ' ){ |
| 363 | isDT = 1; |
| 364 | for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){} |
| @@ -394,11 +437,12 @@ | |
| 437 | }else if( wantBR ){ |
| 438 | appendMixedFont(pHtml, zHelp+nIndent, i-nIndent); |
| 439 | blob_append(pHtml, "<br>\n", 5); |
| 440 | wantBR = 0; |
| 441 | }else{ |
| 442 | appendLinked(pHtml, zHelp+nIndent, i-nIndent); |
| 443 | blob_append_char(pHtml, '\n'); |
| 444 | } |
| 445 | zHelp += i+1; |
| 446 | i = 0; |
| 447 | if( c==0 ) break; |
| 448 | } |
| @@ -409,11 +453,11 @@ | |
| 453 | |
| 454 | /* |
| 455 | ** Format help text for TTY display. |
| 456 | */ |
| 457 | static void help_to_text(const char *zHelp, Blob *pText){ |
| 458 | int i, x; |
| 459 | char c; |
| 460 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 461 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 462 | if( i>0 ) blob_append(pText, zHelp, i); |
| 463 | blob_append(pText, "fossil", 6); |
| @@ -426,10 +470,18 @@ | |
| 470 | blob_append(pText, " ", 1); |
| 471 | zHelp += i+2; |
| 472 | i = -1; |
| 473 | continue; |
| 474 | } |
| 475 | if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){ |
| 476 | if( i>0 ) blob_append(pText, zHelp, i); |
| 477 | zHelp += i+2; |
| 478 | blob_append(pText, zHelp, x-3); |
| 479 | zHelp += x-1; |
| 480 | i = -1; |
| 481 | continue; |
| 482 | } |
| 483 | } |
| 484 | if( i>0 ){ |
| 485 | blob_append(pText, zHelp, i); |
| 486 | } |
| 487 | } |
| @@ -447,15 +499,17 @@ | |
| 499 | ** -e|--everything Show all commands and pages. |
| 500 | ** -t|--test Include test- commands |
| 501 | ** -w|--www Show WWW pages. |
| 502 | ** -s|--settings Show settings. |
| 503 | ** -h|--html Transform output to HTML. |
| 504 | ** -r|--raw No output formatting. |
| 505 | */ |
| 506 | void test_all_help_cmd(void){ |
| 507 | int i; |
| 508 | int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER; |
| 509 | int useHtml = find_option("html","h",0)!=0; |
| 510 | int rawOut = find_option("raw","r",0)!=0; |
| 511 | |
| 512 | if( find_option("www","w",0) ){ |
| 513 | mask = CMDFLAG_WEBPAGE; |
| 514 | } |
| 515 | if( find_option("everything","e",0) ){ |
| @@ -488,10 +542,13 @@ | |
| 542 | blob_init(&html, 0, 0); |
| 543 | help_to_html(aCommand[i].zHelp, &html); |
| 544 | fossil_print("<h1>%h</h1>\n", aCommand[i].zName); |
| 545 | fossil_print("%s\n<hr>\n", blob_str(&html)); |
| 546 | blob_reset(&html); |
| 547 | }else if( rawOut ){ |
| 548 | fossil_print("# %s\n", aCommand[i].zName); |
| 549 | fossil_print("%s\n\n", aCommand[i].zHelp); |
| 550 | }else{ |
| 551 | Blob txt; |
| 552 | blob_init(&txt, 0, 0); |
| 553 | help_to_text(aCommand[i].zHelp, &txt); |
| 554 | fossil_print("# %s\n", aCommand[i].zName); |
| @@ -665,11 +722,11 @@ | |
| 722 | const CmdOrPage *pCmd = 0; |
| 723 | |
| 724 | style_header("Help: %s", zCmd); |
| 725 | |
| 726 | style_submenu_element("Command-List", "%s/help", g.zTop); |
| 727 | rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 728 | if( *zCmd=='/' ){ |
| 729 | /* Some of the webpages require query parameters in order to work. |
| 730 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 731 | @ <h1>The "%h(zCmd)" page:</h1> |
| 732 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| @@ -913,11 +970,11 @@ | |
| 970 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 971 | ** topics is returned. |
| 972 | ** |
| 973 | ** The following options can be used when TOPIC is omitted: |
| 974 | ** |
| 975 | ** -a|--all List both common and auxiliary commands |
| 976 | ** -o|--options List command-line options common to all commands |
| 977 | ** -s|--setting List setting names |
| 978 | ** -t|--test List unsupported "test" commands |
| 979 | ** -x|--aux List only auxiliary commands |
| 980 | ** -w|--www List all web pages |
| @@ -936,12 +993,13 @@ | |
| 993 | Blob txt; |
| 994 | if( g.argc<3 ){ |
| 995 | z = g.argv[0]; |
| 996 | fossil_print( |
| 997 | "Usage: %s help TOPIC\n" |
| 998 | "Try \"%s help help\" or \"%s help -a\" for more options\n" |
| 999 | "Frequently used commands:\n", |
| 1000 | z, z, z); |
| 1001 | command_list(0, CMDFLAG_1ST_TIER); |
| 1002 | version_cmd(); |
| 1003 | return; |
| 1004 | } |
| 1005 | if( find_option("options","o",0) ){ |
| @@ -1076,11 +1134,11 @@ | |
| 1134 | ){ |
| 1135 | helptextVtab_vtab *pNew; |
| 1136 | int rc; |
| 1137 | |
| 1138 | rc = sqlite3_declare_vtab(db, |
| 1139 | "CREATE TABLE x(name,type,flags,helptext,formatted,html)" |
| 1140 | ); |
| 1141 | if( rc==SQLITE_OK ){ |
| 1142 | pNew = sqlite3_malloc( sizeof(*pNew) ); |
| 1143 | *ppVtab = (sqlite3_vtab*)pNew; |
| 1144 | if( pNew==0 ) return SQLITE_NOMEM; |
| @@ -1160,10 +1218,24 @@ | |
| 1218 | sqlite3_result_int(ctx, pPage->eCmdFlags); |
| 1219 | break; |
| 1220 | case 3: /* helptext */ |
| 1221 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1222 | break; |
| 1223 | case 4: { /* formatted */ |
| 1224 | Blob txt; |
| 1225 | blob_init(&txt, 0, 0); |
| 1226 | help_to_text(pPage->zHelp, &txt); |
| 1227 | sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); |
| 1228 | break; |
| 1229 | } |
| 1230 | case 5: { /* formatted */ |
| 1231 | Blob txt; |
| 1232 | blob_init(&txt, 0, 0); |
| 1233 | help_to_html(pPage->zHelp, &txt); |
| 1234 | sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); |
| 1235 | break; |
| 1236 | } |
| 1237 | } |
| 1238 | return SQLITE_OK; |
| 1239 | } |
| 1240 | |
| 1241 | /* |
| 1242 |
+16
-11
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -147,11 +147,11 @@ | ||
| 147 | 147 | { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, |
| 148 | 148 | { "jar", 3, "application/java-archive" }, |
| 149 | 149 | { "jpe", 3, "image/jpeg" }, |
| 150 | 150 | { "jpeg", 4, "image/jpeg" }, |
| 151 | 151 | { "jpg", 3, "image/jpeg" }, |
| 152 | - { "js", 2, "application/x-javascript" }, | |
| 152 | + { "js", 2, "application/javascript" }, | |
| 153 | 153 | { "kar", 3, "audio/midi" }, |
| 154 | 154 | { "latex", 5, "application/x-latex" }, |
| 155 | 155 | { "lha", 3, "application/octet-stream" }, |
| 156 | 156 | { "lsp", 3, "application/x-lisp" }, |
| 157 | 157 | { "lzh", 3, "application/octet-stream" }, |
| @@ -1132,29 +1132,34 @@ | ||
| 1132 | 1132 | |
| 1133 | 1133 | |
| 1134 | 1134 | /* |
| 1135 | 1135 | ** WEBPAGE: favicon.ico |
| 1136 | 1136 | ** |
| 1137 | -** Return the default favicon.ico image. The returned image is for the | |
| 1138 | -** Fossil lizard icon. | |
| 1137 | +** Return the configured "favicon.ico" image. If no "favicon.ico" image | |
| 1138 | +** is defined, the returned image is for the Fossil lizard icon. | |
| 1139 | 1139 | ** |
| 1140 | -** The intended use case here is to supply a favicon for the "fossil ui" | |
| 1140 | +** The intended use case here is to supply an icon for the "fossil ui" | |
| 1141 | 1141 | ** command. For a permanent website, the recommended process is for |
| 1142 | -** the admin to set up a project-specific favicon and reference that | |
| 1143 | -** icon in the HTML header using a line like: | |
| 1142 | +** the admin to set up a project-specific icon and reference that icon | |
| 1143 | +** in the HTML header using a line like: | |
| 1144 | 1144 | ** |
| 1145 | 1145 | ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> |
| 1146 | 1146 | ** |
| 1147 | 1147 | */ |
| 1148 | 1148 | void favicon_page(void){ |
| 1149 | - Blob favicon; | |
| 1149 | + Blob icon; | |
| 1150 | + char *zMime; | |
| 1150 | 1151 | |
| 1151 | 1152 | etag_check(ETAG_CONFIG, 0); |
| 1152 | - blob_zero(&favicon); | |
| 1153 | - blob_init(&favicon, (char*)aLogo, sizeof(aLogo)); | |
| 1154 | - cgi_set_content_type("image/gif"); | |
| 1155 | - cgi_set_content(&favicon); | |
| 1153 | + zMime = db_get("icon-mimetype", "image/gif"); | |
| 1154 | + blob_zero(&icon); | |
| 1155 | + db_blob(&icon, "SELECT value FROM config WHERE name='icon-image'"); | |
| 1156 | + if( blob_size(&icon)==0 ){ | |
| 1157 | + blob_init(&icon, (char*)aLogo, sizeof(aLogo)); | |
| 1158 | + } | |
| 1159 | + cgi_set_content_type(zMime); | |
| 1160 | + cgi_set_content(&icon); | |
| 1156 | 1161 | } |
| 1157 | 1162 | |
| 1158 | 1163 | /* |
| 1159 | 1164 | ** WEBPAGE: docsrch |
| 1160 | 1165 | ** |
| 1161 | 1166 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -147,11 +147,11 @@ | |
| 147 | { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, |
| 148 | { "jar", 3, "application/java-archive" }, |
| 149 | { "jpe", 3, "image/jpeg" }, |
| 150 | { "jpeg", 4, "image/jpeg" }, |
| 151 | { "jpg", 3, "image/jpeg" }, |
| 152 | { "js", 2, "application/x-javascript" }, |
| 153 | { "kar", 3, "audio/midi" }, |
| 154 | { "latex", 5, "application/x-latex" }, |
| 155 | { "lha", 3, "application/octet-stream" }, |
| 156 | { "lsp", 3, "application/x-lisp" }, |
| 157 | { "lzh", 3, "application/octet-stream" }, |
| @@ -1132,29 +1132,34 @@ | |
| 1132 | |
| 1133 | |
| 1134 | /* |
| 1135 | ** WEBPAGE: favicon.ico |
| 1136 | ** |
| 1137 | ** Return the default favicon.ico image. The returned image is for the |
| 1138 | ** Fossil lizard icon. |
| 1139 | ** |
| 1140 | ** The intended use case here is to supply a favicon for the "fossil ui" |
| 1141 | ** command. For a permanent website, the recommended process is for |
| 1142 | ** the admin to set up a project-specific favicon and reference that |
| 1143 | ** icon in the HTML header using a line like: |
| 1144 | ** |
| 1145 | ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> |
| 1146 | ** |
| 1147 | */ |
| 1148 | void favicon_page(void){ |
| 1149 | Blob favicon; |
| 1150 | |
| 1151 | etag_check(ETAG_CONFIG, 0); |
| 1152 | blob_zero(&favicon); |
| 1153 | blob_init(&favicon, (char*)aLogo, sizeof(aLogo)); |
| 1154 | cgi_set_content_type("image/gif"); |
| 1155 | cgi_set_content(&favicon); |
| 1156 | } |
| 1157 | |
| 1158 | /* |
| 1159 | ** WEBPAGE: docsrch |
| 1160 | ** |
| 1161 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -147,11 +147,11 @@ | |
| 147 | { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, |
| 148 | { "jar", 3, "application/java-archive" }, |
| 149 | { "jpe", 3, "image/jpeg" }, |
| 150 | { "jpeg", 4, "image/jpeg" }, |
| 151 | { "jpg", 3, "image/jpeg" }, |
| 152 | { "js", 2, "application/javascript" }, |
| 153 | { "kar", 3, "audio/midi" }, |
| 154 | { "latex", 5, "application/x-latex" }, |
| 155 | { "lha", 3, "application/octet-stream" }, |
| 156 | { "lsp", 3, "application/x-lisp" }, |
| 157 | { "lzh", 3, "application/octet-stream" }, |
| @@ -1132,29 +1132,34 @@ | |
| 1132 | |
| 1133 | |
| 1134 | /* |
| 1135 | ** WEBPAGE: favicon.ico |
| 1136 | ** |
| 1137 | ** Return the configured "favicon.ico" image. If no "favicon.ico" image |
| 1138 | ** is defined, the returned image is for the Fossil lizard icon. |
| 1139 | ** |
| 1140 | ** The intended use case here is to supply an icon for the "fossil ui" |
| 1141 | ** command. For a permanent website, the recommended process is for |
| 1142 | ** the admin to set up a project-specific icon and reference that icon |
| 1143 | ** in the HTML header using a line like: |
| 1144 | ** |
| 1145 | ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> |
| 1146 | ** |
| 1147 | */ |
| 1148 | void favicon_page(void){ |
| 1149 | Blob icon; |
| 1150 | char *zMime; |
| 1151 | |
| 1152 | etag_check(ETAG_CONFIG, 0); |
| 1153 | zMime = db_get("icon-mimetype", "image/gif"); |
| 1154 | blob_zero(&icon); |
| 1155 | db_blob(&icon, "SELECT value FROM config WHERE name='icon-image'"); |
| 1156 | if( blob_size(&icon)==0 ){ |
| 1157 | blob_init(&icon, (char*)aLogo, sizeof(aLogo)); |
| 1158 | } |
| 1159 | cgi_set_content_type(zMime); |
| 1160 | cgi_set_content(&icon); |
| 1161 | } |
| 1162 | |
| 1163 | /* |
| 1164 | ** WEBPAGE: docsrch |
| 1165 | ** |
| 1166 |
+4
-1
| --- src/etag.c | ||
| +++ src/etag.c | ||
| @@ -97,11 +97,14 @@ | ||
| 97 | 97 | const char *zIfNoneMatch; |
| 98 | 98 | char zBuf[50]; |
| 99 | 99 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 100 | 100 | |
| 101 | 101 | if( etagCancelled ) return; |
| 102 | - iMaxAge = 86400; | |
| 102 | + | |
| 103 | + /* By default, ETagged URLs never expire since the ETag will change | |
| 104 | + * when the content changes. Approximate this policy as 10 years. */ | |
| 105 | + iMaxAge = 10 * 365 * 24 * 60 * 60; | |
| 103 | 106 | md5sum_init(); |
| 104 | 107 | |
| 105 | 108 | /* Always include the executable ID as part of the hash */ |
| 106 | 109 | md5sum_step_text("exe-id: ", -1); |
| 107 | 110 | md5sum_step_text(fossil_exe_id(), -1); |
| 108 | 111 |
| --- src/etag.c | |
| +++ src/etag.c | |
| @@ -97,11 +97,14 @@ | |
| 97 | const char *zIfNoneMatch; |
| 98 | char zBuf[50]; |
| 99 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 100 | |
| 101 | if( etagCancelled ) return; |
| 102 | iMaxAge = 86400; |
| 103 | md5sum_init(); |
| 104 | |
| 105 | /* Always include the executable ID as part of the hash */ |
| 106 | md5sum_step_text("exe-id: ", -1); |
| 107 | md5sum_step_text(fossil_exe_id(), -1); |
| 108 |
| --- src/etag.c | |
| +++ src/etag.c | |
| @@ -97,11 +97,14 @@ | |
| 97 | const char *zIfNoneMatch; |
| 98 | char zBuf[50]; |
| 99 | assert( zETag[0]==0 ); /* Only call this routine once! */ |
| 100 | |
| 101 | if( etagCancelled ) return; |
| 102 | |
| 103 | /* By default, ETagged URLs never expire since the ETag will change |
| 104 | * when the content changes. Approximate this policy as 10 years. */ |
| 105 | iMaxAge = 10 * 365 * 24 * 60 * 60; |
| 106 | md5sum_init(); |
| 107 | |
| 108 | /* Always include the executable ID as part of the hash */ |
| 109 | md5sum_step_text("exe-id: ", -1); |
| 110 | md5sum_step_text(fossil_exe_id(), -1); |
| 111 |
+1
-1
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -1642,11 +1642,11 @@ | ||
| 1642 | 1642 | k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile"); |
| 1643 | 1643 | fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); |
| 1644 | 1644 | } |
| 1645 | 1645 | |
| 1646 | 1646 | /* |
| 1647 | -** COMMAND: git | |
| 1647 | +** COMMAND: git* | |
| 1648 | 1648 | ** |
| 1649 | 1649 | ** Usage: %fossil git SUBCOMMAND |
| 1650 | 1650 | ** |
| 1651 | 1651 | ** Do incremental import or export operations between Fossil and Git. |
| 1652 | 1652 | ** Subcommands: |
| 1653 | 1653 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1642,11 +1642,11 @@ | |
| 1642 | k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile"); |
| 1643 | fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); |
| 1644 | } |
| 1645 | |
| 1646 | /* |
| 1647 | ** COMMAND: git |
| 1648 | ** |
| 1649 | ** Usage: %fossil git SUBCOMMAND |
| 1650 | ** |
| 1651 | ** Do incremental import or export operations between Fossil and Git. |
| 1652 | ** Subcommands: |
| 1653 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1642,11 +1642,11 @@ | |
| 1642 | k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile"); |
| 1643 | fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); |
| 1644 | } |
| 1645 | |
| 1646 | /* |
| 1647 | ** COMMAND: git* |
| 1648 | ** |
| 1649 | ** Usage: %fossil git SUBCOMMAND |
| 1650 | ** |
| 1651 | ** Do incremental import or export operations between Fossil and Git. |
| 1652 | ** Subcommands: |
| 1653 |
+26
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1105,10 +1105,12 @@ | ||
| 1105 | 1105 | ** Remove redundant / characters |
| 1106 | 1106 | ** Remove all /./ path elements. |
| 1107 | 1107 | ** Convert /A/../ to just / |
| 1108 | 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | 1109 | ** is retained. |
| 1110 | +** | |
| 1111 | +** See also: file_canonical_name_dup() | |
| 1110 | 1112 | */ |
| 1111 | 1113 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1112 | 1114 | blob_zero(pOut); |
| 1113 | 1115 | if( file_is_absolute_path(zOrigName) ){ |
| 1114 | 1116 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | ||
| 1140 | 1142 | } |
| 1141 | 1143 | #endif |
| 1142 | 1144 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1143 | 1145 | blob_size(pOut), slash)); |
| 1144 | 1146 | } |
| 1147 | + | |
| 1148 | +/* | |
| 1149 | +** Compute the canonical name of a file. Store that name in | |
| 1150 | +** memory obtained from fossil_malloc() and return a pointer to the | |
| 1151 | +** name. | |
| 1152 | +** | |
| 1153 | +** See also: file_canonical_name() | |
| 1154 | +*/ | |
| 1155 | +char *file_canonical_name_dup(const char *zOrigName){ | |
| 1156 | + Blob x; | |
| 1157 | + if( zOrigName==0 ) return 0; | |
| 1158 | + blob_init(&x, 0, 0); | |
| 1159 | + file_canonical_name(zOrigName, &x, 0); | |
| 1160 | + return blob_str(&x); | |
| 1161 | +} | |
| 1145 | 1162 | |
| 1146 | 1163 | /* |
| 1147 | 1164 | ** The input is the name of an executable, such as one might |
| 1148 | 1165 | ** type on a command-line. This routine resolves that name into |
| 1149 | 1166 | ** a full pathname. The result is obtained from fossil_malloc() |
| @@ -2380,5 +2397,14 @@ | ||
| 2380 | 2397 | changeCount); |
| 2381 | 2398 | }else{ |
| 2382 | 2399 | fossil_print("Touched %d file(s)\n", changeCount); |
| 2383 | 2400 | } |
| 2384 | 2401 | } |
| 2402 | + | |
| 2403 | +/* | |
| 2404 | +** If zFileName is not NULL and contains a '.', this returns a pointer | |
| 2405 | +** to the position after the final '.', else it returns NULL. | |
| 2406 | +*/ | |
| 2407 | +const char * file_extension(const char *zFileName){ | |
| 2408 | + const char * zExt = strrchr(zFileName, '.'); | |
| 2409 | + return zExt ? &zExt[1] : 0; | |
| 2410 | +} | |
| 2385 | 2411 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1105,10 +1105,12 @@ | |
| 1105 | ** Remove redundant / characters |
| 1106 | ** Remove all /./ path elements. |
| 1107 | ** Convert /A/../ to just / |
| 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | ** is retained. |
| 1110 | */ |
| 1111 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1112 | blob_zero(pOut); |
| 1113 | if( file_is_absolute_path(zOrigName) ){ |
| 1114 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | |
| 1140 | } |
| 1141 | #endif |
| 1142 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1143 | blob_size(pOut), slash)); |
| 1144 | } |
| 1145 | |
| 1146 | /* |
| 1147 | ** The input is the name of an executable, such as one might |
| 1148 | ** type on a command-line. This routine resolves that name into |
| 1149 | ** a full pathname. The result is obtained from fossil_malloc() |
| @@ -2380,5 +2397,14 @@ | |
| 2380 | changeCount); |
| 2381 | }else{ |
| 2382 | fossil_print("Touched %d file(s)\n", changeCount); |
| 2383 | } |
| 2384 | } |
| 2385 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1105,10 +1105,12 @@ | |
| 1105 | ** Remove redundant / characters |
| 1106 | ** Remove all /./ path elements. |
| 1107 | ** Convert /A/../ to just / |
| 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | ** is retained. |
| 1110 | ** |
| 1111 | ** See also: file_canonical_name_dup() |
| 1112 | */ |
| 1113 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1114 | blob_zero(pOut); |
| 1115 | if( file_is_absolute_path(zOrigName) ){ |
| 1116 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | |
| 1142 | } |
| 1143 | #endif |
| 1144 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1145 | blob_size(pOut), slash)); |
| 1146 | } |
| 1147 | |
| 1148 | /* |
| 1149 | ** Compute the canonical name of a file. Store that name in |
| 1150 | ** memory obtained from fossil_malloc() and return a pointer to the |
| 1151 | ** name. |
| 1152 | ** |
| 1153 | ** See also: file_canonical_name() |
| 1154 | */ |
| 1155 | char *file_canonical_name_dup(const char *zOrigName){ |
| 1156 | Blob x; |
| 1157 | if( zOrigName==0 ) return 0; |
| 1158 | blob_init(&x, 0, 0); |
| 1159 | file_canonical_name(zOrigName, &x, 0); |
| 1160 | return blob_str(&x); |
| 1161 | } |
| 1162 | |
| 1163 | /* |
| 1164 | ** The input is the name of an executable, such as one might |
| 1165 | ** type on a command-line. This routine resolves that name into |
| 1166 | ** a full pathname. The result is obtained from fossil_malloc() |
| @@ -2380,5 +2397,14 @@ | |
| 2397 | changeCount); |
| 2398 | }else{ |
| 2399 | fossil_print("Touched %d file(s)\n", changeCount); |
| 2400 | } |
| 2401 | } |
| 2402 | |
| 2403 | /* |
| 2404 | ** If zFileName is not NULL and contains a '.', this returns a pointer |
| 2405 | ** to the position after the final '.', else it returns NULL. |
| 2406 | */ |
| 2407 | const char * file_extension(const char *zFileName){ |
| 2408 | const char * zExt = strrchr(zFileName, '.'); |
| 2409 | return zExt ? &zExt[1] : 0; |
| 2410 | } |
| 2411 |
+221
-144
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -364,14 +364,10 @@ | ||
| 364 | 364 | blob_appendf(pOut, "B %s\n", |
| 365 | 365 | pCI->pParent->zBaseline |
| 366 | 366 | ? pCI->pParent->zBaseline |
| 367 | 367 | : pCI->zParentUuid); |
| 368 | 368 | } |
| 369 | - blob_reserve(pOut, 1024 * | |
| 370 | - (asDelta ? 2 : pCI->pParent->nFile/11+1 | |
| 371 | - /* In the fossil core repo, each 12-ish F-cards (on | |
| 372 | - ** average) take up roughly 1kb */)); | |
| 373 | 369 | if(blob_size(&pCI->comment)!=0){ |
| 374 | 370 | blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment)); |
| 375 | 371 | }else{ |
| 376 | 372 | blob_append(pOut, "C (no\\scomment)\n", 16); |
| 377 | 373 | } |
| @@ -965,11 +961,12 @@ | ||
| 965 | 961 | ** populates: |
| 966 | 962 | ** |
| 967 | 963 | ** - *zRevUuid = the fully-expanded value of zRev (owned by the |
| 968 | 964 | ** caller). zRevUuid may be NULL. |
| 969 | 965 | ** |
| 970 | -** - *vid = the RID of zRevUuid. May not be NULL. | |
| 966 | +** - *pVid = the RID of zRevUuid. pVid May be NULL. If the vid | |
| 967 | +** cannot be resolved or is ambiguous, pVid is not assigned. | |
| 971 | 968 | ** |
| 972 | 969 | ** - *frid = the RID of zFilename's blob content. May not be NULL |
| 973 | 970 | ** unless zFilename is also NULL. If BOTH of zFilename and frid are |
| 974 | 971 | ** NULL then no confirmation is done on the filename argument - only |
| 975 | 972 | ** zRev is checked. |
| @@ -979,38 +976,41 @@ | ||
| 979 | 976 | ** returns false, it queues up an error response and the caller must |
| 980 | 977 | ** return immediately. |
| 981 | 978 | */ |
| 982 | 979 | static int fileedit_ajax_setup_filerev(const char * zRev, |
| 983 | 980 | char ** zRevUuid, |
| 984 | - int * vid, | |
| 981 | + int * pVid, | |
| 985 | 982 | const char * zFilename, |
| 986 | 983 | int * frid){ |
| 987 | 984 | char * zFileUuid = 0; /* file content UUID */ |
| 988 | 985 | const int checkFile = zFilename!=0 || frid!=0; |
| 986 | + int vid = 0; | |
| 989 | 987 | |
| 990 | 988 | if(checkFile && !fileedit_ajax_check_filename(zFilename)){ |
| 991 | 989 | return 0; |
| 992 | 990 | } |
| 993 | - *vid = symbolic_name_to_rid(zRev, "ci"); | |
| 994 | - if(0==*vid){ | |
| 991 | + vid = symbolic_name_to_rid(zRev, "ci"); | |
| 992 | + if(0==vid){ | |
| 995 | 993 | ajax_route_error(404,"Cannot resolve name as a checkin: %s", |
| 996 | 994 | zRev); |
| 997 | 995 | return 0; |
| 998 | - }else if(*vid<0){ | |
| 996 | + }else if(vid<0){ | |
| 999 | 997 | ajax_route_error(400,"Checkin name is ambiguous: %s", |
| 1000 | 998 | zRev); |
| 1001 | 999 | return 0; |
| 1000 | + }else if(pVid!=0){ | |
| 1001 | + *pVid = vid; | |
| 1002 | 1002 | } |
| 1003 | 1003 | if(checkFile){ |
| 1004 | - zFileUuid = fileedit_file_uuid(zFilename, *vid, 0); | |
| 1004 | + zFileUuid = fileedit_file_uuid(zFilename, vid, 0); | |
| 1005 | 1005 | if(zFileUuid==0){ |
| 1006 | 1006 | ajax_route_error(404, "Checkin does not contain file."); |
| 1007 | 1007 | return 0; |
| 1008 | 1008 | } |
| 1009 | 1009 | } |
| 1010 | 1010 | if(zRevUuid!=0){ |
| 1011 | - *zRevUuid = rid_to_uuid(*vid); | |
| 1011 | + *zRevUuid = rid_to_uuid(vid); | |
| 1012 | 1012 | } |
| 1013 | 1013 | if(checkFile){ |
| 1014 | 1014 | assert(zFileUuid!=0); |
| 1015 | 1015 | if(frid!=0){ |
| 1016 | 1016 | *frid = fast_uuid_to_rid(zFileUuid); |
| @@ -1290,10 +1290,92 @@ | ||
| 1290 | 1290 | end_fail: |
| 1291 | 1291 | #undef fail |
| 1292 | 1292 | fossil_free(zFileUuid); |
| 1293 | 1293 | return rc ? rc : 500; |
| 1294 | 1294 | } |
| 1295 | + | |
| 1296 | +/* | |
| 1297 | +** Renders a list of all open leaves in JSON form: | |
| 1298 | +** | |
| 1299 | +** [ | |
| 1300 | +** {checkin: UUID, branch: branchName, timestamp: string} | |
| 1301 | +** ] | |
| 1302 | +** | |
| 1303 | +** The entries are ordered newest first. | |
| 1304 | +** | |
| 1305 | +** If zFirstUuid is not NULL then *zFirstUuid is set to a copy of the | |
| 1306 | +** full UUID of the first (most recent) leaf, which must be freed by | |
| 1307 | +** the caller. It is set to 0 if there are no leaves. | |
| 1308 | +*/ | |
| 1309 | +static void fileedit_render_leaves_list(char ** zFirstUuid){ | |
| 1310 | + Blob sql = empty_blob; | |
| 1311 | + Stmt q = empty_Stmt; | |
| 1312 | + int i = 0; | |
| 1313 | + | |
| 1314 | + if(zFirstUuid){ | |
| 1315 | + *zFirstUuid = 0; | |
| 1316 | + } | |
| 1317 | + blob_append(&sql, timeline_query_for_tty(), -1); | |
| 1318 | + blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " | |
| 1319 | + "WHERE NOT EXISTS(" | |
| 1320 | + "SELECT 1 from tagxref WHERE tagid=%d AND " | |
| 1321 | + "tagtype>0 AND rid=leaf.rid" | |
| 1322 | + ")) " | |
| 1323 | + "ORDER BY mtime DESC", TAG_CLOSED); | |
| 1324 | + db_prepare_blob(&q, &sql); | |
| 1325 | + CX("["); | |
| 1326 | + while( SQLITE_ROW==db_step(&q) ){ | |
| 1327 | + const char * zUuid = db_column_text(&q, 1); | |
| 1328 | + if(i++){ | |
| 1329 | + CX(","); | |
| 1330 | + }else if(zFirstUuid){ | |
| 1331 | + *zFirstUuid = fossil_strdup(zUuid); | |
| 1332 | + } | |
| 1333 | + CX("{"); | |
| 1334 | + CX("\"checkin\":%!j,", zUuid); | |
| 1335 | + CX("\"branch\":%!j,", db_column_text(&q, 7)); | |
| 1336 | + CX("\"timestamp\":%!j", db_column_text(&q, 2)); | |
| 1337 | + CX("}"); | |
| 1338 | + } | |
| 1339 | + CX("]"); | |
| 1340 | + db_finalize(&q); | |
| 1341 | +} | |
| 1342 | + | |
| 1343 | +/* | |
| 1344 | +** For the given fully resolved UUID, renders a JSON object containing | |
| 1345 | +** the fileeedit-editable files in that checkin: | |
| 1346 | +** | |
| 1347 | +** { | |
| 1348 | +** checkin: UUID, | |
| 1349 | +** editableFiles: [ filename1, ... filenameN ] | |
| 1350 | +** } | |
| 1351 | +** | |
| 1352 | +** They are sorted by name using filename_collation(). | |
| 1353 | +*/ | |
| 1354 | +static void fileedit_render_checkin_files(const char * zFullUuid){ | |
| 1355 | + Blob sql = empty_blob; | |
| 1356 | + Stmt q = empty_Stmt; | |
| 1357 | + int i = 0; | |
| 1358 | + | |
| 1359 | + CX("{\"checkin\":%!j," | |
| 1360 | + "\"editableFiles\":[", zFullUuid); | |
| 1361 | + blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " | |
| 1362 | + "ORDER BY filename %s", | |
| 1363 | + zFullUuid, filename_collation()); | |
| 1364 | + db_prepare_blob(&q, &sql); | |
| 1365 | + while( SQLITE_ROW==db_step(&q) ){ | |
| 1366 | + const char * zFilename = db_column_text(&q, 0); | |
| 1367 | + if(fileedit_is_editable(zFilename)){ | |
| 1368 | + if(i++){ | |
| 1369 | + CX(","); | |
| 1370 | + } | |
| 1371 | + CX("%!j", zFilename); | |
| 1372 | + } | |
| 1373 | + } | |
| 1374 | + db_finalize(&q); | |
| 1375 | + CX("]}"); | |
| 1376 | +} | |
| 1295 | 1377 | |
| 1296 | 1378 | /* |
| 1297 | 1379 | ** AJAX route /fileedit?ajax=filelist |
| 1298 | 1380 | ** |
| 1299 | 1381 | ** Fetches a JSON-format list of leaves and/or filenames for use in |
| @@ -1318,66 +1400,27 @@ | ||
| 1318 | 1400 | ** } |
| 1319 | 1401 | ** |
| 1320 | 1402 | ** On error it produces a JSON response as documented for |
| 1321 | 1403 | ** ajax_route_error(). |
| 1322 | 1404 | */ |
| 1323 | -static void fileedit_ajax_filelist(void){ | |
| 1405 | +static void fileedit_ajax_filelist(){ | |
| 1324 | 1406 | const char * zCi = PD("checkin",P("ci")); |
| 1325 | - Blob sql = empty_blob; | |
| 1326 | - Stmt q = empty_Stmt; | |
| 1327 | - int i = 0; | |
| 1328 | 1407 | |
| 1329 | 1408 | if(!ajax_route_bootstrap(1,0)){ |
| 1330 | 1409 | return; |
| 1331 | 1410 | } |
| 1332 | 1411 | cgi_set_content_type("application/json"); |
| 1333 | 1412 | if(zCi!=0){ |
| 1334 | 1413 | char * zCiFull = 0; |
| 1335 | - int vid = 0; | |
| 1336 | - if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, &vid, 0, 0)){ | |
| 1414 | + if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, 0, 0, 0)){ | |
| 1337 | 1415 | /* Error already reported */ |
| 1338 | 1416 | return; |
| 1339 | 1417 | } |
| 1340 | - CX("{\"checkin\":%!j," | |
| 1341 | - "\"editableFiles\":[", zCiFull); | |
| 1342 | - blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " | |
| 1343 | - "ORDER BY filename %s", | |
| 1344 | - zCiFull, filename_collation()); | |
| 1345 | - db_prepare_blob(&q, &sql); | |
| 1346 | - while( SQLITE_ROW==db_step(&q) ){ | |
| 1347 | - const char * zFilename = db_column_text(&q, 0); | |
| 1348 | - if(fileedit_is_editable(zFilename)){ | |
| 1349 | - if(i++){ | |
| 1350 | - CX(","); | |
| 1351 | - } | |
| 1352 | - CX("%!j", zFilename); | |
| 1353 | - } | |
| 1354 | - } | |
| 1355 | - db_finalize(&q); | |
| 1356 | - CX("]}"); | |
| 1418 | + fileedit_render_checkin_files(zCiFull); | |
| 1419 | + fossil_free(zCiFull); | |
| 1357 | 1420 | }else if(P("leaves")!=0){ |
| 1358 | - blob_append(&sql, timeline_query_for_tty(), -1); | |
| 1359 | - blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " | |
| 1360 | - "WHERE NOT EXISTS(" | |
| 1361 | - "SELECT 1 from tagxref WHERE tagid=%d AND " | |
| 1362 | - "tagtype>0 AND rid=leaf.rid" | |
| 1363 | - ")) " | |
| 1364 | - "ORDER BY mtime DESC", TAG_CLOSED); | |
| 1365 | - db_prepare_blob(&q, &sql); | |
| 1366 | - CX("["); | |
| 1367 | - while( SQLITE_ROW==db_step(&q) ){ | |
| 1368 | - if(i++){ | |
| 1369 | - CX(","); | |
| 1370 | - } | |
| 1371 | - CX("{"); | |
| 1372 | - CX("\"checkin\":%!j,", db_column_text(&q, 1)); | |
| 1373 | - CX("\"branch\":%!j,", db_column_text(&q, 7)); | |
| 1374 | - CX("\"timestamp\":%!j", db_column_text(&q, 2)); | |
| 1375 | - CX("}"); | |
| 1376 | - } | |
| 1377 | - CX("]"); | |
| 1378 | - db_finalize(&q); | |
| 1421 | + fileedit_render_leaves_list(0); | |
| 1379 | 1422 | }else{ |
| 1380 | 1423 | ajax_route_error(500, "Unhandled URL argument."); |
| 1381 | 1424 | } |
| 1382 | 1425 | } |
| 1383 | 1426 | |
| @@ -1484,52 +1527,60 @@ | ||
| 1484 | 1527 | } |
| 1485 | 1528 | |
| 1486 | 1529 | /* |
| 1487 | 1530 | ** WEBPAGE: fileedit |
| 1488 | 1531 | ** |
| 1489 | -** Enables the online editing and committing of individual text files. | |
| 1490 | -** Requires that the user have Write permissions. | |
| 1532 | +** Enables the online editing and committing of text files. Requires | |
| 1533 | +** that the user have Write permissions and that a user with setup | |
| 1534 | +** permissions has set the fileedit-glob setting to a list of glob | |
| 1535 | +** patterns matching files which may be edited (e.g. "*.wiki,*.md"). | |
| 1536 | +** Note that fileedit-glob, by design, is a local-only setting. | |
| 1537 | +** It does not sync across repository clones, and must be explicitly | |
| 1538 | +** set on any repositories where this page should be activated. | |
| 1491 | 1539 | ** |
| 1492 | 1540 | ** Optional query parameters: |
| 1493 | 1541 | ** |
| 1494 | 1542 | ** filename=FILENAME Repo-relative path to the file. |
| 1495 | 1543 | ** checkin=VERSION Checkin version, using any unambiguous |
| 1496 | -** supported symbolic version name. | |
| 1497 | -** | |
| 1498 | -** Internal-use parameters: | |
| 1499 | -** | |
| 1500 | -** name=string The name of a page-specific AJAX operation. | |
| 1501 | -** | |
| 1502 | -** Noting that fossil internally stores all URL path components after | |
| 1503 | -** the first as the "name" value. Thus /fileedit?name=blah is | |
| 1504 | -** equivalent to /fileedit/blah. The latter is the preferred | |
| 1505 | -** form. This means, however, that no fileedit ajax routes may make | |
| 1506 | -** use of the name parameter. | |
| 1507 | -** | |
| 1508 | -** Which additional parameters are used by each distinct ajax value is | |
| 1509 | -** an internal implementation detail and may change with any given | |
| 1510 | -** build of this code. An unknown "name" value triggers an error, as | |
| 1511 | -** documented for ajax_route_error(). | |
| 1544 | +** symbolic version name. | |
| 1545 | +** | |
| 1546 | +** If passed a filename but no checkin then it will attempt to | |
| 1547 | +** load that file from the most recent leaf checkin. | |
| 1548 | +** | |
| 1549 | +** Once the page is loaded, files may be selected from any open leaf | |
| 1550 | +** version. The only way to edit files from non-leaf checkins is to | |
| 1551 | +** pass both the filename and checkin as URL parameters to the page. | |
| 1552 | +** Users with the proper permissions will be presented with "Edit" | |
| 1553 | +** links in various file-specific contexts for files which match the | |
| 1554 | +** fileedit-glob, regardless of whether they refer to leaf versions or | |
| 1555 | +** not. | |
| 1512 | 1556 | */ |
| 1513 | 1557 | void fileedit_page(void){ |
| 1514 | - const char * zFilename = 0; /* filename. We'll accept 'name' | |
| 1515 | - because that param is handled | |
| 1516 | - specially by the core. */ | |
| 1517 | - const char * zRev = 0; /* checkin version */ | |
| 1518 | 1558 | const char * zFileMime = 0; /* File mime type guess */ |
| 1519 | 1559 | CheckinMiniInfo cimi; /* Checkin state */ |
| 1520 | 1560 | int previewRenderMode = AJAX_RENDER_GUESS; /* preview mode */ |
| 1521 | 1561 | Blob err = empty_blob; /* Error report */ |
| 1522 | - Blob endScript = empty_blob; /* Script code to run at the | |
| 1523 | - end. This content will be | |
| 1524 | - combined into a single JS | |
| 1525 | - function call, thus each | |
| 1526 | - entry must end with a | |
| 1527 | - semicolon. */ | |
| 1528 | 1562 | const char *zAjax = P("name"); /* Name of AJAX route for |
| 1529 | 1563 | sub-dispatching. */ |
| 1530 | 1564 | |
| 1565 | + /* | |
| 1566 | + ** Internal-use URL parameters: | |
| 1567 | + ** | |
| 1568 | + ** name=string The name of a page-specific AJAX operation. | |
| 1569 | + ** | |
| 1570 | + ** Noting that fossil internally stores all URL path components | |
| 1571 | + ** after the first as the "name" value. Thus /fileedit?name=blah is | |
| 1572 | + ** equivalent to /fileedit/blah. The latter is the preferred | |
| 1573 | + ** form. This means, however, that no fileedit ajax routes may make | |
| 1574 | + ** use of the name parameter. | |
| 1575 | + ** | |
| 1576 | + ** Which additional parameters are used by each distinct ajax route | |
| 1577 | + ** is an internal implementation detail and may change with any | |
| 1578 | + ** given build of this code. An unknown "name" value triggers an | |
| 1579 | + ** error, as documented for ajax_route_error(). | |
| 1580 | + */ | |
| 1581 | + | |
| 1531 | 1582 | /* Allow no access to this page without check-in privilege */ |
| 1532 | 1583 | login_check_credentials(); |
| 1533 | 1584 | if( !g.perm.Write ){ |
| 1534 | 1585 | if(zAjax!=0){ |
| 1535 | 1586 | ajax_route_error(403, "Write permissions required."); |
| @@ -1590,14 +1641,11 @@ | ||
| 1590 | 1641 | ** transaction cleanly. |
| 1591 | 1642 | */ |
| 1592 | 1643 | { |
| 1593 | 1644 | int isMissingArg = 0; |
| 1594 | 1645 | if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ |
| 1595 | - zFilename = cimi.zFilename; | |
| 1596 | - zRev = cimi.zParentUuid; | |
| 1597 | - assert(zRev); | |
| 1598 | - assert(zFilename); | |
| 1646 | + assert(cimi.zFilename); | |
| 1599 | 1647 | zFileMime = mimetype_from_name(cimi.zFilename); |
| 1600 | 1648 | }else if(isMissingArg!=0){ |
| 1601 | 1649 | /* Squelch these startup warnings - they're non-fatal now but |
| 1602 | 1650 | ** used to be fatal. */ |
| 1603 | 1651 | blob_reset(&err); |
| @@ -1626,34 +1674,39 @@ | ||
| 1626 | 1674 | /* Status bar */ |
| 1627 | 1675 | CX("<div id='fossil-status-bar' " |
| 1628 | 1676 | "title='Status message area. Double-click to clear them.'>" |
| 1629 | 1677 | "Status messages will go here.</div>\n" |
| 1630 | 1678 | /* will be moved into the tab container via JS */); |
| 1679 | + | |
| 1680 | + CX("<div id='fileedit-edit-status'>" | |
| 1681 | + "<span class='name'>(no file loaded)</span>" | |
| 1682 | + "<span class='links'></span>" | |
| 1683 | + "</div>"); | |
| 1631 | 1684 | |
| 1632 | 1685 | /* Main tab container... */ |
| 1633 | 1686 | CX("<div id='fileedit-tabs' class='tab-container'></div>"); |
| 1687 | + | |
| 1688 | + /* The .hidden class on the following tab elements is to help lessen | |
| 1689 | + the FOUC effect of the tabs before JS re-assembles them. */ | |
| 1634 | 1690 | |
| 1635 | 1691 | /***** File/version info tab *****/ |
| 1636 | 1692 | { |
| 1637 | 1693 | CX("<div id='fileedit-tab-fileselect' " |
| 1638 | 1694 | "data-tab-parent='fileedit-tabs' " |
| 1639 | - "data-tab-label='File Info & Selection'" | |
| 1695 | + "data-tab-label='File Selection' " | |
| 1696 | + "class='hidden'" | |
| 1640 | 1697 | ">"); |
| 1641 | - CX("<fieldset id='file-version-details'>" | |
| 1642 | - "<legend>File/Version</legend>" | |
| 1643 | - "<div>No file loaded.</div>" | |
| 1644 | - "</fieldset>"); | |
| 1645 | - CX("<h1>Select a file to edit:</h1>"); | |
| 1646 | 1698 | CX("<div id='fileedit-file-selector'></div>"); |
| 1647 | 1699 | CX("</div>"/*#fileedit-tab-fileselect*/); |
| 1648 | 1700 | } |
| 1649 | 1701 | |
| 1650 | 1702 | /******* Content tab *******/ |
| 1651 | 1703 | { |
| 1652 | 1704 | CX("<div id='fileedit-tab-content' " |
| 1653 | 1705 | "data-tab-parent='fileedit-tabs' " |
| 1654 | - "data-tab-label='File Content'" | |
| 1706 | + "data-tab-label='File Content' " | |
| 1707 | + "class='hidden'" | |
| 1655 | 1708 | ">"); |
| 1656 | 1709 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1657 | 1710 | CX("<button class='fileedit-content-reload confirmer' " |
| 1658 | 1711 | "title='Reload the file from the server, discarding " |
| 1659 | 1712 | "any local edits. To help avoid accidental loss of " |
| @@ -1668,22 +1721,22 @@ | ||
| 1668 | 1721 | "150%", 150, "175%", 175, |
| 1669 | 1722 | "200%", 200, NULL); |
| 1670 | 1723 | CX("</div>"); |
| 1671 | 1724 | CX("<div class='flex-container flex-column stretch'>"); |
| 1672 | 1725 | CX("<textarea name='content' id='fileedit-content-editor' " |
| 1673 | - "class='fileedit' " | |
| 1674 | - "rows='20' cols='80'>"); | |
| 1726 | + "class='fileedit' rows='25'>"); | |
| 1675 | 1727 | CX("</textarea>"); |
| 1676 | 1728 | CX("</div>"/*textarea wrapper*/); |
| 1677 | 1729 | CX("</div>"/*#tab-file-content*/); |
| 1678 | 1730 | } |
| 1679 | 1731 | |
| 1680 | 1732 | /****** Preview tab ******/ |
| 1681 | 1733 | { |
| 1682 | 1734 | CX("<div id='fileedit-tab-preview' " |
| 1683 | 1735 | "data-tab-parent='fileedit-tabs' " |
| 1684 | - "data-tab-label='Preview'" | |
| 1736 | + "data-tab-label='Preview' " | |
| 1737 | + "class='hidden'" | |
| 1685 | 1738 | ">"); |
| 1686 | 1739 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1687 | 1740 | CX("<button id='btn-preview-refresh' " |
| 1688 | 1741 | "data-f-preview-from='fileContent' " |
| 1689 | 1742 | /* ^^^ fossil.page[methodName]() OR text source elem ID, |
| @@ -1741,14 +1794,16 @@ | ||
| 1741 | 1794 | |
| 1742 | 1795 | /****** Diff tab ******/ |
| 1743 | 1796 | { |
| 1744 | 1797 | CX("<div id='fileedit-tab-diff' " |
| 1745 | 1798 | "data-tab-parent='fileedit-tabs' " |
| 1746 | - "data-tab-label='Diff'" | |
| 1799 | + "data-tab-label='Diff' " | |
| 1800 | + "class='hidden'" | |
| 1747 | 1801 | ">"); |
| 1748 | 1802 | |
| 1749 | - CX("<div class='fileedit-options flex-container flex-row' " | |
| 1803 | + CX("<div class='fileedit-options flex-container " | |
| 1804 | + "flex-row child-gap-small' " | |
| 1750 | 1805 | "id='fileedit-tab-diff-buttons'>"); |
| 1751 | 1806 | CX("<button class='sbs'>Side-by-side</button>" |
| 1752 | 1807 | "<button class='unified'>Unified</button>"); |
| 1753 | 1808 | if(0){ |
| 1754 | 1809 | /* For the time being let's just ignore all whitespace |
| @@ -1773,11 +1828,12 @@ | ||
| 1773 | 1828 | } |
| 1774 | 1829 | |
| 1775 | 1830 | /****** Commit ******/ |
| 1776 | 1831 | CX("<div id='fileedit-tab-commit' " |
| 1777 | 1832 | "data-tab-parent='fileedit-tabs' " |
| 1778 | - "data-tab-label='Commit'" | |
| 1833 | + "data-tab-label='Commit' " | |
| 1834 | + "class='hidden'" | |
| 1779 | 1835 | ">"); |
| 1780 | 1836 | { |
| 1781 | 1837 | /******* Commit flags/options *******/ |
| 1782 | 1838 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1783 | 1839 | style_labeled_checkbox("cb-dry-run", |
| @@ -1894,11 +1950,12 @@ | ||
| 1894 | 1950 | CX("</div>"/*#fileedit-tab-commit*/); |
| 1895 | 1951 | |
| 1896 | 1952 | /****** Help/Tips ******/ |
| 1897 | 1953 | CX("<div id='fileedit-tab-help' " |
| 1898 | 1954 | "data-tab-parent='fileedit-tabs' " |
| 1899 | - "data-tab-label='Help'" | |
| 1955 | + "data-tab-label='Help' " | |
| 1956 | + "class='hidden'" | |
| 1900 | 1957 | ">"); |
| 1901 | 1958 | { |
| 1902 | 1959 | CX("<h1>Help & Tips</h1>"); |
| 1903 | 1960 | CX("<ul>"); |
| 1904 | 1961 | CX("<li><strong>Only files matching the <code>fileedit-glob</code> " |
| @@ -1922,61 +1979,81 @@ | ||
| 1922 | 1979 | "file/check-in combination are discarded.</li>"); |
| 1923 | 1980 | CX("</ul>"); |
| 1924 | 1981 | } |
| 1925 | 1982 | CX("</div>"/*#fileedit-tab-help*/); |
| 1926 | 1983 | |
| 1927 | - { | |
| 1928 | - /* Dynamically populate the editor, display any error in the err | |
| 1929 | - ** blob, and/or switch to tab #0, where the file selector | |
| 1930 | - ** lives... */ | |
| 1931 | - blob_appendf(&endScript, | |
| 1932 | - "fossil.onPageLoad("); | |
| 1933 | - if(zRev && zFilename){ | |
| 1934 | - assert(0==blob_size(&err)); | |
| 1935 | - blob_appendf(&endScript, | |
| 1936 | - "()=>fossil.page.loadFile(%!j,%!j)", | |
| 1937 | - zFilename, cimi.zParentUuid); | |
| 1938 | - }else{ | |
| 1939 | - blob_appendf(&endScript,"function(){\n"); | |
| 1940 | - if(blob_size(&err)>0){ | |
| 1941 | - blob_appendf(&endScript, | |
| 1942 | - "fossil.error(%!j);\n", | |
| 1943 | - blob_str(&err)); | |
| 1944 | - } | |
| 1945 | - blob_appendf(&endScript, | |
| 1946 | - "fossil.page.tabs.switchToTab(0);\n"); | |
| 1947 | - blob_appendf(&endScript,"}"); | |
| 1948 | - } | |
| 1949 | - blob_appendf(&endScript,");\n"); | |
| 1950 | - } | |
| 1951 | - | |
| 1952 | - blob_reset(&err); | |
| 1953 | - CheckinMiniInfo_cleanup(&cimi); | |
| 1954 | - style_emit_script_fossil_bootstrap(0); | |
| 1955 | - append_diff_javascript(1); | |
| 1956 | - style_emit_script_fetch(0); | |
| 1957 | - style_emit_script_tabs(0)/*also emits fossil.dom*/; | |
| 1958 | - style_emit_script_confirmer(0); | |
| 1959 | - style_emit_script_builtin(0, "fossil.storage.js"); | |
| 1960 | - | |
| 1984 | + builtin_request_js("sbsdiff.js"); | |
| 1985 | + style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", | |
| 1986 | + "storage", 0); | |
| 1987 | + builtin_fulfill_js_requests(); | |
| 1961 | 1988 | /* |
| 1962 | 1989 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1963 | 1990 | ** used for dynamically toggling certain UI components on and off. |
| 1964 | - ** Must come before fossil.page.fileedit.js. | |
| 1991 | + ** Must come after window.fossil has been intialized and before | |
| 1992 | + ** fossil.page.fileedit.js. Potential TODO: move this into the | |
| 1993 | + ** window.fossil bootstrapping so that we don't have to "fulfill" | |
| 1994 | + ** the JS multiple times. | |
| 1965 | 1995 | */ |
| 1966 | 1996 | ajax_emit_js_preview_modes(1); |
| 1967 | - | |
| 1968 | - style_emit_script_builtin(0, "fossil.page.fileedit.js"); | |
| 1969 | - if(blob_size(&endScript)>0){ | |
| 1997 | + builtin_request_js("fossil.page.fileedit.js"); | |
| 1998 | + builtin_fulfill_js_requests(); | |
| 1999 | + { | |
| 2000 | + /* Dynamically populate the editor, display any error in the err | |
| 2001 | + ** blob, and/or switch to tab #0, where the file selector | |
| 2002 | + ** lives. The extra C scopes here correspond to JS-level scopes, | |
| 2003 | + ** to improve grokability. */ | |
| 1970 | 2004 | style_emit_script_tag(0,0); |
| 1971 | 2005 | CX("\n(function(){\n"); |
| 1972 | - CX("try{\n%b}\n" | |
| 1973 | - "catch(e){" | |
| 2006 | + CX("try{\n"); | |
| 2007 | + { | |
| 2008 | + char * zFirstLeafUuid = 0; | |
| 2009 | + CX("fossil.config['fileedit-glob'] = "); | |
| 2010 | + glob_render_json_to_cgi(fileedit_glob()); | |
| 2011 | + CX(";\n"); | |
| 2012 | + if(blob_size(&err)>0){ | |
| 2013 | + CX("fossil.error(%!j);\n", blob_str(&err)); | |
| 2014 | + } | |
| 2015 | + /* Populate the page with the current leaves and, if available, | |
| 2016 | + the selected checkin's file list, to save 1 or 2 XHR requests | |
| 2017 | + at startup. That makes this page uncacheable, but compressed | |
| 2018 | + delivery of this page is currently less than 6k. */ | |
| 2019 | + CX("fossil.page.initialLeaves = "); | |
| 2020 | + fileedit_render_leaves_list(cimi.zParentUuid ? 0 : &zFirstLeafUuid); | |
| 2021 | + CX(";\n"); | |
| 2022 | + if(zFirstLeafUuid){ | |
| 2023 | + assert(!cimi.zParentUuid); | |
| 2024 | + cimi.zParentUuid = zFirstLeafUuid; | |
| 2025 | + zFirstLeafUuid = 0; | |
| 2026 | + } | |
| 2027 | + if(cimi.zParentUuid){ | |
| 2028 | + CX("fossil.page.initialFiles = "); | |
| 2029 | + fileedit_render_checkin_files(cimi.zParentUuid); | |
| 2030 | + CX(";\n"); | |
| 2031 | + } | |
| 2032 | + CX("fossil.onPageLoad(function(){\n"); | |
| 2033 | + { | |
| 2034 | + if(blob_size(&err)>0){ | |
| 2035 | + CX("fossil.error(%!j);\n", | |
| 2036 | + blob_str(&err)); | |
| 2037 | + CX("fossil.page.tabs.switchToTab(0);\n"); | |
| 2038 | + } | |
| 2039 | + if(cimi.zParentUuid && cimi.zFilename){ | |
| 2040 | + CX("fossil.page.loadFile(%!j,%!j);\n", | |
| 2041 | + cimi.zFilename, cimi.zParentUuid) | |
| 2042 | + /* Reminder we cannot embed the JSON-format | |
| 2043 | + content of the file here because if it contains | |
| 2044 | + a SCRIPT tag then it will break the whole page. */; | |
| 2045 | + } | |
| 2046 | + } | |
| 2047 | + CX("});\n")/*fossil.onPageLoad()*/; | |
| 2048 | + } | |
| 2049 | + CX("}catch(e){" | |
| 1974 | 2050 | "fossil.error(e); console.error('Exception:',e);" |
| 1975 | - "}\n", | |
| 1976 | - &endScript); | |
| 1977 | - CX("})();"); | |
| 2051 | + "}\n"); | |
| 2052 | + CX("})();")/*anonymous function*/; | |
| 1978 | 2053 | style_emit_script_tag(1,0); |
| 1979 | 2054 | } |
| 2055 | + blob_reset(&err); | |
| 2056 | + CheckinMiniInfo_cleanup(&cimi); | |
| 1980 | 2057 | db_end_transaction(0); |
| 1981 | 2058 | style_footer(); |
| 1982 | 2059 | } |
| 1983 | 2060 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -364,14 +364,10 @@ | |
| 364 | blob_appendf(pOut, "B %s\n", |
| 365 | pCI->pParent->zBaseline |
| 366 | ? pCI->pParent->zBaseline |
| 367 | : pCI->zParentUuid); |
| 368 | } |
| 369 | blob_reserve(pOut, 1024 * |
| 370 | (asDelta ? 2 : pCI->pParent->nFile/11+1 |
| 371 | /* In the fossil core repo, each 12-ish F-cards (on |
| 372 | ** average) take up roughly 1kb */)); |
| 373 | if(blob_size(&pCI->comment)!=0){ |
| 374 | blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment)); |
| 375 | }else{ |
| 376 | blob_append(pOut, "C (no\\scomment)\n", 16); |
| 377 | } |
| @@ -965,11 +961,12 @@ | |
| 965 | ** populates: |
| 966 | ** |
| 967 | ** - *zRevUuid = the fully-expanded value of zRev (owned by the |
| 968 | ** caller). zRevUuid may be NULL. |
| 969 | ** |
| 970 | ** - *vid = the RID of zRevUuid. May not be NULL. |
| 971 | ** |
| 972 | ** - *frid = the RID of zFilename's blob content. May not be NULL |
| 973 | ** unless zFilename is also NULL. If BOTH of zFilename and frid are |
| 974 | ** NULL then no confirmation is done on the filename argument - only |
| 975 | ** zRev is checked. |
| @@ -979,38 +976,41 @@ | |
| 979 | ** returns false, it queues up an error response and the caller must |
| 980 | ** return immediately. |
| 981 | */ |
| 982 | static int fileedit_ajax_setup_filerev(const char * zRev, |
| 983 | char ** zRevUuid, |
| 984 | int * vid, |
| 985 | const char * zFilename, |
| 986 | int * frid){ |
| 987 | char * zFileUuid = 0; /* file content UUID */ |
| 988 | const int checkFile = zFilename!=0 || frid!=0; |
| 989 | |
| 990 | if(checkFile && !fileedit_ajax_check_filename(zFilename)){ |
| 991 | return 0; |
| 992 | } |
| 993 | *vid = symbolic_name_to_rid(zRev, "ci"); |
| 994 | if(0==*vid){ |
| 995 | ajax_route_error(404,"Cannot resolve name as a checkin: %s", |
| 996 | zRev); |
| 997 | return 0; |
| 998 | }else if(*vid<0){ |
| 999 | ajax_route_error(400,"Checkin name is ambiguous: %s", |
| 1000 | zRev); |
| 1001 | return 0; |
| 1002 | } |
| 1003 | if(checkFile){ |
| 1004 | zFileUuid = fileedit_file_uuid(zFilename, *vid, 0); |
| 1005 | if(zFileUuid==0){ |
| 1006 | ajax_route_error(404, "Checkin does not contain file."); |
| 1007 | return 0; |
| 1008 | } |
| 1009 | } |
| 1010 | if(zRevUuid!=0){ |
| 1011 | *zRevUuid = rid_to_uuid(*vid); |
| 1012 | } |
| 1013 | if(checkFile){ |
| 1014 | assert(zFileUuid!=0); |
| 1015 | if(frid!=0){ |
| 1016 | *frid = fast_uuid_to_rid(zFileUuid); |
| @@ -1290,10 +1290,92 @@ | |
| 1290 | end_fail: |
| 1291 | #undef fail |
| 1292 | fossil_free(zFileUuid); |
| 1293 | return rc ? rc : 500; |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** AJAX route /fileedit?ajax=filelist |
| 1298 | ** |
| 1299 | ** Fetches a JSON-format list of leaves and/or filenames for use in |
| @@ -1318,66 +1400,27 @@ | |
| 1318 | ** } |
| 1319 | ** |
| 1320 | ** On error it produces a JSON response as documented for |
| 1321 | ** ajax_route_error(). |
| 1322 | */ |
| 1323 | static void fileedit_ajax_filelist(void){ |
| 1324 | const char * zCi = PD("checkin",P("ci")); |
| 1325 | Blob sql = empty_blob; |
| 1326 | Stmt q = empty_Stmt; |
| 1327 | int i = 0; |
| 1328 | |
| 1329 | if(!ajax_route_bootstrap(1,0)){ |
| 1330 | return; |
| 1331 | } |
| 1332 | cgi_set_content_type("application/json"); |
| 1333 | if(zCi!=0){ |
| 1334 | char * zCiFull = 0; |
| 1335 | int vid = 0; |
| 1336 | if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, &vid, 0, 0)){ |
| 1337 | /* Error already reported */ |
| 1338 | return; |
| 1339 | } |
| 1340 | CX("{\"checkin\":%!j," |
| 1341 | "\"editableFiles\":[", zCiFull); |
| 1342 | blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " |
| 1343 | "ORDER BY filename %s", |
| 1344 | zCiFull, filename_collation()); |
| 1345 | db_prepare_blob(&q, &sql); |
| 1346 | while( SQLITE_ROW==db_step(&q) ){ |
| 1347 | const char * zFilename = db_column_text(&q, 0); |
| 1348 | if(fileedit_is_editable(zFilename)){ |
| 1349 | if(i++){ |
| 1350 | CX(","); |
| 1351 | } |
| 1352 | CX("%!j", zFilename); |
| 1353 | } |
| 1354 | } |
| 1355 | db_finalize(&q); |
| 1356 | CX("]}"); |
| 1357 | }else if(P("leaves")!=0){ |
| 1358 | blob_append(&sql, timeline_query_for_tty(), -1); |
| 1359 | blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " |
| 1360 | "WHERE NOT EXISTS(" |
| 1361 | "SELECT 1 from tagxref WHERE tagid=%d AND " |
| 1362 | "tagtype>0 AND rid=leaf.rid" |
| 1363 | ")) " |
| 1364 | "ORDER BY mtime DESC", TAG_CLOSED); |
| 1365 | db_prepare_blob(&q, &sql); |
| 1366 | CX("["); |
| 1367 | while( SQLITE_ROW==db_step(&q) ){ |
| 1368 | if(i++){ |
| 1369 | CX(","); |
| 1370 | } |
| 1371 | CX("{"); |
| 1372 | CX("\"checkin\":%!j,", db_column_text(&q, 1)); |
| 1373 | CX("\"branch\":%!j,", db_column_text(&q, 7)); |
| 1374 | CX("\"timestamp\":%!j", db_column_text(&q, 2)); |
| 1375 | CX("}"); |
| 1376 | } |
| 1377 | CX("]"); |
| 1378 | db_finalize(&q); |
| 1379 | }else{ |
| 1380 | ajax_route_error(500, "Unhandled URL argument."); |
| 1381 | } |
| 1382 | } |
| 1383 | |
| @@ -1484,52 +1527,60 @@ | |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | ** WEBPAGE: fileedit |
| 1488 | ** |
| 1489 | ** Enables the online editing and committing of individual text files. |
| 1490 | ** Requires that the user have Write permissions. |
| 1491 | ** |
| 1492 | ** Optional query parameters: |
| 1493 | ** |
| 1494 | ** filename=FILENAME Repo-relative path to the file. |
| 1495 | ** checkin=VERSION Checkin version, using any unambiguous |
| 1496 | ** supported symbolic version name. |
| 1497 | ** |
| 1498 | ** Internal-use parameters: |
| 1499 | ** |
| 1500 | ** name=string The name of a page-specific AJAX operation. |
| 1501 | ** |
| 1502 | ** Noting that fossil internally stores all URL path components after |
| 1503 | ** the first as the "name" value. Thus /fileedit?name=blah is |
| 1504 | ** equivalent to /fileedit/blah. The latter is the preferred |
| 1505 | ** form. This means, however, that no fileedit ajax routes may make |
| 1506 | ** use of the name parameter. |
| 1507 | ** |
| 1508 | ** Which additional parameters are used by each distinct ajax value is |
| 1509 | ** an internal implementation detail and may change with any given |
| 1510 | ** build of this code. An unknown "name" value triggers an error, as |
| 1511 | ** documented for ajax_route_error(). |
| 1512 | */ |
| 1513 | void fileedit_page(void){ |
| 1514 | const char * zFilename = 0; /* filename. We'll accept 'name' |
| 1515 | because that param is handled |
| 1516 | specially by the core. */ |
| 1517 | const char * zRev = 0; /* checkin version */ |
| 1518 | const char * zFileMime = 0; /* File mime type guess */ |
| 1519 | CheckinMiniInfo cimi; /* Checkin state */ |
| 1520 | int previewRenderMode = AJAX_RENDER_GUESS; /* preview mode */ |
| 1521 | Blob err = empty_blob; /* Error report */ |
| 1522 | Blob endScript = empty_blob; /* Script code to run at the |
| 1523 | end. This content will be |
| 1524 | combined into a single JS |
| 1525 | function call, thus each |
| 1526 | entry must end with a |
| 1527 | semicolon. */ |
| 1528 | const char *zAjax = P("name"); /* Name of AJAX route for |
| 1529 | sub-dispatching. */ |
| 1530 | |
| 1531 | /* Allow no access to this page without check-in privilege */ |
| 1532 | login_check_credentials(); |
| 1533 | if( !g.perm.Write ){ |
| 1534 | if(zAjax!=0){ |
| 1535 | ajax_route_error(403, "Write permissions required."); |
| @@ -1590,14 +1641,11 @@ | |
| 1590 | ** transaction cleanly. |
| 1591 | */ |
| 1592 | { |
| 1593 | int isMissingArg = 0; |
| 1594 | if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ |
| 1595 | zFilename = cimi.zFilename; |
| 1596 | zRev = cimi.zParentUuid; |
| 1597 | assert(zRev); |
| 1598 | assert(zFilename); |
| 1599 | zFileMime = mimetype_from_name(cimi.zFilename); |
| 1600 | }else if(isMissingArg!=0){ |
| 1601 | /* Squelch these startup warnings - they're non-fatal now but |
| 1602 | ** used to be fatal. */ |
| 1603 | blob_reset(&err); |
| @@ -1626,34 +1674,39 @@ | |
| 1626 | /* Status bar */ |
| 1627 | CX("<div id='fossil-status-bar' " |
| 1628 | "title='Status message area. Double-click to clear them.'>" |
| 1629 | "Status messages will go here.</div>\n" |
| 1630 | /* will be moved into the tab container via JS */); |
| 1631 | |
| 1632 | /* Main tab container... */ |
| 1633 | CX("<div id='fileedit-tabs' class='tab-container'></div>"); |
| 1634 | |
| 1635 | /***** File/version info tab *****/ |
| 1636 | { |
| 1637 | CX("<div id='fileedit-tab-fileselect' " |
| 1638 | "data-tab-parent='fileedit-tabs' " |
| 1639 | "data-tab-label='File Info & Selection'" |
| 1640 | ">"); |
| 1641 | CX("<fieldset id='file-version-details'>" |
| 1642 | "<legend>File/Version</legend>" |
| 1643 | "<div>No file loaded.</div>" |
| 1644 | "</fieldset>"); |
| 1645 | CX("<h1>Select a file to edit:</h1>"); |
| 1646 | CX("<div id='fileedit-file-selector'></div>"); |
| 1647 | CX("</div>"/*#fileedit-tab-fileselect*/); |
| 1648 | } |
| 1649 | |
| 1650 | /******* Content tab *******/ |
| 1651 | { |
| 1652 | CX("<div id='fileedit-tab-content' " |
| 1653 | "data-tab-parent='fileedit-tabs' " |
| 1654 | "data-tab-label='File Content'" |
| 1655 | ">"); |
| 1656 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1657 | CX("<button class='fileedit-content-reload confirmer' " |
| 1658 | "title='Reload the file from the server, discarding " |
| 1659 | "any local edits. To help avoid accidental loss of " |
| @@ -1668,22 +1721,22 @@ | |
| 1668 | "150%", 150, "175%", 175, |
| 1669 | "200%", 200, NULL); |
| 1670 | CX("</div>"); |
| 1671 | CX("<div class='flex-container flex-column stretch'>"); |
| 1672 | CX("<textarea name='content' id='fileedit-content-editor' " |
| 1673 | "class='fileedit' " |
| 1674 | "rows='20' cols='80'>"); |
| 1675 | CX("</textarea>"); |
| 1676 | CX("</div>"/*textarea wrapper*/); |
| 1677 | CX("</div>"/*#tab-file-content*/); |
| 1678 | } |
| 1679 | |
| 1680 | /****** Preview tab ******/ |
| 1681 | { |
| 1682 | CX("<div id='fileedit-tab-preview' " |
| 1683 | "data-tab-parent='fileedit-tabs' " |
| 1684 | "data-tab-label='Preview'" |
| 1685 | ">"); |
| 1686 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1687 | CX("<button id='btn-preview-refresh' " |
| 1688 | "data-f-preview-from='fileContent' " |
| 1689 | /* ^^^ fossil.page[methodName]() OR text source elem ID, |
| @@ -1741,14 +1794,16 @@ | |
| 1741 | |
| 1742 | /****** Diff tab ******/ |
| 1743 | { |
| 1744 | CX("<div id='fileedit-tab-diff' " |
| 1745 | "data-tab-parent='fileedit-tabs' " |
| 1746 | "data-tab-label='Diff'" |
| 1747 | ">"); |
| 1748 | |
| 1749 | CX("<div class='fileedit-options flex-container flex-row' " |
| 1750 | "id='fileedit-tab-diff-buttons'>"); |
| 1751 | CX("<button class='sbs'>Side-by-side</button>" |
| 1752 | "<button class='unified'>Unified</button>"); |
| 1753 | if(0){ |
| 1754 | /* For the time being let's just ignore all whitespace |
| @@ -1773,11 +1828,12 @@ | |
| 1773 | } |
| 1774 | |
| 1775 | /****** Commit ******/ |
| 1776 | CX("<div id='fileedit-tab-commit' " |
| 1777 | "data-tab-parent='fileedit-tabs' " |
| 1778 | "data-tab-label='Commit'" |
| 1779 | ">"); |
| 1780 | { |
| 1781 | /******* Commit flags/options *******/ |
| 1782 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1783 | style_labeled_checkbox("cb-dry-run", |
| @@ -1894,11 +1950,12 @@ | |
| 1894 | CX("</div>"/*#fileedit-tab-commit*/); |
| 1895 | |
| 1896 | /****** Help/Tips ******/ |
| 1897 | CX("<div id='fileedit-tab-help' " |
| 1898 | "data-tab-parent='fileedit-tabs' " |
| 1899 | "data-tab-label='Help'" |
| 1900 | ">"); |
| 1901 | { |
| 1902 | CX("<h1>Help & Tips</h1>"); |
| 1903 | CX("<ul>"); |
| 1904 | CX("<li><strong>Only files matching the <code>fileedit-glob</code> " |
| @@ -1922,61 +1979,81 @@ | |
| 1922 | "file/check-in combination are discarded.</li>"); |
| 1923 | CX("</ul>"); |
| 1924 | } |
| 1925 | CX("</div>"/*#fileedit-tab-help*/); |
| 1926 | |
| 1927 | { |
| 1928 | /* Dynamically populate the editor, display any error in the err |
| 1929 | ** blob, and/or switch to tab #0, where the file selector |
| 1930 | ** lives... */ |
| 1931 | blob_appendf(&endScript, |
| 1932 | "fossil.onPageLoad("); |
| 1933 | if(zRev && zFilename){ |
| 1934 | assert(0==blob_size(&err)); |
| 1935 | blob_appendf(&endScript, |
| 1936 | "()=>fossil.page.loadFile(%!j,%!j)", |
| 1937 | zFilename, cimi.zParentUuid); |
| 1938 | }else{ |
| 1939 | blob_appendf(&endScript,"function(){\n"); |
| 1940 | if(blob_size(&err)>0){ |
| 1941 | blob_appendf(&endScript, |
| 1942 | "fossil.error(%!j);\n", |
| 1943 | blob_str(&err)); |
| 1944 | } |
| 1945 | blob_appendf(&endScript, |
| 1946 | "fossil.page.tabs.switchToTab(0);\n"); |
| 1947 | blob_appendf(&endScript,"}"); |
| 1948 | } |
| 1949 | blob_appendf(&endScript,");\n"); |
| 1950 | } |
| 1951 | |
| 1952 | blob_reset(&err); |
| 1953 | CheckinMiniInfo_cleanup(&cimi); |
| 1954 | style_emit_script_fossil_bootstrap(0); |
| 1955 | append_diff_javascript(1); |
| 1956 | style_emit_script_fetch(0); |
| 1957 | style_emit_script_tabs(0)/*also emits fossil.dom*/; |
| 1958 | style_emit_script_confirmer(0); |
| 1959 | style_emit_script_builtin(0, "fossil.storage.js"); |
| 1960 | |
| 1961 | /* |
| 1962 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1963 | ** used for dynamically toggling certain UI components on and off. |
| 1964 | ** Must come before fossil.page.fileedit.js. |
| 1965 | */ |
| 1966 | ajax_emit_js_preview_modes(1); |
| 1967 | |
| 1968 | style_emit_script_builtin(0, "fossil.page.fileedit.js"); |
| 1969 | if(blob_size(&endScript)>0){ |
| 1970 | style_emit_script_tag(0,0); |
| 1971 | CX("\n(function(){\n"); |
| 1972 | CX("try{\n%b}\n" |
| 1973 | "catch(e){" |
| 1974 | "fossil.error(e); console.error('Exception:',e);" |
| 1975 | "}\n", |
| 1976 | &endScript); |
| 1977 | CX("})();"); |
| 1978 | style_emit_script_tag(1,0); |
| 1979 | } |
| 1980 | db_end_transaction(0); |
| 1981 | style_footer(); |
| 1982 | } |
| 1983 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -364,14 +364,10 @@ | |
| 364 | blob_appendf(pOut, "B %s\n", |
| 365 | pCI->pParent->zBaseline |
| 366 | ? pCI->pParent->zBaseline |
| 367 | : pCI->zParentUuid); |
| 368 | } |
| 369 | if(blob_size(&pCI->comment)!=0){ |
| 370 | blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment)); |
| 371 | }else{ |
| 372 | blob_append(pOut, "C (no\\scomment)\n", 16); |
| 373 | } |
| @@ -965,11 +961,12 @@ | |
| 961 | ** populates: |
| 962 | ** |
| 963 | ** - *zRevUuid = the fully-expanded value of zRev (owned by the |
| 964 | ** caller). zRevUuid may be NULL. |
| 965 | ** |
| 966 | ** - *pVid = the RID of zRevUuid. pVid May be NULL. If the vid |
| 967 | ** cannot be resolved or is ambiguous, pVid is not assigned. |
| 968 | ** |
| 969 | ** - *frid = the RID of zFilename's blob content. May not be NULL |
| 970 | ** unless zFilename is also NULL. If BOTH of zFilename and frid are |
| 971 | ** NULL then no confirmation is done on the filename argument - only |
| 972 | ** zRev is checked. |
| @@ -979,38 +976,41 @@ | |
| 976 | ** returns false, it queues up an error response and the caller must |
| 977 | ** return immediately. |
| 978 | */ |
| 979 | static int fileedit_ajax_setup_filerev(const char * zRev, |
| 980 | char ** zRevUuid, |
| 981 | int * pVid, |
| 982 | const char * zFilename, |
| 983 | int * frid){ |
| 984 | char * zFileUuid = 0; /* file content UUID */ |
| 985 | const int checkFile = zFilename!=0 || frid!=0; |
| 986 | int vid = 0; |
| 987 | |
| 988 | if(checkFile && !fileedit_ajax_check_filename(zFilename)){ |
| 989 | return 0; |
| 990 | } |
| 991 | vid = symbolic_name_to_rid(zRev, "ci"); |
| 992 | if(0==vid){ |
| 993 | ajax_route_error(404,"Cannot resolve name as a checkin: %s", |
| 994 | zRev); |
| 995 | return 0; |
| 996 | }else if(vid<0){ |
| 997 | ajax_route_error(400,"Checkin name is ambiguous: %s", |
| 998 | zRev); |
| 999 | return 0; |
| 1000 | }else if(pVid!=0){ |
| 1001 | *pVid = vid; |
| 1002 | } |
| 1003 | if(checkFile){ |
| 1004 | zFileUuid = fileedit_file_uuid(zFilename, vid, 0); |
| 1005 | if(zFileUuid==0){ |
| 1006 | ajax_route_error(404, "Checkin does not contain file."); |
| 1007 | return 0; |
| 1008 | } |
| 1009 | } |
| 1010 | if(zRevUuid!=0){ |
| 1011 | *zRevUuid = rid_to_uuid(vid); |
| 1012 | } |
| 1013 | if(checkFile){ |
| 1014 | assert(zFileUuid!=0); |
| 1015 | if(frid!=0){ |
| 1016 | *frid = fast_uuid_to_rid(zFileUuid); |
| @@ -1290,10 +1290,92 @@ | |
| 1290 | end_fail: |
| 1291 | #undef fail |
| 1292 | fossil_free(zFileUuid); |
| 1293 | return rc ? rc : 500; |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** Renders a list of all open leaves in JSON form: |
| 1298 | ** |
| 1299 | ** [ |
| 1300 | ** {checkin: UUID, branch: branchName, timestamp: string} |
| 1301 | ** ] |
| 1302 | ** |
| 1303 | ** The entries are ordered newest first. |
| 1304 | ** |
| 1305 | ** If zFirstUuid is not NULL then *zFirstUuid is set to a copy of the |
| 1306 | ** full UUID of the first (most recent) leaf, which must be freed by |
| 1307 | ** the caller. It is set to 0 if there are no leaves. |
| 1308 | */ |
| 1309 | static void fileedit_render_leaves_list(char ** zFirstUuid){ |
| 1310 | Blob sql = empty_blob; |
| 1311 | Stmt q = empty_Stmt; |
| 1312 | int i = 0; |
| 1313 | |
| 1314 | if(zFirstUuid){ |
| 1315 | *zFirstUuid = 0; |
| 1316 | } |
| 1317 | blob_append(&sql, timeline_query_for_tty(), -1); |
| 1318 | blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf " |
| 1319 | "WHERE NOT EXISTS(" |
| 1320 | "SELECT 1 from tagxref WHERE tagid=%d AND " |
| 1321 | "tagtype>0 AND rid=leaf.rid" |
| 1322 | ")) " |
| 1323 | "ORDER BY mtime DESC", TAG_CLOSED); |
| 1324 | db_prepare_blob(&q, &sql); |
| 1325 | CX("["); |
| 1326 | while( SQLITE_ROW==db_step(&q) ){ |
| 1327 | const char * zUuid = db_column_text(&q, 1); |
| 1328 | if(i++){ |
| 1329 | CX(","); |
| 1330 | }else if(zFirstUuid){ |
| 1331 | *zFirstUuid = fossil_strdup(zUuid); |
| 1332 | } |
| 1333 | CX("{"); |
| 1334 | CX("\"checkin\":%!j,", zUuid); |
| 1335 | CX("\"branch\":%!j,", db_column_text(&q, 7)); |
| 1336 | CX("\"timestamp\":%!j", db_column_text(&q, 2)); |
| 1337 | CX("}"); |
| 1338 | } |
| 1339 | CX("]"); |
| 1340 | db_finalize(&q); |
| 1341 | } |
| 1342 | |
| 1343 | /* |
| 1344 | ** For the given fully resolved UUID, renders a JSON object containing |
| 1345 | ** the fileeedit-editable files in that checkin: |
| 1346 | ** |
| 1347 | ** { |
| 1348 | ** checkin: UUID, |
| 1349 | ** editableFiles: [ filename1, ... filenameN ] |
| 1350 | ** } |
| 1351 | ** |
| 1352 | ** They are sorted by name using filename_collation(). |
| 1353 | */ |
| 1354 | static void fileedit_render_checkin_files(const char * zFullUuid){ |
| 1355 | Blob sql = empty_blob; |
| 1356 | Stmt q = empty_Stmt; |
| 1357 | int i = 0; |
| 1358 | |
| 1359 | CX("{\"checkin\":%!j," |
| 1360 | "\"editableFiles\":[", zFullUuid); |
| 1361 | blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) " |
| 1362 | "ORDER BY filename %s", |
| 1363 | zFullUuid, filename_collation()); |
| 1364 | db_prepare_blob(&q, &sql); |
| 1365 | while( SQLITE_ROW==db_step(&q) ){ |
| 1366 | const char * zFilename = db_column_text(&q, 0); |
| 1367 | if(fileedit_is_editable(zFilename)){ |
| 1368 | if(i++){ |
| 1369 | CX(","); |
| 1370 | } |
| 1371 | CX("%!j", zFilename); |
| 1372 | } |
| 1373 | } |
| 1374 | db_finalize(&q); |
| 1375 | CX("]}"); |
| 1376 | } |
| 1377 | |
| 1378 | /* |
| 1379 | ** AJAX route /fileedit?ajax=filelist |
| 1380 | ** |
| 1381 | ** Fetches a JSON-format list of leaves and/or filenames for use in |
| @@ -1318,66 +1400,27 @@ | |
| 1400 | ** } |
| 1401 | ** |
| 1402 | ** On error it produces a JSON response as documented for |
| 1403 | ** ajax_route_error(). |
| 1404 | */ |
| 1405 | static void fileedit_ajax_filelist(){ |
| 1406 | const char * zCi = PD("checkin",P("ci")); |
| 1407 | |
| 1408 | if(!ajax_route_bootstrap(1,0)){ |
| 1409 | return; |
| 1410 | } |
| 1411 | cgi_set_content_type("application/json"); |
| 1412 | if(zCi!=0){ |
| 1413 | char * zCiFull = 0; |
| 1414 | if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, 0, 0, 0)){ |
| 1415 | /* Error already reported */ |
| 1416 | return; |
| 1417 | } |
| 1418 | fileedit_render_checkin_files(zCiFull); |
| 1419 | fossil_free(zCiFull); |
| 1420 | }else if(P("leaves")!=0){ |
| 1421 | fileedit_render_leaves_list(0); |
| 1422 | }else{ |
| 1423 | ajax_route_error(500, "Unhandled URL argument."); |
| 1424 | } |
| 1425 | } |
| 1426 | |
| @@ -1484,52 +1527,60 @@ | |
| 1527 | } |
| 1528 | |
| 1529 | /* |
| 1530 | ** WEBPAGE: fileedit |
| 1531 | ** |
| 1532 | ** Enables the online editing and committing of text files. Requires |
| 1533 | ** that the user have Write permissions and that a user with setup |
| 1534 | ** permissions has set the fileedit-glob setting to a list of glob |
| 1535 | ** patterns matching files which may be edited (e.g. "*.wiki,*.md"). |
| 1536 | ** Note that fileedit-glob, by design, is a local-only setting. |
| 1537 | ** It does not sync across repository clones, and must be explicitly |
| 1538 | ** set on any repositories where this page should be activated. |
| 1539 | ** |
| 1540 | ** Optional query parameters: |
| 1541 | ** |
| 1542 | ** filename=FILENAME Repo-relative path to the file. |
| 1543 | ** checkin=VERSION Checkin version, using any unambiguous |
| 1544 | ** symbolic version name. |
| 1545 | ** |
| 1546 | ** If passed a filename but no checkin then it will attempt to |
| 1547 | ** load that file from the most recent leaf checkin. |
| 1548 | ** |
| 1549 | ** Once the page is loaded, files may be selected from any open leaf |
| 1550 | ** version. The only way to edit files from non-leaf checkins is to |
| 1551 | ** pass both the filename and checkin as URL parameters to the page. |
| 1552 | ** Users with the proper permissions will be presented with "Edit" |
| 1553 | ** links in various file-specific contexts for files which match the |
| 1554 | ** fileedit-glob, regardless of whether they refer to leaf versions or |
| 1555 | ** not. |
| 1556 | */ |
| 1557 | void fileedit_page(void){ |
| 1558 | const char * zFileMime = 0; /* File mime type guess */ |
| 1559 | CheckinMiniInfo cimi; /* Checkin state */ |
| 1560 | int previewRenderMode = AJAX_RENDER_GUESS; /* preview mode */ |
| 1561 | Blob err = empty_blob; /* Error report */ |
| 1562 | const char *zAjax = P("name"); /* Name of AJAX route for |
| 1563 | sub-dispatching. */ |
| 1564 | |
| 1565 | /* |
| 1566 | ** Internal-use URL parameters: |
| 1567 | ** |
| 1568 | ** name=string The name of a page-specific AJAX operation. |
| 1569 | ** |
| 1570 | ** Noting that fossil internally stores all URL path components |
| 1571 | ** after the first as the "name" value. Thus /fileedit?name=blah is |
| 1572 | ** equivalent to /fileedit/blah. The latter is the preferred |
| 1573 | ** form. This means, however, that no fileedit ajax routes may make |
| 1574 | ** use of the name parameter. |
| 1575 | ** |
| 1576 | ** Which additional parameters are used by each distinct ajax route |
| 1577 | ** is an internal implementation detail and may change with any |
| 1578 | ** given build of this code. An unknown "name" value triggers an |
| 1579 | ** error, as documented for ajax_route_error(). |
| 1580 | */ |
| 1581 | |
| 1582 | /* Allow no access to this page without check-in privilege */ |
| 1583 | login_check_credentials(); |
| 1584 | if( !g.perm.Write ){ |
| 1585 | if(zAjax!=0){ |
| 1586 | ajax_route_error(403, "Write permissions required."); |
| @@ -1590,14 +1641,11 @@ | |
| 1641 | ** transaction cleanly. |
| 1642 | */ |
| 1643 | { |
| 1644 | int isMissingArg = 0; |
| 1645 | if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){ |
| 1646 | assert(cimi.zFilename); |
| 1647 | zFileMime = mimetype_from_name(cimi.zFilename); |
| 1648 | }else if(isMissingArg!=0){ |
| 1649 | /* Squelch these startup warnings - they're non-fatal now but |
| 1650 | ** used to be fatal. */ |
| 1651 | blob_reset(&err); |
| @@ -1626,34 +1674,39 @@ | |
| 1674 | /* Status bar */ |
| 1675 | CX("<div id='fossil-status-bar' " |
| 1676 | "title='Status message area. Double-click to clear them.'>" |
| 1677 | "Status messages will go here.</div>\n" |
| 1678 | /* will be moved into the tab container via JS */); |
| 1679 | |
| 1680 | CX("<div id='fileedit-edit-status'>" |
| 1681 | "<span class='name'>(no file loaded)</span>" |
| 1682 | "<span class='links'></span>" |
| 1683 | "</div>"); |
| 1684 | |
| 1685 | /* Main tab container... */ |
| 1686 | CX("<div id='fileedit-tabs' class='tab-container'></div>"); |
| 1687 | |
| 1688 | /* The .hidden class on the following tab elements is to help lessen |
| 1689 | the FOUC effect of the tabs before JS re-assembles them. */ |
| 1690 | |
| 1691 | /***** File/version info tab *****/ |
| 1692 | { |
| 1693 | CX("<div id='fileedit-tab-fileselect' " |
| 1694 | "data-tab-parent='fileedit-tabs' " |
| 1695 | "data-tab-label='File Selection' " |
| 1696 | "class='hidden'" |
| 1697 | ">"); |
| 1698 | CX("<div id='fileedit-file-selector'></div>"); |
| 1699 | CX("</div>"/*#fileedit-tab-fileselect*/); |
| 1700 | } |
| 1701 | |
| 1702 | /******* Content tab *******/ |
| 1703 | { |
| 1704 | CX("<div id='fileedit-tab-content' " |
| 1705 | "data-tab-parent='fileedit-tabs' " |
| 1706 | "data-tab-label='File Content' " |
| 1707 | "class='hidden'" |
| 1708 | ">"); |
| 1709 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1710 | CX("<button class='fileedit-content-reload confirmer' " |
| 1711 | "title='Reload the file from the server, discarding " |
| 1712 | "any local edits. To help avoid accidental loss of " |
| @@ -1668,22 +1721,22 @@ | |
| 1721 | "150%", 150, "175%", 175, |
| 1722 | "200%", 200, NULL); |
| 1723 | CX("</div>"); |
| 1724 | CX("<div class='flex-container flex-column stretch'>"); |
| 1725 | CX("<textarea name='content' id='fileedit-content-editor' " |
| 1726 | "class='fileedit' rows='25'>"); |
| 1727 | CX("</textarea>"); |
| 1728 | CX("</div>"/*textarea wrapper*/); |
| 1729 | CX("</div>"/*#tab-file-content*/); |
| 1730 | } |
| 1731 | |
| 1732 | /****** Preview tab ******/ |
| 1733 | { |
| 1734 | CX("<div id='fileedit-tab-preview' " |
| 1735 | "data-tab-parent='fileedit-tabs' " |
| 1736 | "data-tab-label='Preview' " |
| 1737 | "class='hidden'" |
| 1738 | ">"); |
| 1739 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1740 | CX("<button id='btn-preview-refresh' " |
| 1741 | "data-f-preview-from='fileContent' " |
| 1742 | /* ^^^ fossil.page[methodName]() OR text source elem ID, |
| @@ -1741,14 +1794,16 @@ | |
| 1794 | |
| 1795 | /****** Diff tab ******/ |
| 1796 | { |
| 1797 | CX("<div id='fileedit-tab-diff' " |
| 1798 | "data-tab-parent='fileedit-tabs' " |
| 1799 | "data-tab-label='Diff' " |
| 1800 | "class='hidden'" |
| 1801 | ">"); |
| 1802 | |
| 1803 | CX("<div class='fileedit-options flex-container " |
| 1804 | "flex-row child-gap-small' " |
| 1805 | "id='fileedit-tab-diff-buttons'>"); |
| 1806 | CX("<button class='sbs'>Side-by-side</button>" |
| 1807 | "<button class='unified'>Unified</button>"); |
| 1808 | if(0){ |
| 1809 | /* For the time being let's just ignore all whitespace |
| @@ -1773,11 +1828,12 @@ | |
| 1828 | } |
| 1829 | |
| 1830 | /****** Commit ******/ |
| 1831 | CX("<div id='fileedit-tab-commit' " |
| 1832 | "data-tab-parent='fileedit-tabs' " |
| 1833 | "data-tab-label='Commit' " |
| 1834 | "class='hidden'" |
| 1835 | ">"); |
| 1836 | { |
| 1837 | /******* Commit flags/options *******/ |
| 1838 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1839 | style_labeled_checkbox("cb-dry-run", |
| @@ -1894,11 +1950,12 @@ | |
| 1950 | CX("</div>"/*#fileedit-tab-commit*/); |
| 1951 | |
| 1952 | /****** Help/Tips ******/ |
| 1953 | CX("<div id='fileedit-tab-help' " |
| 1954 | "data-tab-parent='fileedit-tabs' " |
| 1955 | "data-tab-label='Help' " |
| 1956 | "class='hidden'" |
| 1957 | ">"); |
| 1958 | { |
| 1959 | CX("<h1>Help & Tips</h1>"); |
| 1960 | CX("<ul>"); |
| 1961 | CX("<li><strong>Only files matching the <code>fileedit-glob</code> " |
| @@ -1922,61 +1979,81 @@ | |
| 1979 | "file/check-in combination are discarded.</li>"); |
| 1980 | CX("</ul>"); |
| 1981 | } |
| 1982 | CX("</div>"/*#fileedit-tab-help*/); |
| 1983 | |
| 1984 | builtin_request_js("sbsdiff.js"); |
| 1985 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1986 | "storage", 0); |
| 1987 | builtin_fulfill_js_requests(); |
| 1988 | /* |
| 1989 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1990 | ** used for dynamically toggling certain UI components on and off. |
| 1991 | ** Must come after window.fossil has been intialized and before |
| 1992 | ** fossil.page.fileedit.js. Potential TODO: move this into the |
| 1993 | ** window.fossil bootstrapping so that we don't have to "fulfill" |
| 1994 | ** the JS multiple times. |
| 1995 | */ |
| 1996 | ajax_emit_js_preview_modes(1); |
| 1997 | builtin_request_js("fossil.page.fileedit.js"); |
| 1998 | builtin_fulfill_js_requests(); |
| 1999 | { |
| 2000 | /* Dynamically populate the editor, display any error in the err |
| 2001 | ** blob, and/or switch to tab #0, where the file selector |
| 2002 | ** lives. The extra C scopes here correspond to JS-level scopes, |
| 2003 | ** to improve grokability. */ |
| 2004 | style_emit_script_tag(0,0); |
| 2005 | CX("\n(function(){\n"); |
| 2006 | CX("try{\n"); |
| 2007 | { |
| 2008 | char * zFirstLeafUuid = 0; |
| 2009 | CX("fossil.config['fileedit-glob'] = "); |
| 2010 | glob_render_json_to_cgi(fileedit_glob()); |
| 2011 | CX(";\n"); |
| 2012 | if(blob_size(&err)>0){ |
| 2013 | CX("fossil.error(%!j);\n", blob_str(&err)); |
| 2014 | } |
| 2015 | /* Populate the page with the current leaves and, if available, |
| 2016 | the selected checkin's file list, to save 1 or 2 XHR requests |
| 2017 | at startup. That makes this page uncacheable, but compressed |
| 2018 | delivery of this page is currently less than 6k. */ |
| 2019 | CX("fossil.page.initialLeaves = "); |
| 2020 | fileedit_render_leaves_list(cimi.zParentUuid ? 0 : &zFirstLeafUuid); |
| 2021 | CX(";\n"); |
| 2022 | if(zFirstLeafUuid){ |
| 2023 | assert(!cimi.zParentUuid); |
| 2024 | cimi.zParentUuid = zFirstLeafUuid; |
| 2025 | zFirstLeafUuid = 0; |
| 2026 | } |
| 2027 | if(cimi.zParentUuid){ |
| 2028 | CX("fossil.page.initialFiles = "); |
| 2029 | fileedit_render_checkin_files(cimi.zParentUuid); |
| 2030 | CX(";\n"); |
| 2031 | } |
| 2032 | CX("fossil.onPageLoad(function(){\n"); |
| 2033 | { |
| 2034 | if(blob_size(&err)>0){ |
| 2035 | CX("fossil.error(%!j);\n", |
| 2036 | blob_str(&err)); |
| 2037 | CX("fossil.page.tabs.switchToTab(0);\n"); |
| 2038 | } |
| 2039 | if(cimi.zParentUuid && cimi.zFilename){ |
| 2040 | CX("fossil.page.loadFile(%!j,%!j);\n", |
| 2041 | cimi.zFilename, cimi.zParentUuid) |
| 2042 | /* Reminder we cannot embed the JSON-format |
| 2043 | content of the file here because if it contains |
| 2044 | a SCRIPT tag then it will break the whole page. */; |
| 2045 | } |
| 2046 | } |
| 2047 | CX("});\n")/*fossil.onPageLoad()*/; |
| 2048 | } |
| 2049 | CX("}catch(e){" |
| 2050 | "fossil.error(e); console.error('Exception:',e);" |
| 2051 | "}\n"); |
| 2052 | CX("})();")/*anonymous function*/; |
| 2053 | style_emit_script_tag(1,0); |
| 2054 | } |
| 2055 | blob_reset(&err); |
| 2056 | CheckinMiniInfo_cleanup(&cimi); |
| 2057 | db_end_transaction(0); |
| 2058 | style_footer(); |
| 2059 | } |
| 2060 |
+2
-2
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -54,11 +54,11 @@ | ||
| 54 | 54 | ** -s|--status select status mode (print a status indicator for FILE) |
| 55 | 55 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 56 | 56 | ** >22 or 0 (= no limit, resulting in a single line per |
| 57 | 57 | ** entry). |
| 58 | 58 | ** |
| 59 | -** See also: artifact, cat, descendants, info, leaves | |
| 59 | +** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]] | |
| 60 | 60 | */ |
| 61 | 61 | void finfo_cmd(void){ |
| 62 | 62 | db_must_be_within_tree(); |
| 63 | 63 | if( find_option("status","s",0) ){ |
| 64 | 64 | Stmt q; |
| @@ -245,11 +245,11 @@ | ||
| 245 | 245 | ** |
| 246 | 246 | ** Options: |
| 247 | 247 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 248 | 248 | ** -r VERSION The specific check-in containing the file |
| 249 | 249 | ** |
| 250 | -** See also: finfo | |
| 250 | +** See also: [[finfo]] | |
| 251 | 251 | */ |
| 252 | 252 | void cat_cmd(void){ |
| 253 | 253 | int i; |
| 254 | 254 | Blob content, fname; |
| 255 | 255 | const char *zRev; |
| 256 | 256 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -54,11 +54,11 @@ | |
| 54 | ** -s|--status select status mode (print a status indicator for FILE) |
| 55 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 56 | ** >22 or 0 (= no limit, resulting in a single line per |
| 57 | ** entry). |
| 58 | ** |
| 59 | ** See also: artifact, cat, descendants, info, leaves |
| 60 | */ |
| 61 | void finfo_cmd(void){ |
| 62 | db_must_be_within_tree(); |
| 63 | if( find_option("status","s",0) ){ |
| 64 | Stmt q; |
| @@ -245,11 +245,11 @@ | |
| 245 | ** |
| 246 | ** Options: |
| 247 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 248 | ** -r VERSION The specific check-in containing the file |
| 249 | ** |
| 250 | ** See also: finfo |
| 251 | */ |
| 252 | void cat_cmd(void){ |
| 253 | int i; |
| 254 | Blob content, fname; |
| 255 | const char *zRev; |
| 256 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -54,11 +54,11 @@ | |
| 54 | ** -s|--status select status mode (print a status indicator for FILE) |
| 55 | ** -W|--width <num> Width of lines (default is to auto-detect). Must be |
| 56 | ** >22 or 0 (= no limit, resulting in a single line per |
| 57 | ** entry). |
| 58 | ** |
| 59 | ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]] |
| 60 | */ |
| 61 | void finfo_cmd(void){ |
| 62 | db_must_be_within_tree(); |
| 63 | if( find_option("status","s",0) ){ |
| 64 | Stmt q; |
| @@ -245,11 +245,11 @@ | |
| 245 | ** |
| 246 | ** Options: |
| 247 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 248 | ** -r VERSION The specific check-in containing the file |
| 249 | ** |
| 250 | ** See also: [[finfo]] |
| 251 | */ |
| 252 | void cat_cmd(void){ |
| 253 | int i; |
| 254 | Blob content, fname; |
| 255 | const char *zRev; |
| 256 |
+7
-11
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -749,24 +749,20 @@ | ||
| 749 | 749 | forumthread_delete(pThread); |
| 750 | 750 | return target; |
| 751 | 751 | } |
| 752 | 752 | |
| 753 | 753 | /* |
| 754 | -** The first time this is called, it emits SCRIPT tags to load various | |
| 755 | -** forum-related JavaScript. Ideally it should be called near the end | |
| 756 | -** of the page, immediately before the call to style_footer() (which | |
| 757 | -** closes the document's <BODY> and <HTML> tags). Calls after the first | |
| 758 | -** are a no-op. | |
| 754 | +** Emits all JS code required by /forumpost. | |
| 759 | 755 | */ |
| 760 | -static void forum_emit_page_js(){ | |
| 756 | +static void forumpost_emit_page_js(){ | |
| 761 | 757 | static int once = 0; |
| 762 | 758 | if(0==once){ |
| 763 | 759 | once = 1; |
| 764 | - style_load_js("forum.js"); | |
| 765 | - style_emit_script_fossil_bootstrap(0); | |
| 766 | - style_emit_script_dom(0); | |
| 767 | - style_emit_script_builtin(0, "fossil.page.forumpost.js"); | |
| 760 | + style_emit_script_fossil_bootstrap(1); | |
| 761 | + builtin_request_js("forum.js"); | |
| 762 | + builtin_request_js("fossil.dom.js"); | |
| 763 | + builtin_request_js("fossil.page.forumpost.js"); | |
| 768 | 764 | } |
| 769 | 765 | } |
| 770 | 766 | |
| 771 | 767 | /* |
| 772 | 768 | ** WEBPAGE: forumpost |
| @@ -894,11 +890,11 @@ | ||
| 894 | 890 | }else{ |
| 895 | 891 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 896 | 892 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 897 | 893 | forum_display_hierarchical(froot, fpid); |
| 898 | 894 | } |
| 899 | - forum_emit_page_js(); | |
| 895 | + forumpost_emit_page_js(); | |
| 900 | 896 | style_footer(); |
| 901 | 897 | } |
| 902 | 898 | |
| 903 | 899 | /* |
| 904 | 900 | ** Return true if a forum post should be moderated. |
| 905 | 901 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -749,24 +749,20 @@ | |
| 749 | forumthread_delete(pThread); |
| 750 | return target; |
| 751 | } |
| 752 | |
| 753 | /* |
| 754 | ** The first time this is called, it emits SCRIPT tags to load various |
| 755 | ** forum-related JavaScript. Ideally it should be called near the end |
| 756 | ** of the page, immediately before the call to style_footer() (which |
| 757 | ** closes the document's <BODY> and <HTML> tags). Calls after the first |
| 758 | ** are a no-op. |
| 759 | */ |
| 760 | static void forum_emit_page_js(){ |
| 761 | static int once = 0; |
| 762 | if(0==once){ |
| 763 | once = 1; |
| 764 | style_load_js("forum.js"); |
| 765 | style_emit_script_fossil_bootstrap(0); |
| 766 | style_emit_script_dom(0); |
| 767 | style_emit_script_builtin(0, "fossil.page.forumpost.js"); |
| 768 | } |
| 769 | } |
| 770 | |
| 771 | /* |
| 772 | ** WEBPAGE: forumpost |
| @@ -894,11 +890,11 @@ | |
| 894 | }else{ |
| 895 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 896 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 897 | forum_display_hierarchical(froot, fpid); |
| 898 | } |
| 899 | forum_emit_page_js(); |
| 900 | style_footer(); |
| 901 | } |
| 902 | |
| 903 | /* |
| 904 | ** Return true if a forum post should be moderated. |
| 905 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -749,24 +749,20 @@ | |
| 749 | forumthread_delete(pThread); |
| 750 | return target; |
| 751 | } |
| 752 | |
| 753 | /* |
| 754 | ** Emits all JS code required by /forumpost. |
| 755 | */ |
| 756 | static void forumpost_emit_page_js(){ |
| 757 | static int once = 0; |
| 758 | if(0==once){ |
| 759 | once = 1; |
| 760 | style_emit_script_fossil_bootstrap(1); |
| 761 | builtin_request_js("forum.js"); |
| 762 | builtin_request_js("fossil.dom.js"); |
| 763 | builtin_request_js("fossil.page.forumpost.js"); |
| 764 | } |
| 765 | } |
| 766 | |
| 767 | /* |
| 768 | ** WEBPAGE: forumpost |
| @@ -894,11 +890,11 @@ | |
| 890 | }else{ |
| 891 | style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName); |
| 892 | style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName); |
| 893 | forum_display_hierarchical(froot, fpid); |
| 894 | } |
| 895 | forumpost_emit_page_js(); |
| 896 | style_footer(); |
| 897 | } |
| 898 | |
| 899 | /* |
| 900 | ** Return true if a forum post should be moderated. |
| 901 |
+1
-1
| --- src/forum.js | ||
| +++ src/forum.js | ||
| @@ -14,6 +14,6 @@ | ||
| 14 | 14 | var h = x[0].scrollHeight; |
| 15 | 15 | var y = absoluteY(x[0]); |
| 16 | 16 | if( w>h ) y = y + (h-w)/2; |
| 17 | 17 | if( y>0 ) window.scrollTo(0, y); |
| 18 | 18 | } |
| 19 | -})() | |
| 19 | +})(); | |
| 20 | 20 |
| --- src/forum.js | |
| +++ src/forum.js | |
| @@ -14,6 +14,6 @@ | |
| 14 | var h = x[0].scrollHeight; |
| 15 | var y = absoluteY(x[0]); |
| 16 | if( w>h ) y = y + (h-w)/2; |
| 17 | if( y>0 ) window.scrollTo(0, y); |
| 18 | } |
| 19 | })() |
| 20 |
| --- src/forum.js | |
| +++ src/forum.js | |
| @@ -14,6 +14,6 @@ | |
| 14 | var h = x[0].scrollHeight; |
| 15 | var y = absoluteY(x[0]); |
| 16 | if( w>h ) y = y + (h-w)/2; |
| 17 | if( y>0 ) window.scrollTo(0, y); |
| 18 | } |
| 19 | })(); |
| 20 |
+33
-3
| --- src/fossil.bootstrap.js | ||
| +++ src/fossil.bootstrap.js | ||
| @@ -1,6 +1,18 @@ | ||
| 1 | 1 | "use strict"; |
| 2 | +(function () { | |
| 3 | + /* CustomEvent polyfill, courtesy of Mozilla: | |
| 4 | + https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent | |
| 5 | + */ | |
| 6 | + if(typeof window.CustomEvent === "function") return false; | |
| 7 | + window.CustomEvent = function(event, params) { | |
| 8 | + if(!params) params = {bubbles: false, cancelable: false, detail: null}; | |
| 9 | + const evt = document.createEvent('CustomEvent'); | |
| 10 | + evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); | |
| 11 | + return evt; | |
| 12 | + }; | |
| 13 | +})(); | |
| 2 | 14 | (function(global){ |
| 3 | 15 | /* Bootstrapping bits for the global.fossil object. Must be |
| 4 | 16 | loaded after style.c:style_emit_script_tag() has initialized |
| 5 | 17 | that object. |
| 6 | 18 | */ |
| @@ -17,10 +29,25 @@ | ||
| 17 | 29 | } |
| 18 | 30 | const d = new Date(); |
| 19 | 31 | return d.toISOString().replace(f.rx1,'').split('T').join(' '); |
| 20 | 32 | }; |
| 21 | 33 | |
| 34 | + /** Returns the local time string of Date object d, defaulting | |
| 35 | + to the current time. */ | |
| 36 | + const localTimeString = function ff(d){ | |
| 37 | + if(!ff.pad){ | |
| 38 | + ff.pad = (x)=>(''+x).length>1 ? x : '0'+x; | |
| 39 | + } | |
| 40 | + d || (d = new Date()); | |
| 41 | + return [ | |
| 42 | + d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/), | |
| 43 | + '-',ff.pad(d.getDate()), | |
| 44 | + ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()), | |
| 45 | + ':',ff.pad(d.getSeconds()) | |
| 46 | + ].join(''); | |
| 47 | + }; | |
| 48 | + | |
| 22 | 49 | /* |
| 23 | 50 | ** By default fossil.message() sends its arguments console.debug(). If |
| 24 | 51 | ** fossil.message.targetElement is set, it is assumed to be a DOM |
| 25 | 52 | ** element, its innerText gets assigned to the concatenation of all |
| 26 | 53 | ** arguments (with a space between each), and the CSS 'error' class is |
| @@ -30,11 +57,14 @@ | ||
| 30 | 57 | ** Returns this object. |
| 31 | 58 | */ |
| 32 | 59 | F.message = function f(msg){ |
| 33 | 60 | const args = Array.prototype.slice.call(arguments,0); |
| 34 | 61 | const tgt = f.targetElement; |
| 35 | - if(args.length) args.unshift(timestring(),'UTC:'); | |
| 62 | + if(args.length) args.unshift( | |
| 63 | + localTimeString()+':' | |
| 64 | + //timestring(),'UTC:' | |
| 65 | + ); | |
| 36 | 66 | if(tgt){ |
| 37 | 67 | tgt.classList.remove('error'); |
| 38 | 68 | tgt.innerText = args.join(' '); |
| 39 | 69 | } |
| 40 | 70 | else{ |
| @@ -107,20 +137,20 @@ | ||
| 107 | 137 | repoUrl( repoRelativePath [,urlParams] ) |
| 108 | 138 | |
| 109 | 139 | Creates a URL by prepending this.rootPath to the given path |
| 110 | 140 | (which must be relative from the top of the site, without a |
| 111 | 141 | leading slash). If urlParams is a string, it must be |
| 112 | - paramters encoded in the form "key=val&key2=val2...", WITHOUT | |
| 142 | + paramters encoded in the form "key=val&key2=val2..." WITHOUT | |
| 113 | 143 | a leading '?'. If it's an object, all of its properties get |
| 114 | 144 | appended to the URL in that form. |
| 115 | 145 | */ |
| 116 | 146 | F.repoUrl = function(path,urlParams){ |
| 117 | 147 | if(!urlParams) return this.rootPath+path; |
| 118 | 148 | const url=[this.rootPath,path]; |
| 119 | 149 | url.push('?'); |
| 120 | 150 | if('string'===typeof urlParams) url.push(urlParams); |
| 121 | - else if('object'===typeof urlParams){ | |
| 151 | + else if(urlParams && 'object'===typeof urlParams){ | |
| 122 | 152 | this.encodeUrlArgs(urlParams, url); |
| 123 | 153 | } |
| 124 | 154 | return url.join(''); |
| 125 | 155 | }; |
| 126 | 156 | |
| 127 | 157 |
| --- src/fossil.bootstrap.js | |
| +++ src/fossil.bootstrap.js | |
| @@ -1,6 +1,18 @@ | |
| 1 | "use strict"; |
| 2 | (function(global){ |
| 3 | /* Bootstrapping bits for the global.fossil object. Must be |
| 4 | loaded after style.c:style_emit_script_tag() has initialized |
| 5 | that object. |
| 6 | */ |
| @@ -17,10 +29,25 @@ | |
| 17 | } |
| 18 | const d = new Date(); |
| 19 | return d.toISOString().replace(f.rx1,'').split('T').join(' '); |
| 20 | }; |
| 21 | |
| 22 | /* |
| 23 | ** By default fossil.message() sends its arguments console.debug(). If |
| 24 | ** fossil.message.targetElement is set, it is assumed to be a DOM |
| 25 | ** element, its innerText gets assigned to the concatenation of all |
| 26 | ** arguments (with a space between each), and the CSS 'error' class is |
| @@ -30,11 +57,14 @@ | |
| 30 | ** Returns this object. |
| 31 | */ |
| 32 | F.message = function f(msg){ |
| 33 | const args = Array.prototype.slice.call(arguments,0); |
| 34 | const tgt = f.targetElement; |
| 35 | if(args.length) args.unshift(timestring(),'UTC:'); |
| 36 | if(tgt){ |
| 37 | tgt.classList.remove('error'); |
| 38 | tgt.innerText = args.join(' '); |
| 39 | } |
| 40 | else{ |
| @@ -107,20 +137,20 @@ | |
| 107 | repoUrl( repoRelativePath [,urlParams] ) |
| 108 | |
| 109 | Creates a URL by prepending this.rootPath to the given path |
| 110 | (which must be relative from the top of the site, without a |
| 111 | leading slash). If urlParams is a string, it must be |
| 112 | paramters encoded in the form "key=val&key2=val2...", WITHOUT |
| 113 | a leading '?'. If it's an object, all of its properties get |
| 114 | appended to the URL in that form. |
| 115 | */ |
| 116 | F.repoUrl = function(path,urlParams){ |
| 117 | if(!urlParams) return this.rootPath+path; |
| 118 | const url=[this.rootPath,path]; |
| 119 | url.push('?'); |
| 120 | if('string'===typeof urlParams) url.push(urlParams); |
| 121 | else if('object'===typeof urlParams){ |
| 122 | this.encodeUrlArgs(urlParams, url); |
| 123 | } |
| 124 | return url.join(''); |
| 125 | }; |
| 126 | |
| 127 |
| --- src/fossil.bootstrap.js | |
| +++ src/fossil.bootstrap.js | |
| @@ -1,6 +1,18 @@ | |
| 1 | "use strict"; |
| 2 | (function () { |
| 3 | /* CustomEvent polyfill, courtesy of Mozilla: |
| 4 | https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent |
| 5 | */ |
| 6 | if(typeof window.CustomEvent === "function") return false; |
| 7 | window.CustomEvent = function(event, params) { |
| 8 | if(!params) params = {bubbles: false, cancelable: false, detail: null}; |
| 9 | const evt = document.createEvent('CustomEvent'); |
| 10 | evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); |
| 11 | return evt; |
| 12 | }; |
| 13 | })(); |
| 14 | (function(global){ |
| 15 | /* Bootstrapping bits for the global.fossil object. Must be |
| 16 | loaded after style.c:style_emit_script_tag() has initialized |
| 17 | that object. |
| 18 | */ |
| @@ -17,10 +29,25 @@ | |
| 29 | } |
| 30 | const d = new Date(); |
| 31 | return d.toISOString().replace(f.rx1,'').split('T').join(' '); |
| 32 | }; |
| 33 | |
| 34 | /** Returns the local time string of Date object d, defaulting |
| 35 | to the current time. */ |
| 36 | const localTimeString = function ff(d){ |
| 37 | if(!ff.pad){ |
| 38 | ff.pad = (x)=>(''+x).length>1 ? x : '0'+x; |
| 39 | } |
| 40 | d || (d = new Date()); |
| 41 | return [ |
| 42 | d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/), |
| 43 | '-',ff.pad(d.getDate()), |
| 44 | ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()), |
| 45 | ':',ff.pad(d.getSeconds()) |
| 46 | ].join(''); |
| 47 | }; |
| 48 | |
| 49 | /* |
| 50 | ** By default fossil.message() sends its arguments console.debug(). If |
| 51 | ** fossil.message.targetElement is set, it is assumed to be a DOM |
| 52 | ** element, its innerText gets assigned to the concatenation of all |
| 53 | ** arguments (with a space between each), and the CSS 'error' class is |
| @@ -30,11 +57,14 @@ | |
| 57 | ** Returns this object. |
| 58 | */ |
| 59 | F.message = function f(msg){ |
| 60 | const args = Array.prototype.slice.call(arguments,0); |
| 61 | const tgt = f.targetElement; |
| 62 | if(args.length) args.unshift( |
| 63 | localTimeString()+':' |
| 64 | //timestring(),'UTC:' |
| 65 | ); |
| 66 | if(tgt){ |
| 67 | tgt.classList.remove('error'); |
| 68 | tgt.innerText = args.join(' '); |
| 69 | } |
| 70 | else{ |
| @@ -107,20 +137,20 @@ | |
| 137 | repoUrl( repoRelativePath [,urlParams] ) |
| 138 | |
| 139 | Creates a URL by prepending this.rootPath to the given path |
| 140 | (which must be relative from the top of the site, without a |
| 141 | leading slash). If urlParams is a string, it must be |
| 142 | paramters encoded in the form "key=val&key2=val2..." WITHOUT |
| 143 | a leading '?'. If it's an object, all of its properties get |
| 144 | appended to the URL in that form. |
| 145 | */ |
| 146 | F.repoUrl = function(path,urlParams){ |
| 147 | if(!urlParams) return this.rootPath+path; |
| 148 | const url=[this.rootPath,path]; |
| 149 | url.push('?'); |
| 150 | if('string'===typeof urlParams) url.push(urlParams); |
| 151 | else if(urlParams && 'object'===typeof urlParams){ |
| 152 | this.encodeUrlArgs(urlParams, url); |
| 153 | } |
| 154 | return url.join(''); |
| 155 | }; |
| 156 | |
| 157 |
+41
-8
| --- src/fossil.confirmer.js | ||
| +++ src/fossil.confirmer.js | ||
| @@ -81,10 +81,21 @@ | ||
| 81 | 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | 84 | then a default implementation is used which updates the element with |
| 85 | 85 | the .confirmText, prepending a countdown to it. |
| 86 | + | |
| 87 | + .pinSize = if true AND confirmText is set, calculate the larger of | |
| 88 | + the element's original and confirmed size and pin it to the larger | |
| 89 | + of those sizes to avoid layout reflows when confirmation is | |
| 90 | + running. The pinning is implemented by setting its minWidth and | |
| 91 | + maxWidth style properties to the same value. This does not work if | |
| 92 | + the element text is updated dynamically via ontick(). This ONLY | |
| 93 | + works if the element is in the DOM and is not hidden (e.g. via | |
| 94 | + display:none) at the time this routine is called, otherwise we | |
| 95 | + cannot calculate its size. If the element needs to be hidden, hide | |
| 96 | + it after initializing the confirmer. | |
| 86 | 97 | |
| 87 | 98 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 88 | 99 | console to track what it's doing. |
| 89 | 100 | |
| 90 | 101 | Various notes: |
| @@ -99,17 +110,26 @@ | ||
| 99 | 110 | - Due to the nature of multi-threaded code, it is potentially possible |
| 100 | 111 | that confirmation and timeout actions BOTH happen if the user |
| 101 | 112 | triggers the associated action at "just the right millisecond" |
| 102 | 113 | before the timeout is triggered. |
| 103 | 114 | |
| 104 | -TODO: add an invert option which activates if the timeout is reached | |
| 105 | -and "times out" if the element is clicked again. e.g. a button which | |
| 106 | -says "Saving..." and cancels the op if it's clicked again, else it | |
| 107 | -saves after X time/ticks. | |
| 115 | +TODO: | |
| 116 | + | |
| 117 | +- Add an invert option which activates if the timeout is reached and | |
| 118 | +"times out" if the element is clicked again. e.g. a button which says | |
| 119 | +"Saving..." and cancels the op if it's clicked again, else it saves | |
| 120 | +after X time/ticks. | |
| 121 | + | |
| 122 | +- Internally we save/restore the initial text of non-INPUT elements | |
| 123 | +using innerHTML. We should instead move their child nodes aside (into | |
| 124 | +an internal out-of-DOM element) and restore them as needed. | |
| 108 | 125 | |
| 109 | 126 | Terse Change history: |
| 110 | 127 | |
| 128 | +- 20200811 | |
| 129 | + - Added pinSize option. | |
| 130 | + | |
| 111 | 131 | - 20200507: |
| 112 | 132 | - Add a tick-based countdown in order to more easily support |
| 113 | 133 | updating the target element with the countdown. |
| 114 | 134 | |
| 115 | 135 | - 20200506: |
| @@ -138,14 +158,26 @@ | ||
| 138 | 158 | const isInput = f.isInput(target); |
| 139 | 159 | const updateText = function(msg){ |
| 140 | 160 | if(isInput) target.value = msg; |
| 141 | 161 | else target.innerHTML = msg; |
| 142 | 162 | } |
| 163 | + const formatCountdown = (txt, number) => txt + " ["+number+"]"; | |
| 164 | + if(opt.pinSize && opt.confirmText){ | |
| 165 | + /* Try to pin the element's width the the greater of its | |
| 166 | + current width or its waiting-on-confirmation width | |
| 167 | + to avoid layout reflow when it's activated. */ | |
| 168 | + const digits = (''+(opt.timeout/1000 || opt.ticks)).length; | |
| 169 | + const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits)); | |
| 170 | + const w1 = parseFloat(window.getComputedStyle(target).width); | |
| 171 | + updateText(lblLong); | |
| 172 | + const w2 = parseFloat(window.getComputedStyle(target).width); | |
| 173 | + target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; | |
| 174 | + } | |
| 143 | 175 | updateText(this.opt.initialText); |
| 144 | 176 | if(this.opt.ticks && !this.opt.ontick){ |
| 145 | 177 | this.opt.ontick = function(tick){ |
| 146 | - updateText("("+tick+") "+self.opt.confirmText); | |
| 178 | + updateText(formatCountdown(self.opt.confirmText,tick)); | |
| 147 | 179 | }; |
| 148 | 180 | } |
| 149 | 181 | this.setClasses(false); |
| 150 | 182 | this.doTimeout = function() { |
| 151 | 183 | if(this.timerID){ |
| @@ -268,15 +300,16 @@ | ||
| 268 | 300 | }; |
| 269 | 301 | /** |
| 270 | 302 | The default options for initConfirmer(). Tweak them to set the |
| 271 | 303 | defaults. A couple of them (initialText and confirmText) are |
| 272 | 304 | dynamically-generated, and can't reasonably be set in the |
| 273 | - defaults. | |
| 305 | + defaults. Some, like ticks, cannot be set here because that would | |
| 306 | + end up indirectly replacing non-tick timeouts with ticks. | |
| 274 | 307 | */ |
| 275 | 308 | F.confirmer.defaultOpts = { |
| 276 | - timeout:3000, | |
| 277 | - ticks: undefined, | |
| 309 | + timeout:undefined, | |
| 310 | + ticks: 3, | |
| 278 | 311 | ticktime: 998/*not *quite* 1000*/, |
| 279 | 312 | onconfirm: undefined, |
| 280 | 313 | ontimeout: undefined, |
| 281 | 314 | onactivate: undefined, |
| 282 | 315 | classInitial: '', |
| 283 | 316 | |
| 284 | 317 | ADDED src/fossil.copybutton.js |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -81,10 +81,21 @@ | |
| 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | then a default implementation is used which updates the element with |
| 85 | the .confirmText, prepending a countdown to it. |
| 86 | |
| 87 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 88 | console to track what it's doing. |
| 89 | |
| 90 | Various notes: |
| @@ -99,17 +110,26 @@ | |
| 99 | - Due to the nature of multi-threaded code, it is potentially possible |
| 100 | that confirmation and timeout actions BOTH happen if the user |
| 101 | triggers the associated action at "just the right millisecond" |
| 102 | before the timeout is triggered. |
| 103 | |
| 104 | TODO: add an invert option which activates if the timeout is reached |
| 105 | and "times out" if the element is clicked again. e.g. a button which |
| 106 | says "Saving..." and cancels the op if it's clicked again, else it |
| 107 | saves after X time/ticks. |
| 108 | |
| 109 | Terse Change history: |
| 110 | |
| 111 | - 20200507: |
| 112 | - Add a tick-based countdown in order to more easily support |
| 113 | updating the target element with the countdown. |
| 114 | |
| 115 | - 20200506: |
| @@ -138,14 +158,26 @@ | |
| 138 | const isInput = f.isInput(target); |
| 139 | const updateText = function(msg){ |
| 140 | if(isInput) target.value = msg; |
| 141 | else target.innerHTML = msg; |
| 142 | } |
| 143 | updateText(this.opt.initialText); |
| 144 | if(this.opt.ticks && !this.opt.ontick){ |
| 145 | this.opt.ontick = function(tick){ |
| 146 | updateText("("+tick+") "+self.opt.confirmText); |
| 147 | }; |
| 148 | } |
| 149 | this.setClasses(false); |
| 150 | this.doTimeout = function() { |
| 151 | if(this.timerID){ |
| @@ -268,15 +300,16 @@ | |
| 268 | }; |
| 269 | /** |
| 270 | The default options for initConfirmer(). Tweak them to set the |
| 271 | defaults. A couple of them (initialText and confirmText) are |
| 272 | dynamically-generated, and can't reasonably be set in the |
| 273 | defaults. |
| 274 | */ |
| 275 | F.confirmer.defaultOpts = { |
| 276 | timeout:3000, |
| 277 | ticks: undefined, |
| 278 | ticktime: 998/*not *quite* 1000*/, |
| 279 | onconfirm: undefined, |
| 280 | ontimeout: undefined, |
| 281 | onactivate: undefined, |
| 282 | classInitial: '', |
| 283 | |
| 284 | DDED src/fossil.copybutton.js |
| --- src/fossil.confirmer.js | |
| +++ src/fossil.confirmer.js | |
| @@ -81,10 +81,21 @@ | |
| 81 | action has been confirmed, immediately before the onconfirm or |
| 82 | ontimeout callback. The intention of the callback is to update the |
| 83 | label of the target element. If .ticks is set but .ontick is not |
| 84 | then a default implementation is used which updates the element with |
| 85 | the .confirmText, prepending a countdown to it. |
| 86 | |
| 87 | .pinSize = if true AND confirmText is set, calculate the larger of |
| 88 | the element's original and confirmed size and pin it to the larger |
| 89 | of those sizes to avoid layout reflows when confirmation is |
| 90 | running. The pinning is implemented by setting its minWidth and |
| 91 | maxWidth style properties to the same value. This does not work if |
| 92 | the element text is updated dynamically via ontick(). This ONLY |
| 93 | works if the element is in the DOM and is not hidden (e.g. via |
| 94 | display:none) at the time this routine is called, otherwise we |
| 95 | cannot calculate its size. If the element needs to be hidden, hide |
| 96 | it after initializing the confirmer. |
| 97 | |
| 98 | .debug = boolean. If truthy, it sends some debug output to the dev |
| 99 | console to track what it's doing. |
| 100 | |
| 101 | Various notes: |
| @@ -99,17 +110,26 @@ | |
| 110 | - Due to the nature of multi-threaded code, it is potentially possible |
| 111 | that confirmation and timeout actions BOTH happen if the user |
| 112 | triggers the associated action at "just the right millisecond" |
| 113 | before the timeout is triggered. |
| 114 | |
| 115 | TODO: |
| 116 | |
| 117 | - Add an invert option which activates if the timeout is reached and |
| 118 | "times out" if the element is clicked again. e.g. a button which says |
| 119 | "Saving..." and cancels the op if it's clicked again, else it saves |
| 120 | after X time/ticks. |
| 121 | |
| 122 | - Internally we save/restore the initial text of non-INPUT elements |
| 123 | using innerHTML. We should instead move their child nodes aside (into |
| 124 | an internal out-of-DOM element) and restore them as needed. |
| 125 | |
| 126 | Terse Change history: |
| 127 | |
| 128 | - 20200811 |
| 129 | - Added pinSize option. |
| 130 | |
| 131 | - 20200507: |
| 132 | - Add a tick-based countdown in order to more easily support |
| 133 | updating the target element with the countdown. |
| 134 | |
| 135 | - 20200506: |
| @@ -138,14 +158,26 @@ | |
| 158 | const isInput = f.isInput(target); |
| 159 | const updateText = function(msg){ |
| 160 | if(isInput) target.value = msg; |
| 161 | else target.innerHTML = msg; |
| 162 | } |
| 163 | const formatCountdown = (txt, number) => txt + " ["+number+"]"; |
| 164 | if(opt.pinSize && opt.confirmText){ |
| 165 | /* Try to pin the element's width the the greater of its |
| 166 | current width or its waiting-on-confirmation width |
| 167 | to avoid layout reflow when it's activated. */ |
| 168 | const digits = (''+(opt.timeout/1000 || opt.ticks)).length; |
| 169 | const lblLong = formatCountdown(opt.confirmText, "00000000".substr(0,digits)); |
| 170 | const w1 = parseFloat(window.getComputedStyle(target).width); |
| 171 | updateText(lblLong); |
| 172 | const w2 = parseFloat(window.getComputedStyle(target).width); |
| 173 | target.style.minWidth = target.style.maxWidth = (w1>w2 ? w1 : w2)+"px"; |
| 174 | } |
| 175 | updateText(this.opt.initialText); |
| 176 | if(this.opt.ticks && !this.opt.ontick){ |
| 177 | this.opt.ontick = function(tick){ |
| 178 | updateText(formatCountdown(self.opt.confirmText,tick)); |
| 179 | }; |
| 180 | } |
| 181 | this.setClasses(false); |
| 182 | this.doTimeout = function() { |
| 183 | if(this.timerID){ |
| @@ -268,15 +300,16 @@ | |
| 300 | }; |
| 301 | /** |
| 302 | The default options for initConfirmer(). Tweak them to set the |
| 303 | defaults. A couple of them (initialText and confirmText) are |
| 304 | dynamically-generated, and can't reasonably be set in the |
| 305 | defaults. Some, like ticks, cannot be set here because that would |
| 306 | end up indirectly replacing non-tick timeouts with ticks. |
| 307 | */ |
| 308 | F.confirmer.defaultOpts = { |
| 309 | timeout:undefined, |
| 310 | ticks: 3, |
| 311 | ticktime: 998/*not *quite* 1000*/, |
| 312 | onconfirm: undefined, |
| 313 | ontimeout: undefined, |
| 314 | onactivate: undefined, |
| 315 | classInitial: '', |
| 316 | |
| 317 | DDED src/fossil.copybutton.js |
| --- a/src/fossil.copybutton.js | ||
| +++ b/src/fossil.copybutton.js | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +(function(F/*fossil object*/){ | |
| 2 | + /** | |
| 3 | + A basic API for creating and |
| --- a/src/fossil.copybutton.js | |
| +++ b/src/fossil.copybutton.js | |
| @@ -0,0 +1,3 @@ | |
| --- a/src/fossil.copybutton.js | |
| +++ b/src/fossil.copybutton.js | |
| @@ -0,0 +1,3 @@ | |
| 1 | (function(F/*fossil object*/){ |
| 2 | /** |
| 3 | A basic API for creating and |
+93
| --- src/fossil.dom.js | ||
| +++ src/fossil.dom.js | ||
| @@ -465,10 +465,103 @@ | ||
| 465 | 465 | e = src.querySelector(x); |
| 466 | 466 | if(!e){ |
| 467 | 467 | e = new Error("Cannot find DOM element: "+x); |
| 468 | 468 | console.error(e, src); |
| 469 | 469 | throw e; |
| 470 | + } | |
| 471 | + return e; | |
| 472 | + }; | |
| 473 | + | |
| 474 | + /** | |
| 475 | + "Blinks" the given element a single time for the given number of | |
| 476 | + milliseconds, defaulting (if the 2nd argument is falsy or not a | |
| 477 | + number) to flashOnce.defaultTimeMs. If a 3rd argument is passed | |
| 478 | + in, it must be a function, and it gets callback back at the end | |
| 479 | + of the asynchronous flashing processes. | |
| 480 | + | |
| 481 | + This will only activate once per element during that timeframe - | |
| 482 | + further calls will become no-ops until the blink is | |
| 483 | + completed. This routine adds a dataset member to the element for | |
| 484 | + the duration of the blink, to allow it to block multiple blinks. | |
| 485 | + | |
| 486 | + If passed 2 arguments and the 2nd is a function, it behaves as if | |
| 487 | + it were called as (arg1, undefined, arg2). | |
| 488 | + | |
| 489 | + Returns e, noting that the flash itself is asynchronous and may | |
| 490 | + still be running, or not yet started, when this function returns. | |
| 491 | + */ | |
| 492 | + dom.flashOnce = function f(e,howLongMs,afterFlashCallback){ | |
| 493 | + if(e.dataset.isBlinking){ | |
| 494 | + return; | |
| 495 | + } | |
| 496 | + if(2===arguments.length && 'function' ===typeof howLongMs){ | |
| 497 | + afterFlashCallback = howLongMs; | |
| 498 | + howLongMs = f.defaultTimeMs; | |
| 499 | + } | |
| 500 | + if(!howLongMs || 'number'!==typeof howLongMs){ | |
| 501 | + howLongMs = f.defaultTimeMs; | |
| 502 | + } | |
| 503 | + e.dataset.isBlinking = true; | |
| 504 | + const transition = e.style.transition; | |
| 505 | + e.style.transition = "opacity "+howLongMs+"ms ease-in-out"; | |
| 506 | + const opacity = e.style.opacity; | |
| 507 | + e.style.opacity = 0; | |
| 508 | + setTimeout(function(){ | |
| 509 | + e.style.transition = transition; | |
| 510 | + e.style.opacity = opacity; | |
| 511 | + delete e.dataset.isBlinking; | |
| 512 | + if(afterFlashCallback) afterFlashCallback(); | |
| 513 | + }, howLongMs); | |
| 514 | + return e; | |
| 515 | + }; | |
| 516 | + dom.flashOnce.defaultTimeMs = 400; | |
| 517 | + | |
| 518 | + /** | |
| 519 | + Attempts to copy the given text to the system clipboard. Returns | |
| 520 | + true if it succeeds, else false. | |
| 521 | + */ | |
| 522 | + dom.copyTextToClipboard = function(text){ | |
| 523 | + if( window.clipboardData && window.clipboardData.setData ){ | |
| 524 | + clipboardData.setData('Text',text); | |
| 525 | + return true; | |
| 526 | + }else{ | |
| 527 | + const x = document.createElement("textarea"); | |
| 528 | + x.style.position = 'fixed'; | |
| 529 | + x.value = text; | |
| 530 | + document.body.appendChild(x); | |
| 531 | + x.select(); | |
| 532 | + var rc; | |
| 533 | + try{ | |
| 534 | + document.execCommand('copy'); | |
| 535 | + rc = true; | |
| 536 | + }catch(err){ | |
| 537 | + rc = false; | |
| 538 | + }finally{ | |
| 539 | + document.body.removeChild(x); | |
| 540 | + } | |
| 541 | + return rc; | |
| 542 | + } | |
| 543 | + }; | |
| 544 | + | |
| 545 | + /** | |
| 546 | + Copies all properties from the 2nd argument (a plain object) into | |
| 547 | + the style member of the first argument (DOM element or a | |
| 548 | + forEach-capable list of elements). If the 2nd argument is falsy | |
| 549 | + or empty, this is a no-op. | |
| 550 | + | |
| 551 | + Returns its first argument. | |
| 552 | + */ | |
| 553 | + dom.copyStyle = function f(e, style){ | |
| 554 | + if(e.forEach){ | |
| 555 | + e.forEach((x)=>f(x, style)); | |
| 556 | + return e; | |
| 557 | + } | |
| 558 | + if(style){ | |
| 559 | + let k; | |
| 560 | + for(k in style){ | |
| 561 | + if(style.hasOwnProperty(k)) e.style[k] = style[k]; | |
| 562 | + } | |
| 470 | 563 | } |
| 471 | 564 | return e; |
| 472 | 565 | }; |
| 473 | 566 | |
| 474 | 567 | return F.dom = dom; |
| 475 | 568 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -465,10 +465,103 @@ | |
| 465 | e = src.querySelector(x); |
| 466 | if(!e){ |
| 467 | e = new Error("Cannot find DOM element: "+x); |
| 468 | console.error(e, src); |
| 469 | throw e; |
| 470 | } |
| 471 | return e; |
| 472 | }; |
| 473 | |
| 474 | return F.dom = dom; |
| 475 |
| --- src/fossil.dom.js | |
| +++ src/fossil.dom.js | |
| @@ -465,10 +465,103 @@ | |
| 465 | e = src.querySelector(x); |
| 466 | if(!e){ |
| 467 | e = new Error("Cannot find DOM element: "+x); |
| 468 | console.error(e, src); |
| 469 | throw e; |
| 470 | } |
| 471 | return e; |
| 472 | }; |
| 473 | |
| 474 | /** |
| 475 | "Blinks" the given element a single time for the given number of |
| 476 | milliseconds, defaulting (if the 2nd argument is falsy or not a |
| 477 | number) to flashOnce.defaultTimeMs. If a 3rd argument is passed |
| 478 | in, it must be a function, and it gets callback back at the end |
| 479 | of the asynchronous flashing processes. |
| 480 | |
| 481 | This will only activate once per element during that timeframe - |
| 482 | further calls will become no-ops until the blink is |
| 483 | completed. This routine adds a dataset member to the element for |
| 484 | the duration of the blink, to allow it to block multiple blinks. |
| 485 | |
| 486 | If passed 2 arguments and the 2nd is a function, it behaves as if |
| 487 | it were called as (arg1, undefined, arg2). |
| 488 | |
| 489 | Returns e, noting that the flash itself is asynchronous and may |
| 490 | still be running, or not yet started, when this function returns. |
| 491 | */ |
| 492 | dom.flashOnce = function f(e,howLongMs,afterFlashCallback){ |
| 493 | if(e.dataset.isBlinking){ |
| 494 | return; |
| 495 | } |
| 496 | if(2===arguments.length && 'function' ===typeof howLongMs){ |
| 497 | afterFlashCallback = howLongMs; |
| 498 | howLongMs = f.defaultTimeMs; |
| 499 | } |
| 500 | if(!howLongMs || 'number'!==typeof howLongMs){ |
| 501 | howLongMs = f.defaultTimeMs; |
| 502 | } |
| 503 | e.dataset.isBlinking = true; |
| 504 | const transition = e.style.transition; |
| 505 | e.style.transition = "opacity "+howLongMs+"ms ease-in-out"; |
| 506 | const opacity = e.style.opacity; |
| 507 | e.style.opacity = 0; |
| 508 | setTimeout(function(){ |
| 509 | e.style.transition = transition; |
| 510 | e.style.opacity = opacity; |
| 511 | delete e.dataset.isBlinking; |
| 512 | if(afterFlashCallback) afterFlashCallback(); |
| 513 | }, howLongMs); |
| 514 | return e; |
| 515 | }; |
| 516 | dom.flashOnce.defaultTimeMs = 400; |
| 517 | |
| 518 | /** |
| 519 | Attempts to copy the given text to the system clipboard. Returns |
| 520 | true if it succeeds, else false. |
| 521 | */ |
| 522 | dom.copyTextToClipboard = function(text){ |
| 523 | if( window.clipboardData && window.clipboardData.setData ){ |
| 524 | clipboardData.setData('Text',text); |
| 525 | return true; |
| 526 | }else{ |
| 527 | const x = document.createElement("textarea"); |
| 528 | x.style.position = 'fixed'; |
| 529 | x.value = text; |
| 530 | document.body.appendChild(x); |
| 531 | x.select(); |
| 532 | var rc; |
| 533 | try{ |
| 534 | document.execCommand('copy'); |
| 535 | rc = true; |
| 536 | }catch(err){ |
| 537 | rc = false; |
| 538 | }finally{ |
| 539 | document.body.removeChild(x); |
| 540 | } |
| 541 | return rc; |
| 542 | } |
| 543 | }; |
| 544 | |
| 545 | /** |
| 546 | Copies all properties from the 2nd argument (a plain object) into |
| 547 | the style member of the first argument (DOM element or a |
| 548 | forEach-capable list of elements). If the 2nd argument is falsy |
| 549 | or empty, this is a no-op. |
| 550 | |
| 551 | Returns its first argument. |
| 552 | */ |
| 553 | dom.copyStyle = function f(e, style){ |
| 554 | if(e.forEach){ |
| 555 | e.forEach((x)=>f(x, style)); |
| 556 | return e; |
| 557 | } |
| 558 | if(style){ |
| 559 | let k; |
| 560 | for(k in style){ |
| 561 | if(style.hasOwnProperty(k)) e.style[k] = style[k]; |
| 562 | } |
| 563 | } |
| 564 | return e; |
| 565 | }; |
| 566 | |
| 567 | return F.dom = dom; |
| 568 |
+23
-9
| --- src/fossil.fetch.js | ||
| +++ src/fossil.fetch.js | ||
| @@ -1,10 +1,13 @@ | ||
| 1 | 1 | "use strict"; |
| 2 | 2 | /** |
| 3 | 3 | Requires that window.fossil has already been set up. |
| 4 | - | |
| 5 | - window.fossil.fetch() is an HTTP request/response mini-framework | |
| 4 | +*/ | |
| 5 | +(function(namespace){ | |
| 6 | +const fossil = namespace; | |
| 7 | + /** | |
| 8 | + fetch() is an HTTP request/response mini-framework | |
| 6 | 9 | similar (but not identical) to the not-quite-ubiquitous |
| 7 | 10 | window.fetch(). |
| 8 | 11 | |
| 9 | 12 | JS usages: |
| 10 | 13 | |
| @@ -87,19 +90,21 @@ | ||
| 87 | 90 | function with the same name, e.g. fossil.fetch.onload(). The |
| 88 | 91 | default onload/onerror implementations route the data through the |
| 89 | 92 | dev console and (for onerror()) through fossil.error(). The default |
| 90 | 93 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 91 | 94 | those members to provide default implementations suitable for the |
| 92 | - page's use, e.g. keeping track of how many in-flight | |
| 95 | + page's use, e.g. keeping track of how many in-flight ajax requests | |
| 96 | + are pending. Any exceptions thrown in an beforesend/aftersend | |
| 97 | + handler are current ignored (feature or bug?). | |
| 93 | 98 | |
| 94 | 99 | Note that this routine may add properties to the 2nd argument, so |
| 95 | 100 | that instance should not be kept around for later use. |
| 96 | 101 | |
| 97 | 102 | Returns this object, noting that the XHR request is asynchronous, |
| 98 | 103 | and still in transit (or has yet to be sent) when that happens. |
| 99 | 104 | */ |
| 100 | -window.fossil.fetch = function f(uri,opt){ | |
| 105 | +fossil.fetch = function f(uri,opt){ | |
| 101 | 106 | const F = fossil; |
| 102 | 107 | if(!f.onload){ |
| 103 | 108 | f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r); |
| 104 | 109 | } |
| 105 | 110 | if(!f.onerror){ |
| @@ -106,11 +111,11 @@ | ||
| 106 | 111 | f.onerror = function(e/*exception*/){ |
| 107 | 112 | console.error("fossil.fetch() XHR error:",e); |
| 108 | 113 | if(e instanceof Error) F.error('Exception:',e); |
| 109 | 114 | else F.error("Unknown error in handling of XHR request."); |
| 110 | 115 | }; |
| 111 | - }/*f.onerror()*/ | |
| 116 | + } | |
| 112 | 117 | if(!f.parseResponseHeaders){ |
| 113 | 118 | f.parseResponseHeaders = function(h){ |
| 114 | 119 | const rc = {}; |
| 115 | 120 | if(!h) return rc; |
| 116 | 121 | const ar = h.trim().split(/[\r\n]+/); |
| @@ -142,11 +147,11 @@ | ||
| 142 | 147 | || payload instanceof Array)){ |
| 143 | 148 | payload = JSON.stringify(payload); |
| 144 | 149 | opt.contentType = 'application/json'; |
| 145 | 150 | } |
| 146 | 151 | } |
| 147 | - const url=[F.repoUrl(uri,opt.urlParams)], | |
| 152 | + const url=[f.urlTransform(uri,opt.urlParams)], | |
| 148 | 153 | x=new XMLHttpRequest(); |
| 149 | 154 | if('json'===opt.responseType){ |
| 150 | 155 | /* 'json' is an extension to the supported XHR.responseType |
| 151 | 156 | list. We use it as a flag to tell us to JSON.parse() |
| 152 | 157 | the response. */ |
| @@ -201,8 +206,17 @@ | ||
| 201 | 206 | if(undefined!==payload) x.send(payload); |
| 202 | 207 | else x.send(); |
| 203 | 208 | return this; |
| 204 | 209 | }; |
| 205 | 210 | |
| 206 | -window.fossil.fetch.beforesend = function(){}; | |
| 207 | -window.fossil.fetch.aftersend = function(){}; | |
| 208 | -window.fossil.fetch.timeout = 15000/* Default timeout, in ms. */; | |
| 211 | +/** | |
| 212 | + urlTransform() must refer to a function which accepts a relative path | |
| 213 | + to the same site as fetch() is served from and an optional set of | |
| 214 | + URL parameters to pass with it (in the form a of a string | |
| 215 | + ("a=b&c=d...") or an object of key/value pairs (which it converts | |
| 216 | + to such a string), and returns the resulting URL or URI as a string. | |
| 217 | +*/ | |
| 218 | +fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p); | |
| 219 | +fossil.fetch.beforesend = function(){}; | |
| 220 | +fossil.fetch.aftersend = function(){}; | |
| 221 | +fossil.fetch.timeout = 15000/* Default timeout, in ms. */; | |
| 222 | +})(window.fossil); | |
| 209 | 223 | |
| 210 | 224 | ADDED src/fossil.numbered-lines.js |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -1,10 +1,13 @@ | |
| 1 | "use strict"; |
| 2 | /** |
| 3 | Requires that window.fossil has already been set up. |
| 4 | |
| 5 | window.fossil.fetch() is an HTTP request/response mini-framework |
| 6 | similar (but not identical) to the not-quite-ubiquitous |
| 7 | window.fetch(). |
| 8 | |
| 9 | JS usages: |
| 10 | |
| @@ -87,19 +90,21 @@ | |
| 87 | function with the same name, e.g. fossil.fetch.onload(). The |
| 88 | default onload/onerror implementations route the data through the |
| 89 | dev console and (for onerror()) through fossil.error(). The default |
| 90 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 91 | those members to provide default implementations suitable for the |
| 92 | page's use, e.g. keeping track of how many in-flight |
| 93 | |
| 94 | Note that this routine may add properties to the 2nd argument, so |
| 95 | that instance should not be kept around for later use. |
| 96 | |
| 97 | Returns this object, noting that the XHR request is asynchronous, |
| 98 | and still in transit (or has yet to be sent) when that happens. |
| 99 | */ |
| 100 | window.fossil.fetch = function f(uri,opt){ |
| 101 | const F = fossil; |
| 102 | if(!f.onload){ |
| 103 | f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r); |
| 104 | } |
| 105 | if(!f.onerror){ |
| @@ -106,11 +111,11 @@ | |
| 106 | f.onerror = function(e/*exception*/){ |
| 107 | console.error("fossil.fetch() XHR error:",e); |
| 108 | if(e instanceof Error) F.error('Exception:',e); |
| 109 | else F.error("Unknown error in handling of XHR request."); |
| 110 | }; |
| 111 | }/*f.onerror()*/ |
| 112 | if(!f.parseResponseHeaders){ |
| 113 | f.parseResponseHeaders = function(h){ |
| 114 | const rc = {}; |
| 115 | if(!h) return rc; |
| 116 | const ar = h.trim().split(/[\r\n]+/); |
| @@ -142,11 +147,11 @@ | |
| 142 | || payload instanceof Array)){ |
| 143 | payload = JSON.stringify(payload); |
| 144 | opt.contentType = 'application/json'; |
| 145 | } |
| 146 | } |
| 147 | const url=[F.repoUrl(uri,opt.urlParams)], |
| 148 | x=new XMLHttpRequest(); |
| 149 | if('json'===opt.responseType){ |
| 150 | /* 'json' is an extension to the supported XHR.responseType |
| 151 | list. We use it as a flag to tell us to JSON.parse() |
| 152 | the response. */ |
| @@ -201,8 +206,17 @@ | |
| 201 | if(undefined!==payload) x.send(payload); |
| 202 | else x.send(); |
| 203 | return this; |
| 204 | }; |
| 205 | |
| 206 | window.fossil.fetch.beforesend = function(){}; |
| 207 | window.fossil.fetch.aftersend = function(){}; |
| 208 | window.fossil.fetch.timeout = 15000/* Default timeout, in ms. */; |
| 209 | |
| 210 | DDED src/fossil.numbered-lines.js |
| --- src/fossil.fetch.js | |
| +++ src/fossil.fetch.js | |
| @@ -1,10 +1,13 @@ | |
| 1 | "use strict"; |
| 2 | /** |
| 3 | Requires that window.fossil has already been set up. |
| 4 | */ |
| 5 | (function(namespace){ |
| 6 | const fossil = namespace; |
| 7 | /** |
| 8 | fetch() is an HTTP request/response mini-framework |
| 9 | similar (but not identical) to the not-quite-ubiquitous |
| 10 | window.fetch(). |
| 11 | |
| 12 | JS usages: |
| 13 | |
| @@ -87,19 +90,21 @@ | |
| 90 | function with the same name, e.g. fossil.fetch.onload(). The |
| 91 | default onload/onerror implementations route the data through the |
| 92 | dev console and (for onerror()) through fossil.error(). The default |
| 93 | beforesend/aftersend are no-ops. Individual pages may overwrite |
| 94 | those members to provide default implementations suitable for the |
| 95 | page's use, e.g. keeping track of how many in-flight ajax requests |
| 96 | are pending. Any exceptions thrown in an beforesend/aftersend |
| 97 | handler are current ignored (feature or bug?). |
| 98 | |
| 99 | Note that this routine may add properties to the 2nd argument, so |
| 100 | that instance should not be kept around for later use. |
| 101 | |
| 102 | Returns this object, noting that the XHR request is asynchronous, |
| 103 | and still in transit (or has yet to be sent) when that happens. |
| 104 | */ |
| 105 | fossil.fetch = function f(uri,opt){ |
| 106 | const F = fossil; |
| 107 | if(!f.onload){ |
| 108 | f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r); |
| 109 | } |
| 110 | if(!f.onerror){ |
| @@ -106,11 +111,11 @@ | |
| 111 | f.onerror = function(e/*exception*/){ |
| 112 | console.error("fossil.fetch() XHR error:",e); |
| 113 | if(e instanceof Error) F.error('Exception:',e); |
| 114 | else F.error("Unknown error in handling of XHR request."); |
| 115 | }; |
| 116 | } |
| 117 | if(!f.parseResponseHeaders){ |
| 118 | f.parseResponseHeaders = function(h){ |
| 119 | const rc = {}; |
| 120 | if(!h) return rc; |
| 121 | const ar = h.trim().split(/[\r\n]+/); |
| @@ -142,11 +147,11 @@ | |
| 147 | || payload instanceof Array)){ |
| 148 | payload = JSON.stringify(payload); |
| 149 | opt.contentType = 'application/json'; |
| 150 | } |
| 151 | } |
| 152 | const url=[f.urlTransform(uri,opt.urlParams)], |
| 153 | x=new XMLHttpRequest(); |
| 154 | if('json'===opt.responseType){ |
| 155 | /* 'json' is an extension to the supported XHR.responseType |
| 156 | list. We use it as a flag to tell us to JSON.parse() |
| 157 | the response. */ |
| @@ -201,8 +206,17 @@ | |
| 206 | if(undefined!==payload) x.send(payload); |
| 207 | else x.send(); |
| 208 | return this; |
| 209 | }; |
| 210 | |
| 211 | /** |
| 212 | urlTransform() must refer to a function which accepts a relative path |
| 213 | to the same site as fetch() is served from and an optional set of |
| 214 | URL parameters to pass with it (in the form a of a string |
| 215 | ("a=b&c=d...") or an object of key/value pairs (which it converts |
| 216 | to such a string), and returns the resulting URL or URI as a string. |
| 217 | */ |
| 218 | fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p); |
| 219 | fossil.fetch.beforesend = function(){}; |
| 220 | fossil.fetch.aftersend = function(){}; |
| 221 | fossil.fetch.timeout = 15000/* Default timeout, in ms. */; |
| 222 | })(window.fossil); |
| 223 | |
| 224 | DDED src/fossil.numbered-lines.js |
| --- a/src/fossil.numbered-lines.js | ||
| +++ b/src/fossil.numbered-lines.js | ||
| @@ -0,0 +1,8 @@ | ||
| 1 | +(function callee(arg){ | |
| 2 | + /* | |
| 3 | + JSelse if(!arg){tion callee(arg){ | |
| 4 | + /* | |
| 5 | + JS(function callee(arg){ | |
| 6 | + /* | |
| 7 | + JSfossil', | |
| 8 | + ( |
| --- a/src/fossil.numbered-lines.js | |
| +++ b/src/fossil.numbered-lines.js | |
| @@ -0,0 +1,8 @@ | |
| --- a/src/fossil.numbered-lines.js | |
| +++ b/src/fossil.numbered-lines.js | |
| @@ -0,0 +1,8 @@ | |
| 1 | (function callee(arg){ |
| 2 | /* |
| 3 | JSelse if(!arg){tion callee(arg){ |
| 4 | /* |
| 5 | JS(function callee(arg){ |
| 6 | /* |
| 7 | JSfossil', |
| 8 | ( |
+126
-104
| --- src/fossil.page.fileedit.js | ||
| +++ src/fossil.page.fileedit.js | ||
| @@ -121,11 +121,11 @@ | ||
| 121 | 121 | and that would be horribly inefficient (meaning "battery-consuming" |
| 122 | 122 | on mobile devices). |
| 123 | 123 | */ |
| 124 | 124 | const $stash = { |
| 125 | 125 | keys: { |
| 126 | - index: F.page.name+'/index' | |
| 126 | + index: F.page.name+'.index' | |
| 127 | 127 | }, |
| 128 | 128 | /** |
| 129 | 129 | index: { |
| 130 | 130 | "CHECKIN_HASH:FILENAME": {file info w/o content} |
| 131 | 131 | ... |
| @@ -149,27 +149,12 @@ | ||
| 149 | 149 | /** Returns the index object, fetching it from the stash or creating |
| 150 | 150 | it anew on the first call. */ |
| 151 | 151 | getIndex: function(){ |
| 152 | 152 | if(!this.index){ |
| 153 | 153 | this.index = F.storage.getJSON( |
| 154 | - this.keys.index, undefined | |
| 154 | + this.keys.index, {} | |
| 155 | 155 | ); |
| 156 | - if(!this.index){ | |
| 157 | - /*check for and remove/replace older name. This whole block | |
| 158 | - can be removed once the test phase is done (don't want to | |
| 159 | - invalidate the testers' edits on the test server). When | |
| 160 | - doing so, be sure to replace undefined in the above | |
| 161 | - getJSON() call with {}. */ | |
| 162 | - const oldName = F.page.name+':index'; | |
| 163 | - this.index = F.storage.getJSON(oldName,undefined); | |
| 164 | - if(this.index){ | |
| 165 | - F.storage.remove(oldName); | |
| 166 | - this.storeIndex(); | |
| 167 | - }else{ | |
| 168 | - this.index = {}; | |
| 169 | - } | |
| 170 | - } | |
| 171 | 156 | } |
| 172 | 157 | return this.index; |
| 173 | 158 | }, |
| 174 | 159 | _fireStashEvent: function(){ |
| 175 | 160 | if(this._disableNextEvent) delete this._disableNextEvent; |
| @@ -300,30 +285,44 @@ | ||
| 300 | 285 | this.e.selectCi, |
| 301 | 286 | this.e.selectFiles |
| 302 | 287 | ),"Loading leaves..."); |
| 303 | 288 | D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); |
| 304 | 289 | const self = this; |
| 305 | - F.fetch('fileedit/filelist',{ | |
| 306 | - urlParams:'leaves', | |
| 307 | - responseType: 'json', | |
| 308 | - onload: function(list){ | |
| 309 | - D.append(D.clearElement(self.e.ciListLabel), | |
| 310 | - "Open leaves (newest first):"); | |
| 311 | - self.cache.checkins = list; | |
| 312 | - D.clearElement(D.enable(self.e.selectCi)); | |
| 313 | - let loadThisOne; | |
| 314 | - list.forEach(function(o,n){ | |
| 315 | - if(!n) loadThisOne = o; | |
| 316 | - self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; | |
| 317 | - D.option(self.e.selectCi, o.checkin, | |
| 318 | - o.timestamp+' ['+o.branch+']: ' | |
| 319 | - +F.hashDigits(o.checkin)); | |
| 320 | - }); | |
| 321 | - F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); | |
| 322 | - self.loadFiles(loadThisOne ? loadThisOne.checkin : false); | |
| 323 | - } | |
| 324 | - }); | |
| 290 | + const onload = function(list){ | |
| 291 | + D.append(D.clearElement(self.e.ciListLabel), | |
| 292 | + "Open leaves (newest first):"); | |
| 293 | + self.cache.checkins = list; | |
| 294 | + D.clearElement(D.enable(self.e.selectCi)); | |
| 295 | + let loadThisOne = P.initialFiles/*possibly injected at page-load time*/; | |
| 296 | + if(loadThisOne){ | |
| 297 | + self.cache.files[loadThisOne.checkin] = loadThisOne; | |
| 298 | + delete P.initialFiles; | |
| 299 | + } | |
| 300 | + list.forEach(function(o,n){ | |
| 301 | + if(!n && !loadThisOne) loadThisOne = o; | |
| 302 | + self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; | |
| 303 | + D.option(self.e.selectCi, o.checkin, | |
| 304 | + o.timestamp+' ['+o.branch+']: ' | |
| 305 | + +F.hashDigits(o.checkin)); | |
| 306 | + }); | |
| 307 | + F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); | |
| 308 | + if(loadThisOne){ | |
| 309 | + self.e.selectCi.value = loadThisOne.checkin; | |
| 310 | + } | |
| 311 | + self.loadFiles(loadThisOne ? loadThisOne.checkin : false); | |
| 312 | + }; | |
| 313 | + if(P.initialLeaves/*injected at page-load time.*/){ | |
| 314 | + const lv = P.initialLeaves; | |
| 315 | + delete P.initialLeaves; | |
| 316 | + onload(lv); | |
| 317 | + }else{ | |
| 318 | + F.fetch('fileedit/filelist',{ | |
| 319 | + urlParams:'leaves', | |
| 320 | + responseType: 'json', | |
| 321 | + onload: onload | |
| 322 | + }); | |
| 323 | + } | |
| 325 | 324 | }, |
| 326 | 325 | /** |
| 327 | 326 | Loads the file list for the given checkin UUID. It uses a |
| 328 | 327 | cached copy on subsequent calls for the same UUID. If passed a |
| 329 | 328 | falsy value, it instead clears and disables the file selection |
| @@ -387,20 +386,20 @@ | ||
| 387 | 386 | Initializes the checkin/file selector widget. Must only be |
| 388 | 387 | called once. |
| 389 | 388 | */ |
| 390 | 389 | init: function(){ |
| 391 | 390 | this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); |
| 392 | - const selCi = this.e.selectCi = D.select(), | |
| 391 | + const selCi = this.e.selectCi = D.addClass(D.select(), 'flex-grow'), | |
| 393 | 392 | selFiles = this.e.selectFiles |
| 394 | 393 | = D.addClass(D.select(), 'file-list'), |
| 395 | 394 | btnLoad = this.e.btnLoadFile = |
| 396 | 395 | D.addClass(D.button("Load file"), "flex-shrink"), |
| 397 | 396 | filesLabel = this.e.fileListLabel = |
| 398 | 397 | D.addClass(D.div(),'flex-shrink','file-list-label'), |
| 399 | 398 | ciLabelWrapper = D.addClass( |
| 400 | 399 | D.div(), 'flex-container','flex-row', 'flex-shrink', |
| 401 | - 'stretch' | |
| 400 | + 'stretch', 'child-gap-small' | |
| 402 | 401 | ), |
| 403 | 402 | btnReload = D.addClass( |
| 404 | 403 | D.button('Reload'), 'flex-shrink' |
| 405 | 404 | ), |
| 406 | 405 | ciLabel = this.e.ciListLabel = |
| @@ -410,34 +409,47 @@ | ||
| 410 | 409 | D.attr(selFiles, 'title', |
| 411 | 410 | "The list of editable files for the selected checkin."); |
| 412 | 411 | D.attr(btnLoad, 'title', |
| 413 | 412 | "Load the selected file into the editor."); |
| 414 | 413 | D.disable(selCi, selFiles, btnLoad); |
| 415 | - D.attr(selFiles, 'size', 10); | |
| 414 | + D.attr(selFiles, 'size', 12); | |
| 416 | 415 | D.append( |
| 417 | 416 | this.e.container, |
| 417 | + ciLabel, | |
| 418 | 418 | D.append(ciLabelWrapper, |
| 419 | - btnReload, ciLabel), | |
| 420 | - selCi, | |
| 419 | + selCi, | |
| 420 | + btnReload), | |
| 421 | 421 | filesLabel, |
| 422 | 422 | selFiles, |
| 423 | 423 | /* Use a wrapper for btnLoad so that the button itself does not |
| 424 | 424 | stretch to fill the parent width: */ |
| 425 | 425 | D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad) |
| 426 | 426 | ); |
| 427 | + if(F.config['fileedit-glob']){ | |
| 428 | + D.append( | |
| 429 | + this.e.container, | |
| 430 | + D.append( | |
| 431 | + D.span(), | |
| 432 | + D.append(D.code(),"fileedit-glob"), | |
| 433 | + " config setting = ", | |
| 434 | + D.append(D.code(), JSON.stringify(F.config['fileedit-glob'])) | |
| 435 | + ) | |
| 436 | + ); | |
| 437 | + } | |
| 438 | + | |
| 427 | 439 | this.loadLeaves(); |
| 428 | 440 | selCi.addEventListener( |
| 429 | 441 | 'change', (e)=>this.loadFiles(e.target.value), false |
| 430 | 442 | ); |
| 431 | - btnLoad.addEventListener( | |
| 432 | - 'click', (e)=>{ | |
| 433 | - this.finfo.filename = selFiles.value; | |
| 434 | - if(this.finfo.filename){ | |
| 435 | - P.loadFile(this.finfo.filename, this.finfo.checkin); | |
| 436 | - } | |
| 437 | - }, false | |
| 438 | - ); | |
| 443 | + const doLoad = (e)=>{ | |
| 444 | + this.finfo.filename = selFiles.value; | |
| 445 | + if(this.finfo.filename){ | |
| 446 | + P.loadFile(this.finfo.filename, this.finfo.checkin); | |
| 447 | + } | |
| 448 | + }; | |
| 449 | + btnLoad.addEventListener('click', doLoad, false); | |
| 450 | + selFiles.addEventListener('dblclick', doLoad, false); | |
| 439 | 451 | btnReload.addEventListener( |
| 440 | 452 | 'click', (e)=>this.loadLeaves(), false |
| 441 | 453 | ); |
| 442 | 454 | delete this.init; |
| 443 | 455 | } |
| @@ -453,11 +465,11 @@ | ||
| 453 | 465 | D.attr(D.div(),'id','fileedit-stash-selector'), |
| 454 | 466 | 'input-with-label' |
| 455 | 467 | ); |
| 456 | 468 | const sel = this.e.select = D.select(); |
| 457 | 469 | const btnClear = this.e.btnClear |
| 458 | - = D.addClass(D.button("Clear"),'hidden'); | |
| 470 | + = D.button("Discard Edits"); | |
| 459 | 471 | D.append(wrapper, "Local edits (", |
| 460 | 472 | D.append(D.code(), |
| 461 | 473 | F.storage.storageImplName()), |
| 462 | 474 | "):", |
| 463 | 475 | sel, btnClear); |
| @@ -472,23 +484,34 @@ | ||
| 472 | 484 | F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail)); |
| 473 | 485 | sel.addEventListener('change',function(e){ |
| 474 | 486 | const opt = this.selectedOptions[0]; |
| 475 | 487 | if(opt && opt._finfo) P.loadFile(opt._finfo); |
| 476 | 488 | }); |
| 477 | - F.confirmer(btnClear, { | |
| 478 | - confirmText: "REALLY delete ALL local edits?", | |
| 479 | - onconfirm: (e)=>P.clearStash().loadFile(/*in case P.finfo() was in the stash*/), | |
| 480 | - ticks: 3 | |
| 481 | - }); | |
| 482 | 489 | if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ |
| 483 | 490 | D.append(wrapper, D.append( |
| 484 | 491 | D.addClass(D.span(),'warning'), |
| 485 | 492 | "Warning: persistent storage is not available, "+ |
| 486 | 493 | "so uncomitted edits will not survive a page reload." |
| 487 | 494 | )); |
| 488 | 495 | } |
| 489 | 496 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 497 | + F.confirmer(btnClear, { | |
| 498 | + /* must come after insertion into the DOM for the pinSize option to work. */ | |
| 499 | + pinSize: true, | |
| 500 | + confirmText: "DISCARD all local edits?", | |
| 501 | + onconfirm: function(e){ | |
| 502 | + if(P.finfo){ | |
| 503 | + const stashed = P.getStashedFinfo(P.finfo); | |
| 504 | + P.clearStash(); | |
| 505 | + if(stashed) P.loadFile(/*reload after discarding edits*/); | |
| 506 | + }else{ | |
| 507 | + P.clearStash(); | |
| 508 | + } | |
| 509 | + }, | |
| 510 | + ticks: F.config.confirmerButtonTicks | |
| 511 | + }); | |
| 512 | + D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); | |
| 490 | 513 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 491 | 514 | delete this.init; |
| 492 | 515 | }, |
| 493 | 516 | /** |
| 494 | 517 | Regenerates the edit selection list. |
| @@ -521,20 +544,20 @@ | ||
| 521 | 544 | return; |
| 522 | 545 | } |
| 523 | 546 | D.enable(this.e.select); |
| 524 | 547 | D.removeClass(this.e.btnClear, 'hidden'); |
| 525 | 548 | D.disable(D.option(this.e.select,0,"Select a local edit...")); |
| 526 | - const currentFinfo = theFinfo || P.finfo || {}; | |
| 549 | + const currentFinfo = theFinfo || P.finfo || {filename:''}; | |
| 527 | 550 | ilist.sort(f.compare).forEach(function(finfo,n){ |
| 528 | 551 | const key = stasher.indexKey(finfo), |
| 529 | 552 | branch = finfo.branch |
| 530 | 553 | || P.fileSelectWidget.checkinBranchName(finfo.checkin)||''; |
| 531 | 554 | /* Remember that we don't know the branch name for non-leaf versions |
| 532 | 555 | which P.fileSelectWidget() has never seen/cached. */ |
| 533 | 556 | const opt = D.option( |
| 534 | 557 | self.e.select, n+1/*value is (almost) irrelevant*/, |
| 535 | - [F.hashDigits(finfo.checkin, 6), ' [',branch||'?branch?','] ', | |
| 558 | + [F.hashDigits(finfo.checkin), ' [',branch||'?branch?','] ', | |
| 536 | 559 | f.timestring(new Date(finfo.stashTime)),' ', |
| 537 | 560 | false ? finfo.filename : F.shortenFilename(finfo.filename) |
| 538 | 561 | ].join('') |
| 539 | 562 | ); |
| 540 | 563 | opt._finfo = finfo; |
| @@ -637,11 +660,11 @@ | ||
| 637 | 660 | previewTarget: E('#fileedit-tab-preview-wrapper'), |
| 638 | 661 | manifestTarget: E('#fileedit-manifest'), |
| 639 | 662 | diffTarget: E('#fileedit-tab-diff-wrapper'), |
| 640 | 663 | cbIsExe: E('input[type=checkbox][name=exec_bit]'), |
| 641 | 664 | cbManifest: E('input[type=checkbox][name=include_manifest]'), |
| 642 | - fsFileVersionDetails: E('#file-version-details'), | |
| 665 | + editStatus: E('#fileedit-edit-status'), | |
| 643 | 666 | tabs:{ |
| 644 | 667 | content: E('#fileedit-tab-content'), |
| 645 | 668 | preview: E('#fileedit-tab-preview'), |
| 646 | 669 | diff: E('#fileedit-tab-diff'), |
| 647 | 670 | commit: E('#fileedit-tab-commit'), |
| @@ -659,24 +682,24 @@ | ||
| 659 | 682 | }else{ |
| 660 | 683 | P.e.taComment = P.e.taCommentSmall; |
| 661 | 684 | D.addClass(P.e.taCommentBig, 'hidden'); |
| 662 | 685 | } |
| 663 | 686 | D.removeClass(P.e.taComment, 'hidden'); |
| 664 | - | |
| 665 | 687 | P.tabs.e.container.insertBefore( |
| 666 | 688 | /* Move the status bar between the tab buttons and |
| 667 | 689 | tab panels. Seems to be the best fit in terms of |
| 668 | 690 | functionality and visibility. */ |
| 669 | 691 | E('#fossil-status-bar'), P.tabs.e.tabs |
| 670 | 692 | ); |
| 693 | + P.tabs.e.container.insertBefore(P.e.editStatus, P.tabs.e.tabs); | |
| 671 | 694 | |
| 672 | 695 | P.tabs.addEventListener( |
| 673 | 696 | /* Set up auto-refresh of the preview tab... */ |
| 674 | 697 | 'before-switch-to', function(ev){ |
| 675 | 698 | if(ev.detail===P.e.tabs.preview){ |
| 676 | 699 | P.baseHrefForFile(); |
| 677 | - if(P.e.cbAutoPreview.checked) P.preview(); | |
| 700 | + if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); | |
| 678 | 701 | }else if(ev.detail===P.e.tabs.diff){ |
| 679 | 702 | /* Work around a weird bug where the page gets wider than |
| 680 | 703 | the window when the diff tab is NOT in view and the |
| 681 | 704 | current SBS diff widget is wider than the window. When |
| 682 | 705 | the diff IS in view then CSS overflow magically reduces |
| @@ -715,13 +738,14 @@ | ||
| 715 | 738 | ); |
| 716 | 739 | P.e.btnCommit.addEventListener( |
| 717 | 740 | "click",(e)=>P.commit(), false |
| 718 | 741 | ); |
| 719 | 742 | F.confirmer(P.e.btnReload, { |
| 743 | + pinSize: true, | |
| 720 | 744 | confirmText: "Really reload, losing edits?", |
| 721 | 745 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 722 | - ticks: 3 | |
| 746 | + ticks: F.config.confirmerButtonTicks | |
| 723 | 747 | }); |
| 724 | 748 | E('#comment-toggle').addEventListener( |
| 725 | 749 | "click",(e)=>P.toggleCommentMode(), false |
| 726 | 750 | ); |
| 727 | 751 | |
| @@ -774,11 +798,14 @@ | ||
| 774 | 798 | } |
| 775 | 799 | |
| 776 | 800 | P.addEventListener( |
| 777 | 801 | // Clear certain views when new content is loaded/set |
| 778 | 802 | 'fileedit-content-replaced', |
| 779 | - ()=>D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget) | |
| 803 | + ()=>{ | |
| 804 | + P.previewNeedsUpdate = true; | |
| 805 | + D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget); | |
| 806 | + } | |
| 780 | 807 | ); |
| 781 | 808 | P.addEventListener( |
| 782 | 809 | // Clear certain views after a non-dry-run commit |
| 783 | 810 | 'fileedit-committed', |
| 784 | 811 | (e)=>{ |
| @@ -789,11 +816,10 @@ | ||
| 789 | 816 | ); |
| 790 | 817 | |
| 791 | 818 | P.fileSelectWidget.init(); |
| 792 | 819 | P.stashWidget.init( |
| 793 | 820 | P.e.tabs.content.lastElementChild |
| 794 | - //P.e.tabs.fileSelect.querySelector("h1") | |
| 795 | 821 | ); |
| 796 | 822 | }/*F.onPageLoad()*/); |
| 797 | 823 | |
| 798 | 824 | /** |
| 799 | 825 | Getter (if called with no args) or setter (if passed an arg) for |
| @@ -819,11 +845,11 @@ | ||
| 819 | 845 | For use when installing a custom editor widget. Pass it the |
| 820 | 846 | getter and setter callbacks to fetch resp. set the content of the |
| 821 | 847 | custom widget. They will be triggered via |
| 822 | 848 | P.fileContent(). Returns this object. |
| 823 | 849 | */ |
| 824 | - P.setFileContentMethods = function(getter, setter){ | |
| 850 | + P.setContentMethods = function(getter, setter){ | |
| 825 | 851 | this.fileContent.get = getter; |
| 826 | 852 | this.fileContent.set = setter; |
| 827 | 853 | return this; |
| 828 | 854 | }; |
| 829 | 855 | |
| @@ -929,63 +955,55 @@ | ||
| 929 | 955 | updateVersion() updates the filename and version in various UI |
| 930 | 956 | elements... |
| 931 | 957 | |
| 932 | 958 | Returns this object. |
| 933 | 959 | */ |
| 934 | - P.updateVersion = function(file,rev){ | |
| 960 | + P.updateVersion = function f(file,rev){ | |
| 961 | + if(!f.eLinks){ | |
| 962 | + f.eName = P.e.editStatus.querySelector('span.name'); | |
| 963 | + f.eLinks = P.e.editStatus.querySelector('span.links'); | |
| 964 | + } | |
| 935 | 965 | if(1===arguments.length){/*assume object*/ |
| 936 | 966 | this.finfo = arguments[0]; |
| 937 | 967 | file = this.finfo.filename; |
| 938 | 968 | rev = this.finfo.checkin; |
| 939 | 969 | }else if(0===arguments.length){ |
| 940 | - if(!affirmHasFile()) return this; | |
| 941 | - file = this.finfo.filename; | |
| 942 | - rev = this.finfo.checkin; | |
| 970 | + if(affirmHasFile()){ | |
| 971 | + file = this.finfo.filename; | |
| 972 | + rev = this.finfo.checkin; | |
| 973 | + } | |
| 943 | 974 | }else{ |
| 944 | 975 | this.finfo = {filename:file,checkin:rev}; |
| 945 | 976 | } |
| 946 | - const eTgt = this.e.fsFileVersionDetails.querySelector('div'), | |
| 947 | - rHuman = F.hashDigits(rev), | |
| 948 | - rUrl = F.hashDigits(rev,true); | |
| 949 | - D.clearElement(eTgt); | |
| 950 | - D.append( | |
| 951 | - eTgt, "File: ", | |
| 952 | - D.append(D.code(), | |
| 953 | - D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)), | |
| 954 | - D.br() | |
| 955 | - ); | |
| 956 | - D.append( | |
| 957 | - eTgt, "Checkin: ", | |
| 958 | - D.append(D.code(), D.a(F.repoUrl('info/'+rUrl), rHuman)), | |
| 959 | - " [",D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"),"]", | |
| 960 | - D.br() | |
| 961 | - ); | |
| 962 | - D.append( | |
| 963 | - eTgt, "Mimetype: ", | |
| 964 | - D.append(D.code(), this.finfo.mimetype||'???'), | |
| 965 | - D.br() | |
| 966 | - ); | |
| 967 | - D.append( | |
| 968 | - eTgt, | |
| 969 | - D.append(D.code(), "[", | |
| 970 | - D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}), | |
| 971 | - 'annotate'), "]"), | |
| 972 | - D.append(D.code(), "[", | |
| 973 | - D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}), | |
| 974 | - 'blame'), "]") | |
| 977 | + const fi = this.finfo; | |
| 978 | + D.clearElement(f.eName, f.eLinks); | |
| 979 | + if(!fi){ | |
| 980 | + D.append(f.eName, '(no file loaded)'); | |
| 981 | + return this; | |
| 982 | + } | |
| 983 | + const rHuman = F.hashDigits(rev), | |
| 984 | + rUrl = F.hashDigits(rev,true); | |
| 985 | + | |
| 986 | + //TODO? port over is-edited marker from /wikiedit | |
| 987 | + //var marker = getEditMarker(wi, false); | |
| 988 | + D.append(f.eName/*,marker*/,D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)); | |
| 989 | + | |
| 990 | + D.append( | |
| 991 | + f.eLinks, | |
| 992 | + D.append(D.span(), fi.mimetype||'?mimetype?'), | |
| 993 | + D.a(F.repoUrl('info/'+rUrl), rHuman), | |
| 994 | + D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"), | |
| 995 | + D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}),'annotate'), | |
| 996 | + D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}),'blame') | |
| 975 | 997 | ); |
| 976 | 998 | const purlArgs = F.encodeUrlArgs({ |
| 977 | 999 | filename: this.finfo.filename, |
| 978 | 1000 | checkin: rUrl |
| 979 | 1001 | },false,true); |
| 980 | 1002 | const purl = F.repoUrl('fileedit',purlArgs); |
| 981 | - D.append( | |
| 982 | - eTgt, | |
| 983 | - D.append(D.code(), | |
| 984 | - "[",D.a(purl,"Editor permalink"),"]") | |
| 985 | - ); | |
| 986 | - this.setPageTitle("Edit: "+this.finfo.filename); | |
| 1003 | + D.append( f.eLinks, D.a(purl,"editor permalink") ); | |
| 1004 | + this.setPageTitle("Edit: "+fi.filename); | |
| 987 | 1005 | return this; |
| 988 | 1006 | }; |
| 989 | 1007 | |
| 990 | 1008 | /** |
| 991 | 1009 | loadFile() loads (file,checkinVersion) and updates the relevant |
| @@ -1032,10 +1050,11 @@ | ||
| 1032 | 1050 | mimetype: headers['content-type'].split(';').shift() |
| 1033 | 1051 | }); |
| 1034 | 1052 | self.tabs.switchToTab(self.e.tabs.content); |
| 1035 | 1053 | self.e.cbIsExe.checked = self.finfo.isExe; |
| 1036 | 1054 | self.fileContent(r); |
| 1055 | + P.previewNeedsUpdate = true; | |
| 1037 | 1056 | self.dispatchEvent('fileedit-file-loaded', self.finfo); |
| 1038 | 1057 | }; |
| 1039 | 1058 | const semiFinfo = {filename: file, checkin: rev}; |
| 1040 | 1059 | const stashFinfo = this.getStashedFinfo(semiFinfo); |
| 1041 | 1060 | if(stashFinfo){ // fake a response from the stash... |
| @@ -1115,10 +1134,11 @@ | ||
| 1115 | 1134 | P.selectPreviewMode(P.previewModes[header]); |
| 1116 | 1135 | if('wiki'===header) P.baseHrefForFile(); |
| 1117 | 1136 | else P.baseHrefRestore(); |
| 1118 | 1137 | callback(r); |
| 1119 | 1138 | F.message('Updated preview.'); |
| 1139 | + P.previewNeedsUpdate = false; | |
| 1120 | 1140 | P.dispatchEvent('fileedit-preview-updated',{ |
| 1121 | 1141 | previewMode: P.previewModes.current, |
| 1122 | 1142 | mimetype: P.finfo.mimetype, |
| 1123 | 1143 | element: P.e.previewTarget |
| 1124 | 1144 | }); |
| @@ -1290,10 +1310,11 @@ | ||
| 1290 | 1310 | }else{ |
| 1291 | 1311 | $stash.updateFile(fi, P.fileContent()); |
| 1292 | 1312 | } |
| 1293 | 1313 | F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); |
| 1294 | 1314 | $stash.prune(); |
| 1315 | + this.previewNeedsUpdate = true; | |
| 1295 | 1316 | } |
| 1296 | 1317 | return this; |
| 1297 | 1318 | }; |
| 1298 | 1319 | |
| 1299 | 1320 | /** |
| @@ -1301,10 +1322,11 @@ | ||
| 1301 | 1322 | F.storage. Returns this. |
| 1302 | 1323 | */ |
| 1303 | 1324 | P.unstashContent = function(){ |
| 1304 | 1325 | const finfo = arguments[0] || this.finfo; |
| 1305 | 1326 | if(finfo){ |
| 1327 | + this.previewNeedsUpdate = true; | |
| 1306 | 1328 | $stash.unstash(finfo); |
| 1307 | 1329 | //console.debug("Unstashed",finfo); |
| 1308 | 1330 | F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); |
| 1309 | 1331 | } |
| 1310 | 1332 | return this; |
| 1311 | 1333 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -121,11 +121,11 @@ | |
| 121 | and that would be horribly inefficient (meaning "battery-consuming" |
| 122 | on mobile devices). |
| 123 | */ |
| 124 | const $stash = { |
| 125 | keys: { |
| 126 | index: F.page.name+'/index' |
| 127 | }, |
| 128 | /** |
| 129 | index: { |
| 130 | "CHECKIN_HASH:FILENAME": {file info w/o content} |
| 131 | ... |
| @@ -149,27 +149,12 @@ | |
| 149 | /** Returns the index object, fetching it from the stash or creating |
| 150 | it anew on the first call. */ |
| 151 | getIndex: function(){ |
| 152 | if(!this.index){ |
| 153 | this.index = F.storage.getJSON( |
| 154 | this.keys.index, undefined |
| 155 | ); |
| 156 | if(!this.index){ |
| 157 | /*check for and remove/replace older name. This whole block |
| 158 | can be removed once the test phase is done (don't want to |
| 159 | invalidate the testers' edits on the test server). When |
| 160 | doing so, be sure to replace undefined in the above |
| 161 | getJSON() call with {}. */ |
| 162 | const oldName = F.page.name+':index'; |
| 163 | this.index = F.storage.getJSON(oldName,undefined); |
| 164 | if(this.index){ |
| 165 | F.storage.remove(oldName); |
| 166 | this.storeIndex(); |
| 167 | }else{ |
| 168 | this.index = {}; |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | return this.index; |
| 173 | }, |
| 174 | _fireStashEvent: function(){ |
| 175 | if(this._disableNextEvent) delete this._disableNextEvent; |
| @@ -300,30 +285,44 @@ | |
| 300 | this.e.selectCi, |
| 301 | this.e.selectFiles |
| 302 | ),"Loading leaves..."); |
| 303 | D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); |
| 304 | const self = this; |
| 305 | F.fetch('fileedit/filelist',{ |
| 306 | urlParams:'leaves', |
| 307 | responseType: 'json', |
| 308 | onload: function(list){ |
| 309 | D.append(D.clearElement(self.e.ciListLabel), |
| 310 | "Open leaves (newest first):"); |
| 311 | self.cache.checkins = list; |
| 312 | D.clearElement(D.enable(self.e.selectCi)); |
| 313 | let loadThisOne; |
| 314 | list.forEach(function(o,n){ |
| 315 | if(!n) loadThisOne = o; |
| 316 | self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; |
| 317 | D.option(self.e.selectCi, o.checkin, |
| 318 | o.timestamp+' ['+o.branch+']: ' |
| 319 | +F.hashDigits(o.checkin)); |
| 320 | }); |
| 321 | F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); |
| 322 | self.loadFiles(loadThisOne ? loadThisOne.checkin : false); |
| 323 | } |
| 324 | }); |
| 325 | }, |
| 326 | /** |
| 327 | Loads the file list for the given checkin UUID. It uses a |
| 328 | cached copy on subsequent calls for the same UUID. If passed a |
| 329 | falsy value, it instead clears and disables the file selection |
| @@ -387,20 +386,20 @@ | |
| 387 | Initializes the checkin/file selector widget. Must only be |
| 388 | called once. |
| 389 | */ |
| 390 | init: function(){ |
| 391 | this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); |
| 392 | const selCi = this.e.selectCi = D.select(), |
| 393 | selFiles = this.e.selectFiles |
| 394 | = D.addClass(D.select(), 'file-list'), |
| 395 | btnLoad = this.e.btnLoadFile = |
| 396 | D.addClass(D.button("Load file"), "flex-shrink"), |
| 397 | filesLabel = this.e.fileListLabel = |
| 398 | D.addClass(D.div(),'flex-shrink','file-list-label'), |
| 399 | ciLabelWrapper = D.addClass( |
| 400 | D.div(), 'flex-container','flex-row', 'flex-shrink', |
| 401 | 'stretch' |
| 402 | ), |
| 403 | btnReload = D.addClass( |
| 404 | D.button('Reload'), 'flex-shrink' |
| 405 | ), |
| 406 | ciLabel = this.e.ciListLabel = |
| @@ -410,34 +409,47 @@ | |
| 410 | D.attr(selFiles, 'title', |
| 411 | "The list of editable files for the selected checkin."); |
| 412 | D.attr(btnLoad, 'title', |
| 413 | "Load the selected file into the editor."); |
| 414 | D.disable(selCi, selFiles, btnLoad); |
| 415 | D.attr(selFiles, 'size', 10); |
| 416 | D.append( |
| 417 | this.e.container, |
| 418 | D.append(ciLabelWrapper, |
| 419 | btnReload, ciLabel), |
| 420 | selCi, |
| 421 | filesLabel, |
| 422 | selFiles, |
| 423 | /* Use a wrapper for btnLoad so that the button itself does not |
| 424 | stretch to fill the parent width: */ |
| 425 | D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad) |
| 426 | ); |
| 427 | this.loadLeaves(); |
| 428 | selCi.addEventListener( |
| 429 | 'change', (e)=>this.loadFiles(e.target.value), false |
| 430 | ); |
| 431 | btnLoad.addEventListener( |
| 432 | 'click', (e)=>{ |
| 433 | this.finfo.filename = selFiles.value; |
| 434 | if(this.finfo.filename){ |
| 435 | P.loadFile(this.finfo.filename, this.finfo.checkin); |
| 436 | } |
| 437 | }, false |
| 438 | ); |
| 439 | btnReload.addEventListener( |
| 440 | 'click', (e)=>this.loadLeaves(), false |
| 441 | ); |
| 442 | delete this.init; |
| 443 | } |
| @@ -453,11 +465,11 @@ | |
| 453 | D.attr(D.div(),'id','fileedit-stash-selector'), |
| 454 | 'input-with-label' |
| 455 | ); |
| 456 | const sel = this.e.select = D.select(); |
| 457 | const btnClear = this.e.btnClear |
| 458 | = D.addClass(D.button("Clear"),'hidden'); |
| 459 | D.append(wrapper, "Local edits (", |
| 460 | D.append(D.code(), |
| 461 | F.storage.storageImplName()), |
| 462 | "):", |
| 463 | sel, btnClear); |
| @@ -472,23 +484,34 @@ | |
| 472 | F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail)); |
| 473 | sel.addEventListener('change',function(e){ |
| 474 | const opt = this.selectedOptions[0]; |
| 475 | if(opt && opt._finfo) P.loadFile(opt._finfo); |
| 476 | }); |
| 477 | F.confirmer(btnClear, { |
| 478 | confirmText: "REALLY delete ALL local edits?", |
| 479 | onconfirm: (e)=>P.clearStash().loadFile(/*in case P.finfo() was in the stash*/), |
| 480 | ticks: 3 |
| 481 | }); |
| 482 | if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ |
| 483 | D.append(wrapper, D.append( |
| 484 | D.addClass(D.span(),'warning'), |
| 485 | "Warning: persistent storage is not available, "+ |
| 486 | "so uncomitted edits will not survive a page reload." |
| 487 | )); |
| 488 | } |
| 489 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 490 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 491 | delete this.init; |
| 492 | }, |
| 493 | /** |
| 494 | Regenerates the edit selection list. |
| @@ -521,20 +544,20 @@ | |
| 521 | return; |
| 522 | } |
| 523 | D.enable(this.e.select); |
| 524 | D.removeClass(this.e.btnClear, 'hidden'); |
| 525 | D.disable(D.option(this.e.select,0,"Select a local edit...")); |
| 526 | const currentFinfo = theFinfo || P.finfo || {}; |
| 527 | ilist.sort(f.compare).forEach(function(finfo,n){ |
| 528 | const key = stasher.indexKey(finfo), |
| 529 | branch = finfo.branch |
| 530 | || P.fileSelectWidget.checkinBranchName(finfo.checkin)||''; |
| 531 | /* Remember that we don't know the branch name for non-leaf versions |
| 532 | which P.fileSelectWidget() has never seen/cached. */ |
| 533 | const opt = D.option( |
| 534 | self.e.select, n+1/*value is (almost) irrelevant*/, |
| 535 | [F.hashDigits(finfo.checkin, 6), ' [',branch||'?branch?','] ', |
| 536 | f.timestring(new Date(finfo.stashTime)),' ', |
| 537 | false ? finfo.filename : F.shortenFilename(finfo.filename) |
| 538 | ].join('') |
| 539 | ); |
| 540 | opt._finfo = finfo; |
| @@ -637,11 +660,11 @@ | |
| 637 | previewTarget: E('#fileedit-tab-preview-wrapper'), |
| 638 | manifestTarget: E('#fileedit-manifest'), |
| 639 | diffTarget: E('#fileedit-tab-diff-wrapper'), |
| 640 | cbIsExe: E('input[type=checkbox][name=exec_bit]'), |
| 641 | cbManifest: E('input[type=checkbox][name=include_manifest]'), |
| 642 | fsFileVersionDetails: E('#file-version-details'), |
| 643 | tabs:{ |
| 644 | content: E('#fileedit-tab-content'), |
| 645 | preview: E('#fileedit-tab-preview'), |
| 646 | diff: E('#fileedit-tab-diff'), |
| 647 | commit: E('#fileedit-tab-commit'), |
| @@ -659,24 +682,24 @@ | |
| 659 | }else{ |
| 660 | P.e.taComment = P.e.taCommentSmall; |
| 661 | D.addClass(P.e.taCommentBig, 'hidden'); |
| 662 | } |
| 663 | D.removeClass(P.e.taComment, 'hidden'); |
| 664 | |
| 665 | P.tabs.e.container.insertBefore( |
| 666 | /* Move the status bar between the tab buttons and |
| 667 | tab panels. Seems to be the best fit in terms of |
| 668 | functionality and visibility. */ |
| 669 | E('#fossil-status-bar'), P.tabs.e.tabs |
| 670 | ); |
| 671 | |
| 672 | P.tabs.addEventListener( |
| 673 | /* Set up auto-refresh of the preview tab... */ |
| 674 | 'before-switch-to', function(ev){ |
| 675 | if(ev.detail===P.e.tabs.preview){ |
| 676 | P.baseHrefForFile(); |
| 677 | if(P.e.cbAutoPreview.checked) P.preview(); |
| 678 | }else if(ev.detail===P.e.tabs.diff){ |
| 679 | /* Work around a weird bug where the page gets wider than |
| 680 | the window when the diff tab is NOT in view and the |
| 681 | current SBS diff widget is wider than the window. When |
| 682 | the diff IS in view then CSS overflow magically reduces |
| @@ -715,13 +738,14 @@ | |
| 715 | ); |
| 716 | P.e.btnCommit.addEventListener( |
| 717 | "click",(e)=>P.commit(), false |
| 718 | ); |
| 719 | F.confirmer(P.e.btnReload, { |
| 720 | confirmText: "Really reload, losing edits?", |
| 721 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 722 | ticks: 3 |
| 723 | }); |
| 724 | E('#comment-toggle').addEventListener( |
| 725 | "click",(e)=>P.toggleCommentMode(), false |
| 726 | ); |
| 727 | |
| @@ -774,11 +798,14 @@ | |
| 774 | } |
| 775 | |
| 776 | P.addEventListener( |
| 777 | // Clear certain views when new content is loaded/set |
| 778 | 'fileedit-content-replaced', |
| 779 | ()=>D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget) |
| 780 | ); |
| 781 | P.addEventListener( |
| 782 | // Clear certain views after a non-dry-run commit |
| 783 | 'fileedit-committed', |
| 784 | (e)=>{ |
| @@ -789,11 +816,10 @@ | |
| 789 | ); |
| 790 | |
| 791 | P.fileSelectWidget.init(); |
| 792 | P.stashWidget.init( |
| 793 | P.e.tabs.content.lastElementChild |
| 794 | //P.e.tabs.fileSelect.querySelector("h1") |
| 795 | ); |
| 796 | }/*F.onPageLoad()*/); |
| 797 | |
| 798 | /** |
| 799 | Getter (if called with no args) or setter (if passed an arg) for |
| @@ -819,11 +845,11 @@ | |
| 819 | For use when installing a custom editor widget. Pass it the |
| 820 | getter and setter callbacks to fetch resp. set the content of the |
| 821 | custom widget. They will be triggered via |
| 822 | P.fileContent(). Returns this object. |
| 823 | */ |
| 824 | P.setFileContentMethods = function(getter, setter){ |
| 825 | this.fileContent.get = getter; |
| 826 | this.fileContent.set = setter; |
| 827 | return this; |
| 828 | }; |
| 829 | |
| @@ -929,63 +955,55 @@ | |
| 929 | updateVersion() updates the filename and version in various UI |
| 930 | elements... |
| 931 | |
| 932 | Returns this object. |
| 933 | */ |
| 934 | P.updateVersion = function(file,rev){ |
| 935 | if(1===arguments.length){/*assume object*/ |
| 936 | this.finfo = arguments[0]; |
| 937 | file = this.finfo.filename; |
| 938 | rev = this.finfo.checkin; |
| 939 | }else if(0===arguments.length){ |
| 940 | if(!affirmHasFile()) return this; |
| 941 | file = this.finfo.filename; |
| 942 | rev = this.finfo.checkin; |
| 943 | }else{ |
| 944 | this.finfo = {filename:file,checkin:rev}; |
| 945 | } |
| 946 | const eTgt = this.e.fsFileVersionDetails.querySelector('div'), |
| 947 | rHuman = F.hashDigits(rev), |
| 948 | rUrl = F.hashDigits(rev,true); |
| 949 | D.clearElement(eTgt); |
| 950 | D.append( |
| 951 | eTgt, "File: ", |
| 952 | D.append(D.code(), |
| 953 | D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)), |
| 954 | D.br() |
| 955 | ); |
| 956 | D.append( |
| 957 | eTgt, "Checkin: ", |
| 958 | D.append(D.code(), D.a(F.repoUrl('info/'+rUrl), rHuman)), |
| 959 | " [",D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"),"]", |
| 960 | D.br() |
| 961 | ); |
| 962 | D.append( |
| 963 | eTgt, "Mimetype: ", |
| 964 | D.append(D.code(), this.finfo.mimetype||'???'), |
| 965 | D.br() |
| 966 | ); |
| 967 | D.append( |
| 968 | eTgt, |
| 969 | D.append(D.code(), "[", |
| 970 | D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}), |
| 971 | 'annotate'), "]"), |
| 972 | D.append(D.code(), "[", |
| 973 | D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}), |
| 974 | 'blame'), "]") |
| 975 | ); |
| 976 | const purlArgs = F.encodeUrlArgs({ |
| 977 | filename: this.finfo.filename, |
| 978 | checkin: rUrl |
| 979 | },false,true); |
| 980 | const purl = F.repoUrl('fileedit',purlArgs); |
| 981 | D.append( |
| 982 | eTgt, |
| 983 | D.append(D.code(), |
| 984 | "[",D.a(purl,"Editor permalink"),"]") |
| 985 | ); |
| 986 | this.setPageTitle("Edit: "+this.finfo.filename); |
| 987 | return this; |
| 988 | }; |
| 989 | |
| 990 | /** |
| 991 | loadFile() loads (file,checkinVersion) and updates the relevant |
| @@ -1032,10 +1050,11 @@ | |
| 1032 | mimetype: headers['content-type'].split(';').shift() |
| 1033 | }); |
| 1034 | self.tabs.switchToTab(self.e.tabs.content); |
| 1035 | self.e.cbIsExe.checked = self.finfo.isExe; |
| 1036 | self.fileContent(r); |
| 1037 | self.dispatchEvent('fileedit-file-loaded', self.finfo); |
| 1038 | }; |
| 1039 | const semiFinfo = {filename: file, checkin: rev}; |
| 1040 | const stashFinfo = this.getStashedFinfo(semiFinfo); |
| 1041 | if(stashFinfo){ // fake a response from the stash... |
| @@ -1115,10 +1134,11 @@ | |
| 1115 | P.selectPreviewMode(P.previewModes[header]); |
| 1116 | if('wiki'===header) P.baseHrefForFile(); |
| 1117 | else P.baseHrefRestore(); |
| 1118 | callback(r); |
| 1119 | F.message('Updated preview.'); |
| 1120 | P.dispatchEvent('fileedit-preview-updated',{ |
| 1121 | previewMode: P.previewModes.current, |
| 1122 | mimetype: P.finfo.mimetype, |
| 1123 | element: P.e.previewTarget |
| 1124 | }); |
| @@ -1290,10 +1310,11 @@ | |
| 1290 | }else{ |
| 1291 | $stash.updateFile(fi, P.fileContent()); |
| 1292 | } |
| 1293 | F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); |
| 1294 | $stash.prune(); |
| 1295 | } |
| 1296 | return this; |
| 1297 | }; |
| 1298 | |
| 1299 | /** |
| @@ -1301,10 +1322,11 @@ | |
| 1301 | F.storage. Returns this. |
| 1302 | */ |
| 1303 | P.unstashContent = function(){ |
| 1304 | const finfo = arguments[0] || this.finfo; |
| 1305 | if(finfo){ |
| 1306 | $stash.unstash(finfo); |
| 1307 | //console.debug("Unstashed",finfo); |
| 1308 | F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); |
| 1309 | } |
| 1310 | return this; |
| 1311 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -121,11 +121,11 @@ | |
| 121 | and that would be horribly inefficient (meaning "battery-consuming" |
| 122 | on mobile devices). |
| 123 | */ |
| 124 | const $stash = { |
| 125 | keys: { |
| 126 | index: F.page.name+'.index' |
| 127 | }, |
| 128 | /** |
| 129 | index: { |
| 130 | "CHECKIN_HASH:FILENAME": {file info w/o content} |
| 131 | ... |
| @@ -149,27 +149,12 @@ | |
| 149 | /** Returns the index object, fetching it from the stash or creating |
| 150 | it anew on the first call. */ |
| 151 | getIndex: function(){ |
| 152 | if(!this.index){ |
| 153 | this.index = F.storage.getJSON( |
| 154 | this.keys.index, {} |
| 155 | ); |
| 156 | } |
| 157 | return this.index; |
| 158 | }, |
| 159 | _fireStashEvent: function(){ |
| 160 | if(this._disableNextEvent) delete this._disableNextEvent; |
| @@ -300,30 +285,44 @@ | |
| 285 | this.e.selectCi, |
| 286 | this.e.selectFiles |
| 287 | ),"Loading leaves..."); |
| 288 | D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); |
| 289 | const self = this; |
| 290 | const onload = function(list){ |
| 291 | D.append(D.clearElement(self.e.ciListLabel), |
| 292 | "Open leaves (newest first):"); |
| 293 | self.cache.checkins = list; |
| 294 | D.clearElement(D.enable(self.e.selectCi)); |
| 295 | let loadThisOne = P.initialFiles/*possibly injected at page-load time*/; |
| 296 | if(loadThisOne){ |
| 297 | self.cache.files[loadThisOne.checkin] = loadThisOne; |
| 298 | delete P.initialFiles; |
| 299 | } |
| 300 | list.forEach(function(o,n){ |
| 301 | if(!n && !loadThisOne) loadThisOne = o; |
| 302 | self.cache.branchNames[F.hashDigits(o.checkin,true)] = o.branch; |
| 303 | D.option(self.e.selectCi, o.checkin, |
| 304 | o.timestamp+' ['+o.branch+']: ' |
| 305 | +F.hashDigits(o.checkin)); |
| 306 | }); |
| 307 | F.storage.setJSON(self.cache.branchKey, self.cache.branchNames); |
| 308 | if(loadThisOne){ |
| 309 | self.e.selectCi.value = loadThisOne.checkin; |
| 310 | } |
| 311 | self.loadFiles(loadThisOne ? loadThisOne.checkin : false); |
| 312 | }; |
| 313 | if(P.initialLeaves/*injected at page-load time.*/){ |
| 314 | const lv = P.initialLeaves; |
| 315 | delete P.initialLeaves; |
| 316 | onload(lv); |
| 317 | }else{ |
| 318 | F.fetch('fileedit/filelist',{ |
| 319 | urlParams:'leaves', |
| 320 | responseType: 'json', |
| 321 | onload: onload |
| 322 | }); |
| 323 | } |
| 324 | }, |
| 325 | /** |
| 326 | Loads the file list for the given checkin UUID. It uses a |
| 327 | cached copy on subsequent calls for the same UUID. If passed a |
| 328 | falsy value, it instead clears and disables the file selection |
| @@ -387,20 +386,20 @@ | |
| 386 | Initializes the checkin/file selector widget. Must only be |
| 387 | called once. |
| 388 | */ |
| 389 | init: function(){ |
| 390 | this.cache.branchNames = F.storage.getJSON(this.cache.branchKey, {}); |
| 391 | const selCi = this.e.selectCi = D.addClass(D.select(), 'flex-grow'), |
| 392 | selFiles = this.e.selectFiles |
| 393 | = D.addClass(D.select(), 'file-list'), |
| 394 | btnLoad = this.e.btnLoadFile = |
| 395 | D.addClass(D.button("Load file"), "flex-shrink"), |
| 396 | filesLabel = this.e.fileListLabel = |
| 397 | D.addClass(D.div(),'flex-shrink','file-list-label'), |
| 398 | ciLabelWrapper = D.addClass( |
| 399 | D.div(), 'flex-container','flex-row', 'flex-shrink', |
| 400 | 'stretch', 'child-gap-small' |
| 401 | ), |
| 402 | btnReload = D.addClass( |
| 403 | D.button('Reload'), 'flex-shrink' |
| 404 | ), |
| 405 | ciLabel = this.e.ciListLabel = |
| @@ -410,34 +409,47 @@ | |
| 409 | D.attr(selFiles, 'title', |
| 410 | "The list of editable files for the selected checkin."); |
| 411 | D.attr(btnLoad, 'title', |
| 412 | "Load the selected file into the editor."); |
| 413 | D.disable(selCi, selFiles, btnLoad); |
| 414 | D.attr(selFiles, 'size', 12); |
| 415 | D.append( |
| 416 | this.e.container, |
| 417 | ciLabel, |
| 418 | D.append(ciLabelWrapper, |
| 419 | selCi, |
| 420 | btnReload), |
| 421 | filesLabel, |
| 422 | selFiles, |
| 423 | /* Use a wrapper for btnLoad so that the button itself does not |
| 424 | stretch to fill the parent width: */ |
| 425 | D.append(D.addClass(D.div(), 'flex-shrink'), btnLoad) |
| 426 | ); |
| 427 | if(F.config['fileedit-glob']){ |
| 428 | D.append( |
| 429 | this.e.container, |
| 430 | D.append( |
| 431 | D.span(), |
| 432 | D.append(D.code(),"fileedit-glob"), |
| 433 | " config setting = ", |
| 434 | D.append(D.code(), JSON.stringify(F.config['fileedit-glob'])) |
| 435 | ) |
| 436 | ); |
| 437 | } |
| 438 | |
| 439 | this.loadLeaves(); |
| 440 | selCi.addEventListener( |
| 441 | 'change', (e)=>this.loadFiles(e.target.value), false |
| 442 | ); |
| 443 | const doLoad = (e)=>{ |
| 444 | this.finfo.filename = selFiles.value; |
| 445 | if(this.finfo.filename){ |
| 446 | P.loadFile(this.finfo.filename, this.finfo.checkin); |
| 447 | } |
| 448 | }; |
| 449 | btnLoad.addEventListener('click', doLoad, false); |
| 450 | selFiles.addEventListener('dblclick', doLoad, false); |
| 451 | btnReload.addEventListener( |
| 452 | 'click', (e)=>this.loadLeaves(), false |
| 453 | ); |
| 454 | delete this.init; |
| 455 | } |
| @@ -453,11 +465,11 @@ | |
| 465 | D.attr(D.div(),'id','fileedit-stash-selector'), |
| 466 | 'input-with-label' |
| 467 | ); |
| 468 | const sel = this.e.select = D.select(); |
| 469 | const btnClear = this.e.btnClear |
| 470 | = D.button("Discard Edits"); |
| 471 | D.append(wrapper, "Local edits (", |
| 472 | D.append(D.code(), |
| 473 | F.storage.storageImplName()), |
| 474 | "):", |
| 475 | sel, btnClear); |
| @@ -472,23 +484,34 @@ | |
| 484 | F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail)); |
| 485 | sel.addEventListener('change',function(e){ |
| 486 | const opt = this.selectedOptions[0]; |
| 487 | if(opt && opt._finfo) P.loadFile(opt._finfo); |
| 488 | }); |
| 489 | if(F.storage.isTransient()){/*Warn if our storage is particularly transient...*/ |
| 490 | D.append(wrapper, D.append( |
| 491 | D.addClass(D.span(),'warning'), |
| 492 | "Warning: persistent storage is not available, "+ |
| 493 | "so uncomitted edits will not survive a page reload." |
| 494 | )); |
| 495 | } |
| 496 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 497 | F.confirmer(btnClear, { |
| 498 | /* must come after insertion into the DOM for the pinSize option to work. */ |
| 499 | pinSize: true, |
| 500 | confirmText: "DISCARD all local edits?", |
| 501 | onconfirm: function(e){ |
| 502 | if(P.finfo){ |
| 503 | const stashed = P.getStashedFinfo(P.finfo); |
| 504 | P.clearStash(); |
| 505 | if(stashed) P.loadFile(/*reload after discarding edits*/); |
| 506 | }else{ |
| 507 | P.clearStash(); |
| 508 | } |
| 509 | }, |
| 510 | ticks: F.config.confirmerButtonTicks |
| 511 | }); |
| 512 | D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); |
| 513 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 514 | delete this.init; |
| 515 | }, |
| 516 | /** |
| 517 | Regenerates the edit selection list. |
| @@ -521,20 +544,20 @@ | |
| 544 | return; |
| 545 | } |
| 546 | D.enable(this.e.select); |
| 547 | D.removeClass(this.e.btnClear, 'hidden'); |
| 548 | D.disable(D.option(this.e.select,0,"Select a local edit...")); |
| 549 | const currentFinfo = theFinfo || P.finfo || {filename:''}; |
| 550 | ilist.sort(f.compare).forEach(function(finfo,n){ |
| 551 | const key = stasher.indexKey(finfo), |
| 552 | branch = finfo.branch |
| 553 | || P.fileSelectWidget.checkinBranchName(finfo.checkin)||''; |
| 554 | /* Remember that we don't know the branch name for non-leaf versions |
| 555 | which P.fileSelectWidget() has never seen/cached. */ |
| 556 | const opt = D.option( |
| 557 | self.e.select, n+1/*value is (almost) irrelevant*/, |
| 558 | [F.hashDigits(finfo.checkin), ' [',branch||'?branch?','] ', |
| 559 | f.timestring(new Date(finfo.stashTime)),' ', |
| 560 | false ? finfo.filename : F.shortenFilename(finfo.filename) |
| 561 | ].join('') |
| 562 | ); |
| 563 | opt._finfo = finfo; |
| @@ -637,11 +660,11 @@ | |
| 660 | previewTarget: E('#fileedit-tab-preview-wrapper'), |
| 661 | manifestTarget: E('#fileedit-manifest'), |
| 662 | diffTarget: E('#fileedit-tab-diff-wrapper'), |
| 663 | cbIsExe: E('input[type=checkbox][name=exec_bit]'), |
| 664 | cbManifest: E('input[type=checkbox][name=include_manifest]'), |
| 665 | editStatus: E('#fileedit-edit-status'), |
| 666 | tabs:{ |
| 667 | content: E('#fileedit-tab-content'), |
| 668 | preview: E('#fileedit-tab-preview'), |
| 669 | diff: E('#fileedit-tab-diff'), |
| 670 | commit: E('#fileedit-tab-commit'), |
| @@ -659,24 +682,24 @@ | |
| 682 | }else{ |
| 683 | P.e.taComment = P.e.taCommentSmall; |
| 684 | D.addClass(P.e.taCommentBig, 'hidden'); |
| 685 | } |
| 686 | D.removeClass(P.e.taComment, 'hidden'); |
| 687 | P.tabs.e.container.insertBefore( |
| 688 | /* Move the status bar between the tab buttons and |
| 689 | tab panels. Seems to be the best fit in terms of |
| 690 | functionality and visibility. */ |
| 691 | E('#fossil-status-bar'), P.tabs.e.tabs |
| 692 | ); |
| 693 | P.tabs.e.container.insertBefore(P.e.editStatus, P.tabs.e.tabs); |
| 694 | |
| 695 | P.tabs.addEventListener( |
| 696 | /* Set up auto-refresh of the preview tab... */ |
| 697 | 'before-switch-to', function(ev){ |
| 698 | if(ev.detail===P.e.tabs.preview){ |
| 699 | P.baseHrefForFile(); |
| 700 | if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); |
| 701 | }else if(ev.detail===P.e.tabs.diff){ |
| 702 | /* Work around a weird bug where the page gets wider than |
| 703 | the window when the diff tab is NOT in view and the |
| 704 | current SBS diff widget is wider than the window. When |
| 705 | the diff IS in view then CSS overflow magically reduces |
| @@ -715,13 +738,14 @@ | |
| 738 | ); |
| 739 | P.e.btnCommit.addEventListener( |
| 740 | "click",(e)=>P.commit(), false |
| 741 | ); |
| 742 | F.confirmer(P.e.btnReload, { |
| 743 | pinSize: true, |
| 744 | confirmText: "Really reload, losing edits?", |
| 745 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 746 | ticks: F.config.confirmerButtonTicks |
| 747 | }); |
| 748 | E('#comment-toggle').addEventListener( |
| 749 | "click",(e)=>P.toggleCommentMode(), false |
| 750 | ); |
| 751 | |
| @@ -774,11 +798,14 @@ | |
| 798 | } |
| 799 | |
| 800 | P.addEventListener( |
| 801 | // Clear certain views when new content is loaded/set |
| 802 | 'fileedit-content-replaced', |
| 803 | ()=>{ |
| 804 | P.previewNeedsUpdate = true; |
| 805 | D.clearElement(P.e.diffTarget, P.e.previewTarget, P.e.manifestTarget); |
| 806 | } |
| 807 | ); |
| 808 | P.addEventListener( |
| 809 | // Clear certain views after a non-dry-run commit |
| 810 | 'fileedit-committed', |
| 811 | (e)=>{ |
| @@ -789,11 +816,10 @@ | |
| 816 | ); |
| 817 | |
| 818 | P.fileSelectWidget.init(); |
| 819 | P.stashWidget.init( |
| 820 | P.e.tabs.content.lastElementChild |
| 821 | ); |
| 822 | }/*F.onPageLoad()*/); |
| 823 | |
| 824 | /** |
| 825 | Getter (if called with no args) or setter (if passed an arg) for |
| @@ -819,11 +845,11 @@ | |
| 845 | For use when installing a custom editor widget. Pass it the |
| 846 | getter and setter callbacks to fetch resp. set the content of the |
| 847 | custom widget. They will be triggered via |
| 848 | P.fileContent(). Returns this object. |
| 849 | */ |
| 850 | P.setContentMethods = function(getter, setter){ |
| 851 | this.fileContent.get = getter; |
| 852 | this.fileContent.set = setter; |
| 853 | return this; |
| 854 | }; |
| 855 | |
| @@ -929,63 +955,55 @@ | |
| 955 | updateVersion() updates the filename and version in various UI |
| 956 | elements... |
| 957 | |
| 958 | Returns this object. |
| 959 | */ |
| 960 | P.updateVersion = function f(file,rev){ |
| 961 | if(!f.eLinks){ |
| 962 | f.eName = P.e.editStatus.querySelector('span.name'); |
| 963 | f.eLinks = P.e.editStatus.querySelector('span.links'); |
| 964 | } |
| 965 | if(1===arguments.length){/*assume object*/ |
| 966 | this.finfo = arguments[0]; |
| 967 | file = this.finfo.filename; |
| 968 | rev = this.finfo.checkin; |
| 969 | }else if(0===arguments.length){ |
| 970 | if(affirmHasFile()){ |
| 971 | file = this.finfo.filename; |
| 972 | rev = this.finfo.checkin; |
| 973 | } |
| 974 | }else{ |
| 975 | this.finfo = {filename:file,checkin:rev}; |
| 976 | } |
| 977 | const fi = this.finfo; |
| 978 | D.clearElement(f.eName, f.eLinks); |
| 979 | if(!fi){ |
| 980 | D.append(f.eName, '(no file loaded)'); |
| 981 | return this; |
| 982 | } |
| 983 | const rHuman = F.hashDigits(rev), |
| 984 | rUrl = F.hashDigits(rev,true); |
| 985 | |
| 986 | //TODO? port over is-edited marker from /wikiedit |
| 987 | //var marker = getEditMarker(wi, false); |
| 988 | D.append(f.eName/*,marker*/,D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)); |
| 989 | |
| 990 | D.append( |
| 991 | f.eLinks, |
| 992 | D.append(D.span(), fi.mimetype||'?mimetype?'), |
| 993 | D.a(F.repoUrl('info/'+rUrl), rHuman), |
| 994 | D.a(F.repoUrl('timeline',{m:rUrl}), "timeline"), |
| 995 | D.a(F.repoUrl('annotate',{filename:file, checkin:rUrl}),'annotate'), |
| 996 | D.a(F.repoUrl('blame',{filename:file, checkin:rUrl}),'blame') |
| 997 | ); |
| 998 | const purlArgs = F.encodeUrlArgs({ |
| 999 | filename: this.finfo.filename, |
| 1000 | checkin: rUrl |
| 1001 | },false,true); |
| 1002 | const purl = F.repoUrl('fileedit',purlArgs); |
| 1003 | D.append( f.eLinks, D.a(purl,"editor permalink") ); |
| 1004 | this.setPageTitle("Edit: "+fi.filename); |
| 1005 | return this; |
| 1006 | }; |
| 1007 | |
| 1008 | /** |
| 1009 | loadFile() loads (file,checkinVersion) and updates the relevant |
| @@ -1032,10 +1050,11 @@ | |
| 1050 | mimetype: headers['content-type'].split(';').shift() |
| 1051 | }); |
| 1052 | self.tabs.switchToTab(self.e.tabs.content); |
| 1053 | self.e.cbIsExe.checked = self.finfo.isExe; |
| 1054 | self.fileContent(r); |
| 1055 | P.previewNeedsUpdate = true; |
| 1056 | self.dispatchEvent('fileedit-file-loaded', self.finfo); |
| 1057 | }; |
| 1058 | const semiFinfo = {filename: file, checkin: rev}; |
| 1059 | const stashFinfo = this.getStashedFinfo(semiFinfo); |
| 1060 | if(stashFinfo){ // fake a response from the stash... |
| @@ -1115,10 +1134,11 @@ | |
| 1134 | P.selectPreviewMode(P.previewModes[header]); |
| 1135 | if('wiki'===header) P.baseHrefForFile(); |
| 1136 | else P.baseHrefRestore(); |
| 1137 | callback(r); |
| 1138 | F.message('Updated preview.'); |
| 1139 | P.previewNeedsUpdate = false; |
| 1140 | P.dispatchEvent('fileedit-preview-updated',{ |
| 1141 | previewMode: P.previewModes.current, |
| 1142 | mimetype: P.finfo.mimetype, |
| 1143 | element: P.e.previewTarget |
| 1144 | }); |
| @@ -1290,10 +1310,11 @@ | |
| 1310 | }else{ |
| 1311 | $stash.updateFile(fi, P.fileContent()); |
| 1312 | } |
| 1313 | F.message("Stashed change to",F.hashDigits(fi.checkin),fi.filename); |
| 1314 | $stash.prune(); |
| 1315 | this.previewNeedsUpdate = true; |
| 1316 | } |
| 1317 | return this; |
| 1318 | }; |
| 1319 | |
| 1320 | /** |
| @@ -1301,10 +1322,11 @@ | |
| 1322 | F.storage. Returns this. |
| 1323 | */ |
| 1324 | P.unstashContent = function(){ |
| 1325 | const finfo = arguments[0] || this.finfo; |
| 1326 | if(finfo){ |
| 1327 | this.previewNeedsUpdate = true; |
| 1328 | $stash.unstash(finfo); |
| 1329 | //console.debug("Unstashed",finfo); |
| 1330 | F.message("Unstashed",F.hashDigits(finfo.checkin),finfo.filename); |
| 1331 | } |
| 1332 | return this; |
| 1333 |
+46
-10
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -11,26 +11,64 @@ | ||
| 11 | 11 | return function(ev){ |
| 12 | 12 | if(ev) ev.preventDefault(); |
| 13 | 13 | const wasExpanded = widget.classList.contains('expanded'); |
| 14 | 14 | widget.classList.toggle('expanded'); |
| 15 | 15 | contentElem.classList.toggle('expanded'); |
| 16 | - if(wasExpanded) widget.scrollIntoView(); | |
| 16 | + if(wasExpanded){ | |
| 17 | + contentElem.classList.add('shrunken'); | |
| 18 | + contentElem.parentElement.scrollIntoView({ | |
| 19 | + /* This is non-standard, but !(MSIE, Safari) supposedly support it: | |
| 20 | + https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility | |
| 21 | + */ behavior: 'smooth' | |
| 22 | + }); | |
| 23 | + }else{ | |
| 24 | + contentElem.classList.remove('shrunken'); | |
| 25 | + } | |
| 17 | 26 | return false; |
| 18 | 27 | }; |
| 19 | 28 | }; |
| 29 | + | |
| 20 | 30 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 21 | 31 | elements which are deemed "too large" (those for which |
| 22 | 32 | scrolling is currently activated because they are taller than |
| 23 | 33 | their max-height). */ |
| 24 | 34 | document.querySelectorAll( |
| 25 | 35 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 26 | - ).forEach(function(forumPostWrapper){ | |
| 36 | + ).forEach(function f(forumPostWrapper){ | |
| 27 | 37 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 28 | 38 | if(!content || !scrollbarIsVisible(content)) return; |
| 29 | - const widget = D.div(), | |
| 30 | - widgetEventHandler = getWidgetHandler(widget, content); | |
| 31 | - widget.classList.add('forum-post-collapser'); | |
| 39 | + const parent = content.parentElement, | |
| 40 | + widget = D.addClass( | |
| 41 | + D.div(), | |
| 42 | + 'forum-post-collapser','bottom' | |
| 43 | + ), | |
| 44 | + rightTapZone = D.addClass( | |
| 45 | + D.div(), | |
| 46 | + 'forum-post-collapser','right' | |
| 47 | + ); | |
| 48 | + /* Repopulates the rightTapZone with arrow indicators. Because | |
| 49 | + of the wildly varying height of these elements, This has to | |
| 50 | + be done dynamically at init time and upon collapse/expand. Will not | |
| 51 | + work until the rightTapZone has been added to the DOM. */ | |
| 52 | + const refillTapZone = function f(){ | |
| 53 | + if(!f.baseTapIndicatorHeight){ | |
| 54 | + /* To figure out how often to place an arrow in the rightTapZone, | |
| 55 | + we simply grab the first header element from the page and use | |
| 56 | + its hight as our basis for calculation. */ | |
| 57 | + const h1 = document.querySelector('h1, h2'); | |
| 58 | + f.baseTapIndicatorHeight = h1.getBoundingClientRect().height; | |
| 59 | + } | |
| 60 | + D.clearElement(rightTapZone); | |
| 61 | + var rtzHeight = parseInt(window.getComputedStyle(rightTapZone).height); | |
| 62 | + do { | |
| 63 | + D.append(rightTapZone, D.span()); | |
| 64 | + rtzHeight -= f.baseTapIndicatorHeight * 8; | |
| 65 | + }while(rtzHeight>0); | |
| 66 | + }; | |
| 67 | + const handlerStep1 = getWidgetHandler(widget, content); | |
| 68 | + const widgetEventHandler = ()=>{ handlerStep1(); refillTapZone(); }; | |
| 69 | + content.classList.add('with-expander'); | |
| 32 | 70 | widget.addEventListener('click', widgetEventHandler, false); |
| 33 | 71 | /** Append 3 children, which CSS will evenly space across the |
| 34 | 72 | widget. This improves visibility over having the label |
| 35 | 73 | in only the left, right, or center. */ |
| 36 | 74 | var i = 0; |
| @@ -38,14 +76,12 @@ | ||
| 38 | 76 | if(content.nextSibling){ |
| 39 | 77 | forumPostWrapper.insertBefore(widget, content.nextSibling); |
| 40 | 78 | }else{ |
| 41 | 79 | forumPostWrapper.appendChild(widget); |
| 42 | 80 | } |
| 43 | - /** A double-click toggle will select "the current word" on the | |
| 44 | - post, which is minorly annoying but otherwise harmless. Such | |
| 45 | - a toggle has proven convenient on "excessive" posts, | |
| 46 | - though. */ | |
| 47 | - content.addEventListener('dblclick', widgetEventHandler); | |
| 81 | + content.appendChild(rightTapZone); | |
| 82 | + rightTapZone.addEventListener('click', widgetEventHandler, false); | |
| 83 | + refillTapZone(); | |
| 48 | 84 | }); |
| 49 | 85 | })/*onload callback*/; |
| 50 | 86 | |
| 51 | 87 | })(window.fossil); |
| 52 | 88 | |
| 53 | 89 | ADDED src/fossil.page.wikiedit.js |
| 54 | 90 | ADDED src/fossil.popupwidget.js |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -11,26 +11,64 @@ | |
| 11 | return function(ev){ |
| 12 | if(ev) ev.preventDefault(); |
| 13 | const wasExpanded = widget.classList.contains('expanded'); |
| 14 | widget.classList.toggle('expanded'); |
| 15 | contentElem.classList.toggle('expanded'); |
| 16 | if(wasExpanded) widget.scrollIntoView(); |
| 17 | return false; |
| 18 | }; |
| 19 | }; |
| 20 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 21 | elements which are deemed "too large" (those for which |
| 22 | scrolling is currently activated because they are taller than |
| 23 | their max-height). */ |
| 24 | document.querySelectorAll( |
| 25 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 26 | ).forEach(function(forumPostWrapper){ |
| 27 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 28 | if(!content || !scrollbarIsVisible(content)) return; |
| 29 | const widget = D.div(), |
| 30 | widgetEventHandler = getWidgetHandler(widget, content); |
| 31 | widget.classList.add('forum-post-collapser'); |
| 32 | widget.addEventListener('click', widgetEventHandler, false); |
| 33 | /** Append 3 children, which CSS will evenly space across the |
| 34 | widget. This improves visibility over having the label |
| 35 | in only the left, right, or center. */ |
| 36 | var i = 0; |
| @@ -38,14 +76,12 @@ | |
| 38 | if(content.nextSibling){ |
| 39 | forumPostWrapper.insertBefore(widget, content.nextSibling); |
| 40 | }else{ |
| 41 | forumPostWrapper.appendChild(widget); |
| 42 | } |
| 43 | /** A double-click toggle will select "the current word" on the |
| 44 | post, which is minorly annoying but otherwise harmless. Such |
| 45 | a toggle has proven convenient on "excessive" posts, |
| 46 | though. */ |
| 47 | content.addEventListener('dblclick', widgetEventHandler); |
| 48 | }); |
| 49 | })/*onload callback*/; |
| 50 | |
| 51 | })(window.fossil); |
| 52 | |
| 53 | DDED src/fossil.page.wikiedit.js |
| 54 | DDED src/fossil.popupwidget.js |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -11,26 +11,64 @@ | |
| 11 | return function(ev){ |
| 12 | if(ev) ev.preventDefault(); |
| 13 | const wasExpanded = widget.classList.contains('expanded'); |
| 14 | widget.classList.toggle('expanded'); |
| 15 | contentElem.classList.toggle('expanded'); |
| 16 | if(wasExpanded){ |
| 17 | contentElem.classList.add('shrunken'); |
| 18 | contentElem.parentElement.scrollIntoView({ |
| 19 | /* This is non-standard, but !(MSIE, Safari) supposedly support it: |
| 20 | https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#Browser_compatibility |
| 21 | */ behavior: 'smooth' |
| 22 | }); |
| 23 | }else{ |
| 24 | contentElem.classList.remove('shrunken'); |
| 25 | } |
| 26 | return false; |
| 27 | }; |
| 28 | }; |
| 29 | |
| 30 | /* Adds an Expand/Collapse toggle to all div.forumPostBody |
| 31 | elements which are deemed "too large" (those for which |
| 32 | scrolling is currently activated because they are taller than |
| 33 | their max-height). */ |
| 34 | document.querySelectorAll( |
| 35 | 'div.forumHier, div.forumTime, div.forumHierRoot' |
| 36 | ).forEach(function f(forumPostWrapper){ |
| 37 | const content = forumPostWrapper.querySelector('div.forumPostBody'); |
| 38 | if(!content || !scrollbarIsVisible(content)) return; |
| 39 | const parent = content.parentElement, |
| 40 | widget = D.addClass( |
| 41 | D.div(), |
| 42 | 'forum-post-collapser','bottom' |
| 43 | ), |
| 44 | rightTapZone = D.addClass( |
| 45 | D.div(), |
| 46 | 'forum-post-collapser','right' |
| 47 | ); |
| 48 | /* Repopulates the rightTapZone with arrow indicators. Because |
| 49 | of the wildly varying height of these elements, This has to |
| 50 | be done dynamically at init time and upon collapse/expand. Will not |
| 51 | work until the rightTapZone has been added to the DOM. */ |
| 52 | const refillTapZone = function f(){ |
| 53 | if(!f.baseTapIndicatorHeight){ |
| 54 | /* To figure out how often to place an arrow in the rightTapZone, |
| 55 | we simply grab the first header element from the page and use |
| 56 | its hight as our basis for calculation. */ |
| 57 | const h1 = document.querySelector('h1, h2'); |
| 58 | f.baseTapIndicatorHeight = h1.getBoundingClientRect().height; |
| 59 | } |
| 60 | D.clearElement(rightTapZone); |
| 61 | var rtzHeight = parseInt(window.getComputedStyle(rightTapZone).height); |
| 62 | do { |
| 63 | D.append(rightTapZone, D.span()); |
| 64 | rtzHeight -= f.baseTapIndicatorHeight * 8; |
| 65 | }while(rtzHeight>0); |
| 66 | }; |
| 67 | const handlerStep1 = getWidgetHandler(widget, content); |
| 68 | const widgetEventHandler = ()=>{ handlerStep1(); refillTapZone(); }; |
| 69 | content.classList.add('with-expander'); |
| 70 | widget.addEventListener('click', widgetEventHandler, false); |
| 71 | /** Append 3 children, which CSS will evenly space across the |
| 72 | widget. This improves visibility over having the label |
| 73 | in only the left, right, or center. */ |
| 74 | var i = 0; |
| @@ -38,14 +76,12 @@ | |
| 76 | if(content.nextSibling){ |
| 77 | forumPostWrapper.insertBefore(widget, content.nextSibling); |
| 78 | }else{ |
| 79 | forumPostWrapper.appendChild(widget); |
| 80 | } |
| 81 | content.appendChild(rightTapZone); |
| 82 | rightTapZone.addEventListener('click', widgetEventHandler, false); |
| 83 | refillTapZone(); |
| 84 | }); |
| 85 | })/*onload callback*/; |
| 86 | |
| 87 | })(window.fossil); |
| 88 | |
| 89 | DDED src/fossil.page.wikiedit.js |
| 90 | DDED src/fossil.popupwidget.js |
| --- a/src/fossil.page.wikiedit.js | ||
| +++ b/src/fossil.page.wikiedit.js | ||
| @@ -0,0 +1,43 @@ | ||
| 1 | +notag" |(function(F/*ttarget.innerHTML =for a sandbox page or new page, | |
| 2 | + parent: parent UUID string or null if no parent, | |
| 3 | + isEmpty: true if page has no content (is "dele ctrnormal" | "tag" |(function(F/*the fossil object*/){ | |
| 4 | + "use strict"; | |
| 5 | + /** | |
| 6 | + Clienuires that | |
| 7 | + the fossil JS bootstrapping is complete and that several fossil | |
| 8 | + JS APIs have been installed: fossil.fetch, fossil.dom, | |
| 9 | + fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. | |
| 10 | + | |
| 11 | + Custom events which can be listened for via | |
| 12 | + fossil.page.addEventListener(): | |
| 13 | + | |
| 14 | + - Event 'wiki-page-loaded': passes on information when it | |
| 15 | + loads a wiki (whether from the network or its internal local-edit | |
| 16 | + cache), in the form of an "winfo" objec | |
| 17 | + 'change',n information when it | |
| 18 | + loads a wiki (whether from the network or its internal local-edit | |
| 19 | + cache), in the form of an "winfo" objecnot'localStorage'),' uses browD.append(D.code(),'sessionStorage'),' uses storage local to this browser tab.'update > input[type=checkbox], 'title);cb; | |
| 20 | + constD.attr(wrapper, "title", [ | |
| 21 | +', | |
| 22 | + 'Only themost recent pages', | |
| 23 | +' | |
| 24 | + ].join(' ')); | |
| 25 | +D.attr(title' 'Save changes and Pfunction(F/*ttargenotag" |(function(F/*ttarget.innerHTML =for a sandbox page or new page, | |
| 26 | + parent: parent UUID string or null if no parent, | |
| 27 | + isEmpty: true if page has no content (is "dele ctrnormal" | "tag" |(function(F/*the fossil object*/){ | |
| 28 | + "use strict"; | |
| 29 | + /** | |
| 30 | + Client-side implementation of the /wikiedit app. Requires that | |
| 31 | + the fossil JS bootstrapping is complete and that several fossil | |
| 32 | + JS APIs have been installed: fossil.fetch, fossil.dom, | |
| 33 | + fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. | |
| 34 | + | |
| 35 | + Custom events which can be listened for via | |
| 36 | + fossil.page.addEventListener(): | |
| 37 | + | |
| 38 | + - Event 'wiki-page-loaded': passes on information when it | |
| 39 | + loads a wiki (whether from the network or its internal local-edit | |
| 40 | + cache), in the form of an "winfo" objec | |
| 41 | + 'change',n information when it | |
| 42 | + loads a wiki (whether from the network or its internal local-edit | |
| 43 | + cache), in the form of an "winfo" objecnot'localStorage'),' uses */fossilfossil |
| --- a/src/fossil.page.wikiedit.js | |
| +++ b/src/fossil.page.wikiedit.js | |
| @@ -0,0 +1,43 @@ | |
| --- a/src/fossil.page.wikiedit.js | |
| +++ b/src/fossil.page.wikiedit.js | |
| @@ -0,0 +1,43 @@ | |
| 1 | notag" |(function(F/*ttarget.innerHTML =for a sandbox page or new page, |
| 2 | parent: parent UUID string or null if no parent, |
| 3 | isEmpty: true if page has no content (is "dele ctrnormal" | "tag" |(function(F/*the fossil object*/){ |
| 4 | "use strict"; |
| 5 | /** |
| 6 | Clienuires that |
| 7 | the fossil JS bootstrapping is complete and that several fossil |
| 8 | JS APIs have been installed: fossil.fetch, fossil.dom, |
| 9 | fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. |
| 10 | |
| 11 | Custom events which can be listened for via |
| 12 | fossil.page.addEventListener(): |
| 13 | |
| 14 | - Event 'wiki-page-loaded': passes on information when it |
| 15 | loads a wiki (whether from the network or its internal local-edit |
| 16 | cache), in the form of an "winfo" objec |
| 17 | 'change',n information when it |
| 18 | loads a wiki (whether from the network or its internal local-edit |
| 19 | cache), in the form of an "winfo" objecnot'localStorage'),' uses browD.append(D.code(),'sessionStorage'),' uses storage local to this browser tab.'update > input[type=checkbox], 'title);cb; |
| 20 | constD.attr(wrapper, "title", [ |
| 21 | ', |
| 22 | 'Only themost recent pages', |
| 23 | ' |
| 24 | ].join(' ')); |
| 25 | D.attr(title' 'Save changes and Pfunction(F/*ttargenotag" |(function(F/*ttarget.innerHTML =for a sandbox page or new page, |
| 26 | parent: parent UUID string or null if no parent, |
| 27 | isEmpty: true if page has no content (is "dele ctrnormal" | "tag" |(function(F/*the fossil object*/){ |
| 28 | "use strict"; |
| 29 | /** |
| 30 | Client-side implementation of the /wikiedit app. Requires that |
| 31 | the fossil JS bootstrapping is complete and that several fossil |
| 32 | JS APIs have been installed: fossil.fetch, fossil.dom, |
| 33 | fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. |
| 34 | |
| 35 | Custom events which can be listened for via |
| 36 | fossil.page.addEventListener(): |
| 37 | |
| 38 | - Event 'wiki-page-loaded': passes on information when it |
| 39 | loads a wiki (whether from the network or its internal local-edit |
| 40 | cache), in the form of an "winfo" objec |
| 41 | 'change',n information when it |
| 42 | loads a wiki (whether from the network or its internal local-edit |
| 43 | cache), in the form of an "winfo" objecnot'localStorage'),' uses */fossilfossil |
| --- a/src/fossil.popupwidget.js | ||
| +++ b/src/fossil.popupwidget.js | ||
| @@ -0,0 +1,65 @@ | ||
| 1 | +(function(F/*fossil object*/){ | |
| 2 | + 30 */ | |
| 3 | + installClickToHiden or basic user intera(function(F/*fossil object*/){ | |
| 4 | + 3000sClass = cssClass; | |
| 5 | + unct}, true);isplay basic information ClickToHide(nction(F/*fossil object*/fossil object*/){ | |
| 6 | + 3000sClass = cssClass; | |
| 7 | + unction(F/*fossil object*/){ | |
| 8 | + /** | |
| 9 | + A very basic tooltip-like widget. It's intended to be popped up | |
| 10 | + to display basic information or basic user interaction | |
| 11 | + components, e.g. a cop or movopy-to-clipboard butt | |
| 12 | + if needed,l.dom | |
| 13 | + */ | |
| 14 | + const D = F.dom | |
| 15 | + base DOMwidget using the | |
| 16 | +). If theallback whic | |
| 17 | + h is called just before*/){ | |
| 18 | + 30 */ | |
| 19 | + instfunction(F/*fossil object*/){ | |
| 20 | + 30 */ | |
| 21 | + installClickToHiden or basic user intera(function(F/*fossil object*/){ | |
| 22 | + 3000sClass = cssClass; | |
| 23 | + unct}, true);isplay basic information ClickToHide(nction(F/*fossil object*/fossil object*/){ | |
| 24 | + 3000sClass = cssClass; | |
| 25 | + unction(F/*fossil object*/){ | |
| 26 | + /** | |
| 27 | + A very basic tooltip-like widget. It's intended to be popped up | |
| 28 | + to display basic information or basic user interaction | |
| 29 | + components, e.g. a copy-to-clipboard button. | |
| 30 | + | |
| 31 | + Requires: fossil.bootstrap, fossil.dom | |
| 32 | + */ | |
| 33 | + conbjecthe popup when either | |
| 34 | + call this | |
| 35 | + show(falseshow(falseshow(falseconst hide Just be careful to mess only with the X coordinate | |
| 36 | + and the width. The browser will try to keep the widget | |
| 37 | + from being truncated off-screen on the right, shifting it | |
| 38 | + to the left if needed, and we cannot generically be sure | |
| 39 | + that an enforced fully on-screen size will actually fit | |
| 40 | + the current help textclickHandler){const rect1unction(F/*fossil object*(function(F/*fossil object*/){})(window.fossil); | |
| 41 | +deleteleft'); | |
| 42 | + deletea toas(function(F/*fossil o*fossil object*/){ | |
| 43 | +function(F/*fossil ct*/){ | |
| 44 | + 30 */ | |
| 45 | + installClickToHiden or basic user intera(fun*/ | |
| 46 | + F.toast = function f(/*...*/){ | |
| 47 | + f.toast = function ff(argsObject){ | |
| 48 | + if(!ff.toaster) f ['fossil-tooltip', 'fossil-toast'] | |
| 49 | + }); | |
| 50 | + i@1hi,X: D.clearElement(ff.toaster.e);9@1SA,2w:var i = 0; | |
| 51 | + for( ; i < argsObject.length; ++i ){ | |
| 52 | + D.append(ff.toaster.e, argsObject[i]); | |
| 53 | + }; | |
| 54 | + ff.toaster.show(f.config.position.x, f.config.position.y); | |
| 55 | + Q@1mw,1m:()=>ff.toaster.hide(), f.config.displayTimeMs); | |
| 56 | + }; | |
| 57 | + } | |
| 58 | + f.toast(arguments); | |
| 59 | + }; | |
| 60 | + F.toast.config = { | |
| 61 | +10@1pR,i:displayTimeMs: 2500 | |
| 62 | + }; | |
| 63 | + | |
| 64 | +})(window.fossil); | |
| 65 | +ZfWzX; |
| --- a/src/fossil.popupwidget.js | |
| +++ b/src/fossil.popupwidget.js | |
| @@ -0,0 +1,65 @@ | |
| --- a/src/fossil.popupwidget.js | |
| +++ b/src/fossil.popupwidget.js | |
| @@ -0,0 +1,65 @@ | |
| 1 | (function(F/*fossil object*/){ |
| 2 | 30 */ |
| 3 | installClickToHiden or basic user intera(function(F/*fossil object*/){ |
| 4 | 3000sClass = cssClass; |
| 5 | unct}, true);isplay basic information ClickToHide(nction(F/*fossil object*/fossil object*/){ |
| 6 | 3000sClass = cssClass; |
| 7 | unction(F/*fossil object*/){ |
| 8 | /** |
| 9 | A very basic tooltip-like widget. It's intended to be popped up |
| 10 | to display basic information or basic user interaction |
| 11 | components, e.g. a cop or movopy-to-clipboard butt |
| 12 | if needed,l.dom |
| 13 | */ |
| 14 | const D = F.dom |
| 15 | base DOMwidget using the |
| 16 | ). If theallback whic |
| 17 | h is called just before*/){ |
| 18 | 30 */ |
| 19 | instfunction(F/*fossil object*/){ |
| 20 | 30 */ |
| 21 | installClickToHiden or basic user intera(function(F/*fossil object*/){ |
| 22 | 3000sClass = cssClass; |
| 23 | unct}, true);isplay basic information ClickToHide(nction(F/*fossil object*/fossil object*/){ |
| 24 | 3000sClass = cssClass; |
| 25 | unction(F/*fossil object*/){ |
| 26 | /** |
| 27 | A very basic tooltip-like widget. It's intended to be popped up |
| 28 | to display basic information or basic user interaction |
| 29 | components, e.g. a copy-to-clipboard button. |
| 30 | |
| 31 | Requires: fossil.bootstrap, fossil.dom |
| 32 | */ |
| 33 | conbjecthe popup when either |
| 34 | call this |
| 35 | show(falseshow(falseshow(falseconst hide Just be careful to mess only with the X coordinate |
| 36 | and the width. The browser will try to keep the widget |
| 37 | from being truncated off-screen on the right, shifting it |
| 38 | to the left if needed, and we cannot generically be sure |
| 39 | that an enforced fully on-screen size will actually fit |
| 40 | the current help textclickHandler){const rect1unction(F/*fossil object*(function(F/*fossil object*/){})(window.fossil); |
| 41 | deleteleft'); |
| 42 | deletea toas(function(F/*fossil o*fossil object*/){ |
| 43 | function(F/*fossil ct*/){ |
| 44 | 30 */ |
| 45 | installClickToHiden or basic user intera(fun*/ |
| 46 | F.toast = function f(/*...*/){ |
| 47 | f.toast = function ff(argsObject){ |
| 48 | if(!ff.toaster) f ['fossil-tooltip', 'fossil-toast'] |
| 49 | }); |
| 50 | i@1hi,X: D.clearElement(ff.toaster.e);9@1SA,2w:var i = 0; |
| 51 | for( ; i < argsObject.length; ++i ){ |
| 52 | D.append(ff.toaster.e, argsObject[i]); |
| 53 | }; |
| 54 | ff.toaster.show(f.config.position.x, f.config.position.y); |
| 55 | Q@1mw,1m:()=>ff.toaster.hide(), f.config.displayTimeMs); |
| 56 | }; |
| 57 | } |
| 58 | f.toast(arguments); |
| 59 | }; |
| 60 | F.toast.config = { |
| 61 | 10@1pR,i:displayTimeMs: 2500 |
| 62 | }; |
| 63 | |
| 64 | })(window.fossil); |
| 65 | ZfWzX; |
+45
-8
| --- src/fossil.tabs.js | ||
| +++ src/fossil.tabs.js | ||
| @@ -3,30 +3,53 @@ | ||
| 3 | 3 | const E = (s)=>document.querySelector(s), |
| 4 | 4 | EA = (s)=>document.querySelectorAll(s), |
| 5 | 5 | D = F.dom; |
| 6 | 6 | |
| 7 | 7 | /** |
| 8 | - Creates a TabManager. If passed an argument, it is | |
| 9 | - passed to init(). | |
| 8 | + Creates a TabManager. If passed a truthy first argument, it is | |
| 9 | + passed to init(). If passed a truthy second argument, it must be | |
| 10 | + an Object holding configuration options: | |
| 11 | + | |
| 12 | + { | |
| 13 | + tabAccessKeys: boolean (=true) | |
| 14 | + If true, tab buttons are assigned "accesskey" values | |
| 15 | + equal to their 1-based tab number. | |
| 16 | + } | |
| 10 | 17 | */ |
| 11 | - const TabManager = function(domElem){ | |
| 18 | + const TabManager = function(domElem, options){ | |
| 12 | 19 | this.e = {}; |
| 20 | + this.options = F.mergeLastWins(TabManager.defaultOptions , options); | |
| 13 | 21 | if(domElem) this.init(domElem); |
| 14 | 22 | }; |
| 15 | 23 | |
| 16 | 24 | /** |
| 17 | - Internal helper to normalize a method argument | |
| 18 | - to a tab element. | |
| 25 | + Default values for the options object passed to the TabManager | |
| 26 | + constructor. Changing these affects the defaults of all | |
| 27 | + TabManager instances instantiated after that point. | |
| 28 | + */ | |
| 29 | + TabManager.defaultOptions = { | |
| 30 | + tabAccessKeys: true | |
| 31 | + }; | |
| 32 | + | |
| 33 | + /** | |
| 34 | + Internal helper to normalize a method argument to a tab | |
| 35 | + element. arg may be a tab DOM element, a selector string, or an | |
| 36 | + index into tabMgr.e.tabs.childNodes. Returns the corresponding | |
| 37 | + tab element. | |
| 19 | 38 | */ |
| 20 | 39 | const tabArg = function(arg,tabMgr){ |
| 21 | 40 | if('string'===typeof arg) arg = E(arg); |
| 22 | 41 | else if(tabMgr && 'number'===typeof arg && arg>=0){ |
| 23 | 42 | arg = tabMgr.e.tabs.childNodes[arg]; |
| 24 | 43 | } |
| 25 | 44 | return arg; |
| 26 | 45 | }; |
| 27 | 46 | |
| 47 | + /** | |
| 48 | + Sets sets the visibility of tab element e to on or off. e MUST be | |
| 49 | + a TabManager tab element. | |
| 50 | + */ | |
| 28 | 51 | const setVisible = function(e,yes){ |
| 29 | 52 | D[yes ? 'removeClass' : 'addClass'](e, 'hidden'); |
| 30 | 53 | }; |
| 31 | 54 | |
| 32 | 55 | TabManager.prototype = { |
| @@ -33,16 +56,18 @@ | ||
| 33 | 56 | /** |
| 34 | 57 | Initializes the tabs associated with the given tab container |
| 35 | 58 | (DOM element or selector for a single element). This must be |
| 36 | 59 | called once before using any other member functions of a given |
| 37 | 60 | instance, noting that the constructor will call this if it is |
| 38 | - passed an argument. | |
| 61 | + passed an argument. | |
| 39 | 62 | |
| 40 | 63 | The tab container must have an 'id' attribute. This function |
| 41 | 64 | looks through the DOM for all elements which have |
| 42 | 65 | data-tab-parent=thatId. For each one it creates a button to |
| 43 | - switch to that tab and moves the element into this.e.tabs. | |
| 66 | + switch to that tab and moves the element into this.e.tabs, | |
| 67 | + *possibly* injecting an intermediary element between | |
| 68 | + this.e.tabs and the element. | |
| 44 | 69 | |
| 45 | 70 | The label for each tab is set by the data-tab-label attribute |
| 46 | 71 | of each element, defaulting to something not terribly useful. |
| 47 | 72 | |
| 48 | 73 | When it's done, it auto-selects the first tab unless a tab has |
| @@ -108,10 +133,18 @@ | ||
| 108 | 133 | }, |
| 109 | 134 | /** |
| 110 | 135 | Adds the given DOM element or unique selector as the next |
| 111 | 136 | tab in the tab container, adding a button to switch to |
| 112 | 137 | the tab. Returns this object. |
| 138 | + | |
| 139 | + If this object's options include a truthy tabAccessKeys then | |
| 140 | + each tab button gets assigned an accesskey attribute equal to | |
| 141 | + its 1-based index in the tab list. e.g. key 1 is the first tab | |
| 142 | + and key 5 is the 5th. Whether/how that accesskey is accessed is | |
| 143 | + dependent on the browser and its OS: | |
| 144 | + | |
| 145 | + https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey | |
| 113 | 146 | */ |
| 114 | 147 | addTab: function f(tab){ |
| 115 | 148 | if(!f.click){ |
| 116 | 149 | f.click = function(e){ |
| 117 | 150 | e.target.$manager.switchToTab(e.target.$tab); |
| @@ -118,15 +151,19 @@ | ||
| 118 | 151 | }; |
| 119 | 152 | } |
| 120 | 153 | tab = tabArg(tab); |
| 121 | 154 | tab.remove(); |
| 122 | 155 | D.append(this.e.tabs, D.addClass(tab,'tab-panel')); |
| 123 | - const lbl = tab.dataset.tabLabel || 'Tab #'+(this.e.tabs.childNodes.length-1); | |
| 156 | + const tabCount = this.e.tabBar.childNodes.length+1; | |
| 157 | + const lbl = tab.dataset.tabLabel || 'Tab #'+tabCount; | |
| 124 | 158 | const btn = D.addClass(D.append(D.span(), lbl), 'tab-button'); |
| 125 | 159 | D.append(this.e.tabBar,btn); |
| 126 | 160 | btn.$manager = this; |
| 127 | 161 | btn.$tab = tab; |
| 162 | + if(this.options.tabAccessKeys){ | |
| 163 | + D.attr(btn, 'accesskey', tabCount); | |
| 164 | + } | |
| 128 | 165 | btn.addEventListener('click', f.click, false); |
| 129 | 166 | return this; |
| 130 | 167 | }, |
| 131 | 168 | |
| 132 | 169 | /** |
| 133 | 170 |
| --- src/fossil.tabs.js | |
| +++ src/fossil.tabs.js | |
| @@ -3,30 +3,53 @@ | |
| 3 | const E = (s)=>document.querySelector(s), |
| 4 | EA = (s)=>document.querySelectorAll(s), |
| 5 | D = F.dom; |
| 6 | |
| 7 | /** |
| 8 | Creates a TabManager. If passed an argument, it is |
| 9 | passed to init(). |
| 10 | */ |
| 11 | const TabManager = function(domElem){ |
| 12 | this.e = {}; |
| 13 | if(domElem) this.init(domElem); |
| 14 | }; |
| 15 | |
| 16 | /** |
| 17 | Internal helper to normalize a method argument |
| 18 | to a tab element. |
| 19 | */ |
| 20 | const tabArg = function(arg,tabMgr){ |
| 21 | if('string'===typeof arg) arg = E(arg); |
| 22 | else if(tabMgr && 'number'===typeof arg && arg>=0){ |
| 23 | arg = tabMgr.e.tabs.childNodes[arg]; |
| 24 | } |
| 25 | return arg; |
| 26 | }; |
| 27 | |
| 28 | const setVisible = function(e,yes){ |
| 29 | D[yes ? 'removeClass' : 'addClass'](e, 'hidden'); |
| 30 | }; |
| 31 | |
| 32 | TabManager.prototype = { |
| @@ -33,16 +56,18 @@ | |
| 33 | /** |
| 34 | Initializes the tabs associated with the given tab container |
| 35 | (DOM element or selector for a single element). This must be |
| 36 | called once before using any other member functions of a given |
| 37 | instance, noting that the constructor will call this if it is |
| 38 | passed an argument. |
| 39 | |
| 40 | The tab container must have an 'id' attribute. This function |
| 41 | looks through the DOM for all elements which have |
| 42 | data-tab-parent=thatId. For each one it creates a button to |
| 43 | switch to that tab and moves the element into this.e.tabs. |
| 44 | |
| 45 | The label for each tab is set by the data-tab-label attribute |
| 46 | of each element, defaulting to something not terribly useful. |
| 47 | |
| 48 | When it's done, it auto-selects the first tab unless a tab has |
| @@ -108,10 +133,18 @@ | |
| 108 | }, |
| 109 | /** |
| 110 | Adds the given DOM element or unique selector as the next |
| 111 | tab in the tab container, adding a button to switch to |
| 112 | the tab. Returns this object. |
| 113 | */ |
| 114 | addTab: function f(tab){ |
| 115 | if(!f.click){ |
| 116 | f.click = function(e){ |
| 117 | e.target.$manager.switchToTab(e.target.$tab); |
| @@ -118,15 +151,19 @@ | |
| 118 | }; |
| 119 | } |
| 120 | tab = tabArg(tab); |
| 121 | tab.remove(); |
| 122 | D.append(this.e.tabs, D.addClass(tab,'tab-panel')); |
| 123 | const lbl = tab.dataset.tabLabel || 'Tab #'+(this.e.tabs.childNodes.length-1); |
| 124 | const btn = D.addClass(D.append(D.span(), lbl), 'tab-button'); |
| 125 | D.append(this.e.tabBar,btn); |
| 126 | btn.$manager = this; |
| 127 | btn.$tab = tab; |
| 128 | btn.addEventListener('click', f.click, false); |
| 129 | return this; |
| 130 | }, |
| 131 | |
| 132 | /** |
| 133 |
| --- src/fossil.tabs.js | |
| +++ src/fossil.tabs.js | |
| @@ -3,30 +3,53 @@ | |
| 3 | const E = (s)=>document.querySelector(s), |
| 4 | EA = (s)=>document.querySelectorAll(s), |
| 5 | D = F.dom; |
| 6 | |
| 7 | /** |
| 8 | Creates a TabManager. If passed a truthy first argument, it is |
| 9 | passed to init(). If passed a truthy second argument, it must be |
| 10 | an Object holding configuration options: |
| 11 | |
| 12 | { |
| 13 | tabAccessKeys: boolean (=true) |
| 14 | If true, tab buttons are assigned "accesskey" values |
| 15 | equal to their 1-based tab number. |
| 16 | } |
| 17 | */ |
| 18 | const TabManager = function(domElem, options){ |
| 19 | this.e = {}; |
| 20 | this.options = F.mergeLastWins(TabManager.defaultOptions , options); |
| 21 | if(domElem) this.init(domElem); |
| 22 | }; |
| 23 | |
| 24 | /** |
| 25 | Default values for the options object passed to the TabManager |
| 26 | constructor. Changing these affects the defaults of all |
| 27 | TabManager instances instantiated after that point. |
| 28 | */ |
| 29 | TabManager.defaultOptions = { |
| 30 | tabAccessKeys: true |
| 31 | }; |
| 32 | |
| 33 | /** |
| 34 | Internal helper to normalize a method argument to a tab |
| 35 | element. arg may be a tab DOM element, a selector string, or an |
| 36 | index into tabMgr.e.tabs.childNodes. Returns the corresponding |
| 37 | tab element. |
| 38 | */ |
| 39 | const tabArg = function(arg,tabMgr){ |
| 40 | if('string'===typeof arg) arg = E(arg); |
| 41 | else if(tabMgr && 'number'===typeof arg && arg>=0){ |
| 42 | arg = tabMgr.e.tabs.childNodes[arg]; |
| 43 | } |
| 44 | return arg; |
| 45 | }; |
| 46 | |
| 47 | /** |
| 48 | Sets sets the visibility of tab element e to on or off. e MUST be |
| 49 | a TabManager tab element. |
| 50 | */ |
| 51 | const setVisible = function(e,yes){ |
| 52 | D[yes ? 'removeClass' : 'addClass'](e, 'hidden'); |
| 53 | }; |
| 54 | |
| 55 | TabManager.prototype = { |
| @@ -33,16 +56,18 @@ | |
| 56 | /** |
| 57 | Initializes the tabs associated with the given tab container |
| 58 | (DOM element or selector for a single element). This must be |
| 59 | called once before using any other member functions of a given |
| 60 | instance, noting that the constructor will call this if it is |
| 61 | passed an argument. |
| 62 | |
| 63 | The tab container must have an 'id' attribute. This function |
| 64 | looks through the DOM for all elements which have |
| 65 | data-tab-parent=thatId. For each one it creates a button to |
| 66 | switch to that tab and moves the element into this.e.tabs, |
| 67 | *possibly* injecting an intermediary element between |
| 68 | this.e.tabs and the element. |
| 69 | |
| 70 | The label for each tab is set by the data-tab-label attribute |
| 71 | of each element, defaulting to something not terribly useful. |
| 72 | |
| 73 | When it's done, it auto-selects the first tab unless a tab has |
| @@ -108,10 +133,18 @@ | |
| 133 | }, |
| 134 | /** |
| 135 | Adds the given DOM element or unique selector as the next |
| 136 | tab in the tab container, adding a button to switch to |
| 137 | the tab. Returns this object. |
| 138 | |
| 139 | If this object's options include a truthy tabAccessKeys then |
| 140 | each tab button gets assigned an accesskey attribute equal to |
| 141 | its 1-based index in the tab list. e.g. key 1 is the first tab |
| 142 | and key 5 is the 5th. Whether/how that accesskey is accessed is |
| 143 | dependent on the browser and its OS: |
| 144 | |
| 145 | https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey |
| 146 | */ |
| 147 | addTab: function f(tab){ |
| 148 | if(!f.click){ |
| 149 | f.click = function(e){ |
| 150 | e.target.$manager.switchToTab(e.target.$tab); |
| @@ -118,15 +151,19 @@ | |
| 151 | }; |
| 152 | } |
| 153 | tab = tabArg(tab); |
| 154 | tab.remove(); |
| 155 | D.append(this.e.tabs, D.addClass(tab,'tab-panel')); |
| 156 | const tabCount = this.e.tabBar.childNodes.length+1; |
| 157 | const lbl = tab.dataset.tabLabel || 'Tab #'+tabCount; |
| 158 | const btn = D.addClass(D.append(D.span(), lbl), 'tab-button'); |
| 159 | D.append(this.e.tabBar,btn); |
| 160 | btn.$manager = this; |
| 161 | btn.$tab = tab; |
| 162 | if(this.options.tabAccessKeys){ |
| 163 | D.attr(btn, 'accesskey', tabCount); |
| 164 | } |
| 165 | btn.addEventListener('click', f.click, false); |
| 166 | return this; |
| 167 | }, |
| 168 | |
| 169 | /** |
| 170 |
+1
-1
| --- src/fusefs.c | ||
| +++ src/fusefs.c | ||
| @@ -285,11 +285,11 @@ | ||
| 285 | 285 | .readdir = fusefs_readdir, |
| 286 | 286 | .read = fusefs_read, |
| 287 | 287 | }; |
| 288 | 288 | |
| 289 | 289 | /* |
| 290 | -** COMMAND: fusefs | |
| 290 | +** COMMAND: fusefs* | |
| 291 | 291 | ** |
| 292 | 292 | ** Usage: %fossil fusefs [--debug] DIRECTORY |
| 293 | 293 | ** |
| 294 | 294 | ** This command uses the Fuse Filesystem (FuseFS) to mount a directory |
| 295 | 295 | ** at DIRECTORY that contains the content of all check-ins in the |
| 296 | 296 |
| --- src/fusefs.c | |
| +++ src/fusefs.c | |
| @@ -285,11 +285,11 @@ | |
| 285 | .readdir = fusefs_readdir, |
| 286 | .read = fusefs_read, |
| 287 | }; |
| 288 | |
| 289 | /* |
| 290 | ** COMMAND: fusefs |
| 291 | ** |
| 292 | ** Usage: %fossil fusefs [--debug] DIRECTORY |
| 293 | ** |
| 294 | ** This command uses the Fuse Filesystem (FuseFS) to mount a directory |
| 295 | ** at DIRECTORY that contains the content of all check-ins in the |
| 296 |
| --- src/fusefs.c | |
| +++ src/fusefs.c | |
| @@ -285,11 +285,11 @@ | |
| 285 | .readdir = fusefs_readdir, |
| 286 | .read = fusefs_read, |
| 287 | }; |
| 288 | |
| 289 | /* |
| 290 | ** COMMAND: fusefs* |
| 291 | ** |
| 292 | ** Usage: %fossil fusefs [--debug] DIRECTORY |
| 293 | ** |
| 294 | ** This command uses the Fuse Filesystem (FuseFS) to mount a directory |
| 295 | ** at DIRECTORY that contains the content of all check-ins in the |
| 296 |
+33
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -163,10 +163,43 @@ | ||
| 163 | 163 | if( pGlob ){ |
| 164 | 164 | fossil_free(pGlob->azPattern); |
| 165 | 165 | fossil_free(pGlob); |
| 166 | 166 | } |
| 167 | 167 | } |
| 168 | + | |
| 169 | +/* | |
| 170 | +** Appends the given glob to the given buffer in the form of a | |
| 171 | +** JS/JSON-compatible array. It requires that pDest have been | |
| 172 | +** initialized. If pGlob is NULL or empty it emits [] (an empty | |
| 173 | +** array). | |
| 174 | +*/ | |
| 175 | +void glob_render_json_to_blob(Glob *pGlob, Blob *pDest){ | |
| 176 | + int i = 0; | |
| 177 | + blob_append(pDest, "[", 1); | |
| 178 | + for( ; pGlob && i < pGlob->nPattern; ++i ){ | |
| 179 | + if(i){ | |
| 180 | + blob_append(pDest, ",", 1); | |
| 181 | + } | |
| 182 | + blob_appendf(pDest, "%!j", pGlob->azPattern[i]); | |
| 183 | + } | |
| 184 | + blob_append(pDest, "]", 1); | |
| 185 | +} | |
| 186 | +/* | |
| 187 | +** Functionally equivalent to glob_render_json_to_blob() | |
| 188 | +** but outputs via cgi_print(). | |
| 189 | +*/ | |
| 190 | +void glob_render_json_to_cgi(Glob *pGlob){ | |
| 191 | + int i = 0; | |
| 192 | + CX("["); | |
| 193 | + for( ; pGlob && i < pGlob->nPattern; ++i ){ | |
| 194 | + if(i){ | |
| 195 | + CX(","); | |
| 196 | + } | |
| 197 | + CX("%!j", pGlob->azPattern[i]); | |
| 198 | + } | |
| 199 | + CX("]"); | |
| 200 | +} | |
| 168 | 201 | |
| 169 | 202 | /* |
| 170 | 203 | ** COMMAND: test-glob |
| 171 | 204 | ** |
| 172 | 205 | ** Usage: %fossil test-glob PATTERN STRING... |
| 173 | 206 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -163,10 +163,43 @@ | |
| 163 | if( pGlob ){ |
| 164 | fossil_free(pGlob->azPattern); |
| 165 | fossil_free(pGlob); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | ** COMMAND: test-glob |
| 171 | ** |
| 172 | ** Usage: %fossil test-glob PATTERN STRING... |
| 173 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -163,10 +163,43 @@ | |
| 163 | if( pGlob ){ |
| 164 | fossil_free(pGlob->azPattern); |
| 165 | fossil_free(pGlob); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | ** Appends the given glob to the given buffer in the form of a |
| 171 | ** JS/JSON-compatible array. It requires that pDest have been |
| 172 | ** initialized. If pGlob is NULL or empty it emits [] (an empty |
| 173 | ** array). |
| 174 | */ |
| 175 | void glob_render_json_to_blob(Glob *pGlob, Blob *pDest){ |
| 176 | int i = 0; |
| 177 | blob_append(pDest, "[", 1); |
| 178 | for( ; pGlob && i < pGlob->nPattern; ++i ){ |
| 179 | if(i){ |
| 180 | blob_append(pDest, ",", 1); |
| 181 | } |
| 182 | blob_appendf(pDest, "%!j", pGlob->azPattern[i]); |
| 183 | } |
| 184 | blob_append(pDest, "]", 1); |
| 185 | } |
| 186 | /* |
| 187 | ** Functionally equivalent to glob_render_json_to_blob() |
| 188 | ** but outputs via cgi_print(). |
| 189 | */ |
| 190 | void glob_render_json_to_cgi(Glob *pGlob){ |
| 191 | int i = 0; |
| 192 | CX("["); |
| 193 | for( ; pGlob && i < pGlob->nPattern; ++i ){ |
| 194 | if(i){ |
| 195 | CX(","); |
| 196 | } |
| 197 | CX("%!j", pGlob->azPattern[i]); |
| 198 | } |
| 199 | CX("]"); |
| 200 | } |
| 201 | |
| 202 | /* |
| 203 | ** COMMAND: test-glob |
| 204 | ** |
| 205 | ** Usage: %fossil test-glob PATTERN STRING... |
| 206 |
+1
-1
| --- src/graph.js | ||
| +++ src/graph.js | ||
| @@ -779,6 +779,6 @@ | ||
| 779 | 779 | if(!dataObj) break; |
| 780 | 780 | var txJson = dataObj.textContent || dataObj.innerText; |
| 781 | 781 | var tx = JSON.parse(txJson); |
| 782 | 782 | TimelineGraph(tx); |
| 783 | 783 | } |
| 784 | -}()) | |
| 784 | +}()); | |
| 785 | 785 |
| --- src/graph.js | |
| +++ src/graph.js | |
| @@ -779,6 +779,6 @@ | |
| 779 | if(!dataObj) break; |
| 780 | var txJson = dataObj.textContent || dataObj.innerText; |
| 781 | var tx = JSON.parse(txJson); |
| 782 | TimelineGraph(tx); |
| 783 | } |
| 784 | }()) |
| 785 |
| --- src/graph.js | |
| +++ src/graph.js | |
| @@ -779,6 +779,6 @@ | |
| 779 | if(!dataObj) break; |
| 780 | var txJson = dataObj.textContent || dataObj.innerText; |
| 781 | var tx = JSON.parse(txJson); |
| 782 | TimelineGraph(tx); |
| 783 | } |
| 784 | }()); |
| 785 |
+1
-1
| --- src/hook.c | ||
| +++ src/hook.c | ||
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | } |
| 175 | 175 | db_finalize(&q); |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | 178 | /* |
| 179 | -** COMMAND: hook | |
| 179 | +** COMMAND: hook* | |
| 180 | 180 | ** |
| 181 | 181 | ** Usage: %fossil hook COMMAND ... |
| 182 | 182 | ** |
| 183 | 183 | ** Commands include: |
| 184 | 184 | ** |
| 185 | 185 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -174,11 +174,11 @@ | |
| 174 | } |
| 175 | db_finalize(&q); |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | ** COMMAND: hook |
| 180 | ** |
| 181 | ** Usage: %fossil hook COMMAND ... |
| 182 | ** |
| 183 | ** Commands include: |
| 184 | ** |
| 185 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -174,11 +174,11 @@ | |
| 174 | } |
| 175 | db_finalize(&q); |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | ** COMMAND: hook* |
| 180 | ** |
| 181 | ** Usage: %fossil hook COMMAND ... |
| 182 | ** |
| 183 | ** Commands include: |
| 184 | ** |
| 185 |
+1
-1
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -1594,11 +1594,11 @@ | ||
| 1594 | 1594 | db_finalize(&revSrc); |
| 1595 | 1595 | fossil_print(" Done!\n"); |
| 1596 | 1596 | } |
| 1597 | 1597 | |
| 1598 | 1598 | /* |
| 1599 | -** COMMAND: import | |
| 1599 | +** COMMAND: import* | |
| 1600 | 1600 | ** |
| 1601 | 1601 | ** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1602 | 1602 | ** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1603 | 1603 | ** |
| 1604 | 1604 | ** Read interchange format generated by another VCS and use it to |
| 1605 | 1605 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1594,11 +1594,11 @@ | |
| 1594 | db_finalize(&revSrc); |
| 1595 | fossil_print(" Done!\n"); |
| 1596 | } |
| 1597 | |
| 1598 | /* |
| 1599 | ** COMMAND: import |
| 1600 | ** |
| 1601 | ** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1602 | ** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1603 | ** |
| 1604 | ** Read interchange format generated by another VCS and use it to |
| 1605 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1594,11 +1594,11 @@ | |
| 1594 | db_finalize(&revSrc); |
| 1595 | fossil_print(" Done!\n"); |
| 1596 | } |
| 1597 | |
| 1598 | /* |
| 1599 | ** COMMAND: import* |
| 1600 | ** |
| 1601 | ** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1602 | ** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1603 | ** |
| 1604 | ** Read interchange format generated by another VCS and use it to |
| 1605 |
+104
-48
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -191,11 +191,11 @@ | ||
| 191 | 191 | ** Options: |
| 192 | 192 | ** |
| 193 | 193 | ** -R|--repository FILE Extract info from repository FILE |
| 194 | 194 | ** -v|--verbose Show extra information about repositories |
| 195 | 195 | ** |
| 196 | -** See also: annotate, artifact, finfo, timeline | |
| 196 | +** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]] | |
| 197 | 197 | */ |
| 198 | 198 | void info_cmd(void){ |
| 199 | 199 | i64 fsize; |
| 200 | 200 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 201 | 201 | if( !verboseFlag ){ |
| @@ -441,11 +441,11 @@ | ||
| 441 | 441 | /* |
| 442 | 442 | ** Generate javascript to enhance HTML diffs. |
| 443 | 443 | */ |
| 444 | 444 | void append_diff_javascript(int sideBySide){ |
| 445 | 445 | if( !sideBySide ) return; |
| 446 | - style_load_one_js_file("sbsdiff.js"); | |
| 446 | + builtin_request_js("sbsdiff.js"); | |
| 447 | 447 | } |
| 448 | 448 | |
| 449 | 449 | /* |
| 450 | 450 | ** Construct an appropriate diffFlag for text_diff() based on query |
| 451 | 451 | ** parameters and the to boolean arguments. |
| @@ -2011,27 +2011,37 @@ | ||
| 2011 | 2011 | manifest_destroy(pManifest); |
| 2012 | 2012 | return rid; |
| 2013 | 2013 | } |
| 2014 | 2014 | |
| 2015 | 2015 | /* |
| 2016 | -** The "z" argument is a string that contains the text of a source code | |
| 2017 | -** file. This routine appends that text to the HTTP reply with line numbering. | |
| 2016 | +** The "z" argument is a string that contains the text of a source | |
| 2017 | +** code file and nZ is its length in bytes. This routine appends that | |
| 2018 | +** text to the HTTP reply with line numbering. | |
| 2019 | +** | |
| 2020 | +** zName is the content's file name, if any (it may be NULL). If that | |
| 2021 | +** name contains a '.' then the part after the final '.' is used as | |
| 2022 | +** the X part of a "language-X" CSS class on the generated CODE block. | |
| 2018 | 2023 | ** |
| 2019 | 2024 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2020 | 2025 | ** then highlight that line number and scroll to it once the page loads. |
| 2021 | 2026 | ** If there are two line numbers, highlight the range of lines. |
| 2022 | 2027 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2023 | 2028 | ** separated by a non-digit character (also not one of [-,.]). |
| 2024 | 2029 | */ |
| 2025 | 2030 | void output_text_with_line_numbers( |
| 2026 | 2031 | const char *z, |
| 2032 | + int nZ, | |
| 2033 | + const char *zName, | |
| 2027 | 2034 | const char *zLn |
| 2028 | 2035 | ){ |
| 2029 | 2036 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2030 | 2037 | int n = 0; /* Current line number */ |
| 2031 | 2038 | int i = 0; /* Loop index */ |
| 2032 | 2039 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2040 | + int nLine = 0; /* content line count */ | |
| 2041 | + int nSpans = 0; /* number of distinct zLn spans */ | |
| 2042 | + const char *zExt = file_extension(zName); | |
| 2033 | 2043 | Stmt q; |
| 2034 | 2044 | |
| 2035 | 2045 | iStart = iEnd = atoi(zLn); |
| 2036 | 2046 | db_multi_exec( |
| 2037 | 2047 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2047,56 +2057,101 @@ | ||
| 2047 | 2057 | while( fossil_isdigit(zLn[i]) ) i++; |
| 2048 | 2058 | if( iEnd<iStart ) iEnd = iStart; |
| 2049 | 2059 | db_multi_exec( |
| 2050 | 2060 | "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd |
| 2051 | 2061 | ); |
| 2062 | + ++nSpans; | |
| 2052 | 2063 | iStart = iEnd = atoi(&zLn[i++]); |
| 2053 | 2064 | }while( zLn[i] && iStart && iEnd ); |
| 2054 | 2065 | } |
| 2055 | - db_prepare(&q, "SELECT min(iStart), max(iEnd) FROM lnos"); | |
| 2056 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 2057 | - iStart = db_column_int(&q, 0); | |
| 2058 | - iEnd = db_column_int(&q, 1); | |
| 2059 | - iTop = iStart - 15 + (iEnd-iStart)/4; | |
| 2060 | - if( iTop>iStart - 2 ) iTop = iStart-2; | |
| 2061 | - } | |
| 2062 | - db_finalize(&q); | |
| 2063 | - @ <pre> | |
| 2064 | - while( z[0] ){ | |
| 2065 | - n++; | |
| 2066 | - db_prepare(&q, | |
| 2067 | - "SELECT min(iStart), max(iEnd) FROM lnos" | |
| 2068 | - " WHERE iStart <= %d AND iEnd >= %d", n, n); | |
| 2069 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 2070 | - iStart = db_column_int(&q, 0); | |
| 2071 | - iEnd = db_column_int(&q, 1); | |
| 2072 | - } | |
| 2073 | - db_finalize(&q); | |
| 2074 | - for(i=0; z[i] && z[i]!='\n'; i++){} | |
| 2075 | - if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1); | |
| 2076 | - if( n==iStart ){ | |
| 2077 | - cgi_append_content("<div class=\"selectedText\">",-1); | |
| 2078 | - } | |
| 2079 | - cgi_printf("%6d ", n); | |
| 2080 | - if( i>0 ){ | |
| 2081 | - char *zHtml = htmlize(z, i); | |
| 2082 | - cgi_append_content(zHtml, -1); | |
| 2083 | - fossil_free(zHtml); | |
| 2084 | - } | |
| 2085 | - if( n==iTop ) cgi_append_content("</span>", -1); | |
| 2086 | - if( n==iEnd ) cgi_append_content("</div>", -1); | |
| 2087 | - else cgi_append_content("\n", 1); | |
| 2088 | - z += i; | |
| 2089 | - if( z[0]=='\n' ) z++; | |
| 2090 | - } | |
| 2091 | - if( n<iEnd ) cgi_printf("</div>"); | |
| 2092 | - @ </pre> | |
| 2066 | + /*cgi_printf("<!-- ln span count=%d -->", nSpans);*/ | |
| 2067 | + cgi_append_content("<table class='numbered-lines'><tbody>" | |
| 2068 | + "<tr><td class='line-numbers'>", -1); | |
| 2069 | + iStart = iEnd = 0; | |
| 2070 | + count_lines(z, nZ, &nLine); | |
| 2071 | + for( n=1 ; n<=nLine; ++n ){ | |
| 2072 | + const char * zAttr = ""; | |
| 2073 | + const char * zId = ""; | |
| 2074 | + if(nSpans>0 && iEnd==0){/*Grab the next range of zLn marking*/ | |
| 2075 | + db_prepare(&q, "SELECT iStart, iEnd FROM lnos " | |
| 2076 | + "WHERE iStart >= %d ORDER BY iStart", n); | |
| 2077 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 2078 | + iStart = db_column_int(&q, 0); | |
| 2079 | + iEnd = db_column_int(&q, 1); | |
| 2080 | + if(!iTop){ | |
| 2081 | + iTop = iStart - 15 + (iEnd-iStart)/4; | |
| 2082 | + if( iTop>iStart - 2 ) iTop = iStart-2; | |
| 2083 | + } | |
| 2084 | + }else{ | |
| 2085 | + /* Note that overlapping multi-spans, e.g. 10-15+12-20, | |
| 2086 | + can cause us to miss a row. */ | |
| 2087 | + iStart = iEnd = 0; | |
| 2088 | + } | |
| 2089 | + db_finalize(&q); | |
| 2090 | + --nSpans; | |
| 2091 | + /*cgi_printf("<!-- iStart=%d, iEnd=%d -->", iStart, iEnd);*/ | |
| 2092 | + } | |
| 2093 | + if(n==iTop) { | |
| 2094 | + zId = " id='scrollToMe'"; | |
| 2095 | + } | |
| 2096 | + if(n==iStart){/*Figure out which CSS class(es) this line needs...*/ | |
| 2097 | + if(n==iEnd){ | |
| 2098 | + zAttr = " class='selected-line start end'"; | |
| 2099 | + iEnd = 0; | |
| 2100 | + }else{ | |
| 2101 | + zAttr = " class='selected-line start'"; | |
| 2102 | + } | |
| 2103 | + iStart = 0; | |
| 2104 | + }else if(n==iEnd){ | |
| 2105 | + zAttr = " class='selected-line end'"; | |
| 2106 | + iEnd = 0; | |
| 2107 | + }else if( n>iStart && n<iEnd ){ | |
| 2108 | + zAttr = " class='selected-line'"; | |
| 2109 | + } | |
| 2110 | + cgi_printf("<span%s%s>%6d</span>", zId, zAttr, n); | |
| 2111 | + } | |
| 2112 | + cgi_append_content("</td><td class='file-content'><pre>",-1); | |
| 2113 | + if(zExt && *zExt){ | |
| 2114 | + cgi_printf("<code class='language-%h'>",zExt); | |
| 2115 | + }else{ | |
| 2116 | + cgi_append_content("<code>", -1); | |
| 2117 | + } | |
| 2118 | + cgi_printf("%z", htmlize(z, nZ)); | |
| 2119 | + CX("</code></pre></td></tr></tbody></table>\n"); | |
| 2093 | 2120 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2094 | - style_load_one_js_file("scroll.js"); | |
| 2121 | + builtin_request_js("scroll.js"); | |
| 2095 | 2122 | } |
| 2123 | + style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", | |
| 2124 | + "numbered-lines", 0); | |
| 2096 | 2125 | } |
| 2097 | 2126 | |
| 2127 | +/* | |
| 2128 | +** COMMAND: test-line-numbers | |
| 2129 | +** | |
| 2130 | +** Usage: %fossil test-line-numbers FILE ?LN-SPEC? | |
| 2131 | +** | |
| 2132 | +*/ | |
| 2133 | +void cmd_test_line_numbers(void){ | |
| 2134 | + Blob content = empty_blob; | |
| 2135 | + const char * zLn = ""; | |
| 2136 | + const char * zFilename = 0; | |
| 2137 | + | |
| 2138 | + if(g.argc < 3){ | |
| 2139 | + usage("FILE"); | |
| 2140 | + }else if(g.argc>3){ | |
| 2141 | + zLn = g.argv[3]; | |
| 2142 | + } | |
| 2143 | + db_find_and_open_repository(0,0); | |
| 2144 | + zFilename = g.argv[2]; | |
| 2145 | + fossil_print("%s %s\n", zFilename, zLn); | |
| 2146 | + | |
| 2147 | + blob_read_from_file(&content, zFilename, ExtFILE); | |
| 2148 | + output_text_with_line_numbers(blob_str(&content), blob_size(&content), | |
| 2149 | + zFilename, zLn); | |
| 2150 | + blob_reset(&content); | |
| 2151 | + fossil_print("%b\n", cgi_output_blob()); | |
| 2152 | +} | |
| 2098 | 2153 | |
| 2099 | 2154 | /* |
| 2100 | 2155 | ** WEBPAGE: artifact |
| 2101 | 2156 | ** WEBPAGE: file |
| 2102 | 2157 | ** WEBPAGE: whatis |
| @@ -2387,25 +2442,26 @@ | ||
| 2387 | 2442 | if( zLn==0 || atoi(zLn)==0 ){ |
| 2388 | 2443 | style_submenu_checkbox("ln", "Line Numbers", 0, 0); |
| 2389 | 2444 | } |
| 2390 | 2445 | blob_to_utf8_no_bom(&content, 0); |
| 2391 | 2446 | zMime = mimetype_from_content(&content); |
| 2392 | - @ <blockquote> | |
| 2447 | + @ <blockquote class="file-content"> | |
| 2393 | 2448 | if( zMime==0 ){ |
| 2394 | 2449 | const char *z, *zFileName, *zExt; |
| 2395 | 2450 | z = blob_str(&content); |
| 2396 | 2451 | zFileName = db_text(0, |
| 2397 | 2452 | "SELECT name FROM mlink, filename" |
| 2398 | 2453 | " WHERE filename.fnid=mlink.fnid" |
| 2399 | 2454 | " AND mlink.fid=%d", |
| 2400 | 2455 | rid); |
| 2401 | - zExt = zFileName ? strrchr(zFileName, '.') : 0; | |
| 2456 | + zExt = file_extension(zFileName); | |
| 2402 | 2457 | if( zLn ){ |
| 2403 | - output_text_with_line_numbers(z, zLn); | |
| 2458 | + output_text_with_line_numbers(z, blob_size(&content), | |
| 2459 | + zFileName, zLn); | |
| 2404 | 2460 | }else if( zExt && zExt[1] ){ |
| 2405 | 2461 | @ <pre> |
| 2406 | - @ <code class="language-%s(zExt+1)">%h(z)</code> | |
| 2462 | + @ <code class="language-%s(zExt)">%h(z)</code> | |
| 2407 | 2463 | @ </pre> |
| 2408 | 2464 | }else{ |
| 2409 | 2465 | @ <pre> |
| 2410 | 2466 | @ %h(z) |
| 2411 | 2467 | @ </pre> |
| @@ -3113,11 +3169,11 @@ | ||
| 3113 | 3169 | @ <input type="submit" name="apply" value="Apply Changes" /> |
| 3114 | 3170 | } |
| 3115 | 3171 | @ </td></tr> |
| 3116 | 3172 | @ </table> |
| 3117 | 3173 | @ </div></form> |
| 3118 | - style_load_one_js_file("ci_edit.js"); | |
| 3174 | + builtin_request_js("ci_edit.js"); | |
| 3119 | 3175 | style_footer(); |
| 3120 | 3176 | } |
| 3121 | 3177 | |
| 3122 | 3178 | /* |
| 3123 | 3179 | ** Prepare an ammended commit comment. Let the user modify it using the |
| 3124 | 3180 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | ** Options: |
| 192 | ** |
| 193 | ** -R|--repository FILE Extract info from repository FILE |
| 194 | ** -v|--verbose Show extra information about repositories |
| 195 | ** |
| 196 | ** See also: annotate, artifact, finfo, timeline |
| 197 | */ |
| 198 | void info_cmd(void){ |
| 199 | i64 fsize; |
| 200 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 201 | if( !verboseFlag ){ |
| @@ -441,11 +441,11 @@ | |
| 441 | /* |
| 442 | ** Generate javascript to enhance HTML diffs. |
| 443 | */ |
| 444 | void append_diff_javascript(int sideBySide){ |
| 445 | if( !sideBySide ) return; |
| 446 | style_load_one_js_file("sbsdiff.js"); |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | ** Construct an appropriate diffFlag for text_diff() based on query |
| 451 | ** parameters and the to boolean arguments. |
| @@ -2011,27 +2011,37 @@ | |
| 2011 | manifest_destroy(pManifest); |
| 2012 | return rid; |
| 2013 | } |
| 2014 | |
| 2015 | /* |
| 2016 | ** The "z" argument is a string that contains the text of a source code |
| 2017 | ** file. This routine appends that text to the HTTP reply with line numbering. |
| 2018 | ** |
| 2019 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2020 | ** then highlight that line number and scroll to it once the page loads. |
| 2021 | ** If there are two line numbers, highlight the range of lines. |
| 2022 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2023 | ** separated by a non-digit character (also not one of [-,.]). |
| 2024 | */ |
| 2025 | void output_text_with_line_numbers( |
| 2026 | const char *z, |
| 2027 | const char *zLn |
| 2028 | ){ |
| 2029 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2030 | int n = 0; /* Current line number */ |
| 2031 | int i = 0; /* Loop index */ |
| 2032 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2033 | Stmt q; |
| 2034 | |
| 2035 | iStart = iEnd = atoi(zLn); |
| 2036 | db_multi_exec( |
| 2037 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2047,56 +2057,101 @@ | |
| 2047 | while( fossil_isdigit(zLn[i]) ) i++; |
| 2048 | if( iEnd<iStart ) iEnd = iStart; |
| 2049 | db_multi_exec( |
| 2050 | "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd |
| 2051 | ); |
| 2052 | iStart = iEnd = atoi(&zLn[i++]); |
| 2053 | }while( zLn[i] && iStart && iEnd ); |
| 2054 | } |
| 2055 | db_prepare(&q, "SELECT min(iStart), max(iEnd) FROM lnos"); |
| 2056 | if( db_step(&q)==SQLITE_ROW ){ |
| 2057 | iStart = db_column_int(&q, 0); |
| 2058 | iEnd = db_column_int(&q, 1); |
| 2059 | iTop = iStart - 15 + (iEnd-iStart)/4; |
| 2060 | if( iTop>iStart - 2 ) iTop = iStart-2; |
| 2061 | } |
| 2062 | db_finalize(&q); |
| 2063 | @ <pre> |
| 2064 | while( z[0] ){ |
| 2065 | n++; |
| 2066 | db_prepare(&q, |
| 2067 | "SELECT min(iStart), max(iEnd) FROM lnos" |
| 2068 | " WHERE iStart <= %d AND iEnd >= %d", n, n); |
| 2069 | if( db_step(&q)==SQLITE_ROW ){ |
| 2070 | iStart = db_column_int(&q, 0); |
| 2071 | iEnd = db_column_int(&q, 1); |
| 2072 | } |
| 2073 | db_finalize(&q); |
| 2074 | for(i=0; z[i] && z[i]!='\n'; i++){} |
| 2075 | if( n==iTop ) cgi_append_content("<span id=\"scrollToMe\">", -1); |
| 2076 | if( n==iStart ){ |
| 2077 | cgi_append_content("<div class=\"selectedText\">",-1); |
| 2078 | } |
| 2079 | cgi_printf("%6d ", n); |
| 2080 | if( i>0 ){ |
| 2081 | char *zHtml = htmlize(z, i); |
| 2082 | cgi_append_content(zHtml, -1); |
| 2083 | fossil_free(zHtml); |
| 2084 | } |
| 2085 | if( n==iTop ) cgi_append_content("</span>", -1); |
| 2086 | if( n==iEnd ) cgi_append_content("</div>", -1); |
| 2087 | else cgi_append_content("\n", 1); |
| 2088 | z += i; |
| 2089 | if( z[0]=='\n' ) z++; |
| 2090 | } |
| 2091 | if( n<iEnd ) cgi_printf("</div>"); |
| 2092 | @ </pre> |
| 2093 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2094 | style_load_one_js_file("scroll.js"); |
| 2095 | } |
| 2096 | } |
| 2097 | |
| 2098 | |
| 2099 | /* |
| 2100 | ** WEBPAGE: artifact |
| 2101 | ** WEBPAGE: file |
| 2102 | ** WEBPAGE: whatis |
| @@ -2387,25 +2442,26 @@ | |
| 2387 | if( zLn==0 || atoi(zLn)==0 ){ |
| 2388 | style_submenu_checkbox("ln", "Line Numbers", 0, 0); |
| 2389 | } |
| 2390 | blob_to_utf8_no_bom(&content, 0); |
| 2391 | zMime = mimetype_from_content(&content); |
| 2392 | @ <blockquote> |
| 2393 | if( zMime==0 ){ |
| 2394 | const char *z, *zFileName, *zExt; |
| 2395 | z = blob_str(&content); |
| 2396 | zFileName = db_text(0, |
| 2397 | "SELECT name FROM mlink, filename" |
| 2398 | " WHERE filename.fnid=mlink.fnid" |
| 2399 | " AND mlink.fid=%d", |
| 2400 | rid); |
| 2401 | zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2402 | if( zLn ){ |
| 2403 | output_text_with_line_numbers(z, zLn); |
| 2404 | }else if( zExt && zExt[1] ){ |
| 2405 | @ <pre> |
| 2406 | @ <code class="language-%s(zExt+1)">%h(z)</code> |
| 2407 | @ </pre> |
| 2408 | }else{ |
| 2409 | @ <pre> |
| 2410 | @ %h(z) |
| 2411 | @ </pre> |
| @@ -3113,11 +3169,11 @@ | |
| 3113 | @ <input type="submit" name="apply" value="Apply Changes" /> |
| 3114 | } |
| 3115 | @ </td></tr> |
| 3116 | @ </table> |
| 3117 | @ </div></form> |
| 3118 | style_load_one_js_file("ci_edit.js"); |
| 3119 | style_footer(); |
| 3120 | } |
| 3121 | |
| 3122 | /* |
| 3123 | ** Prepare an ammended commit comment. Let the user modify it using the |
| 3124 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | ** Options: |
| 192 | ** |
| 193 | ** -R|--repository FILE Extract info from repository FILE |
| 194 | ** -v|--verbose Show extra information about repositories |
| 195 | ** |
| 196 | ** See also: [[annotate]], [[artifact]], [[finfo]], [[timeline]] |
| 197 | */ |
| 198 | void info_cmd(void){ |
| 199 | i64 fsize; |
| 200 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 201 | if( !verboseFlag ){ |
| @@ -441,11 +441,11 @@ | |
| 441 | /* |
| 442 | ** Generate javascript to enhance HTML diffs. |
| 443 | */ |
| 444 | void append_diff_javascript(int sideBySide){ |
| 445 | if( !sideBySide ) return; |
| 446 | builtin_request_js("sbsdiff.js"); |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | ** Construct an appropriate diffFlag for text_diff() based on query |
| 451 | ** parameters and the to boolean arguments. |
| @@ -2011,27 +2011,37 @@ | |
| 2011 | manifest_destroy(pManifest); |
| 2012 | return rid; |
| 2013 | } |
| 2014 | |
| 2015 | /* |
| 2016 | ** The "z" argument is a string that contains the text of a source |
| 2017 | ** code file and nZ is its length in bytes. This routine appends that |
| 2018 | ** text to the HTTP reply with line numbering. |
| 2019 | ** |
| 2020 | ** zName is the content's file name, if any (it may be NULL). If that |
| 2021 | ** name contains a '.' then the part after the final '.' is used as |
| 2022 | ** the X part of a "language-X" CSS class on the generated CODE block. |
| 2023 | ** |
| 2024 | ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, |
| 2025 | ** then highlight that line number and scroll to it once the page loads. |
| 2026 | ** If there are two line numbers, highlight the range of lines. |
| 2027 | ** Multiple ranges can be highlighed by adding additional line numbers |
| 2028 | ** separated by a non-digit character (also not one of [-,.]). |
| 2029 | */ |
| 2030 | void output_text_with_line_numbers( |
| 2031 | const char *z, |
| 2032 | int nZ, |
| 2033 | const char *zName, |
| 2034 | const char *zLn |
| 2035 | ){ |
| 2036 | int iStart, iEnd; /* Start and end of region to highlight */ |
| 2037 | int n = 0; /* Current line number */ |
| 2038 | int i = 0; /* Loop index */ |
| 2039 | int iTop = 0; /* Scroll so that this line is on top of screen. */ |
| 2040 | int nLine = 0; /* content line count */ |
| 2041 | int nSpans = 0; /* number of distinct zLn spans */ |
| 2042 | const char *zExt = file_extension(zName); |
| 2043 | Stmt q; |
| 2044 | |
| 2045 | iStart = iEnd = atoi(zLn); |
| 2046 | db_multi_exec( |
| 2047 | "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); |
| @@ -2047,56 +2057,101 @@ | |
| 2057 | while( fossil_isdigit(zLn[i]) ) i++; |
| 2058 | if( iEnd<iStart ) iEnd = iStart; |
| 2059 | db_multi_exec( |
| 2060 | "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd |
| 2061 | ); |
| 2062 | ++nSpans; |
| 2063 | iStart = iEnd = atoi(&zLn[i++]); |
| 2064 | }while( zLn[i] && iStart && iEnd ); |
| 2065 | } |
| 2066 | /*cgi_printf("<!-- ln span count=%d -->", nSpans);*/ |
| 2067 | cgi_append_content("<table class='numbered-lines'><tbody>" |
| 2068 | "<tr><td class='line-numbers'>", -1); |
| 2069 | iStart = iEnd = 0; |
| 2070 | count_lines(z, nZ, &nLine); |
| 2071 | for( n=1 ; n<=nLine; ++n ){ |
| 2072 | const char * zAttr = ""; |
| 2073 | const char * zId = ""; |
| 2074 | if(nSpans>0 && iEnd==0){/*Grab the next range of zLn marking*/ |
| 2075 | db_prepare(&q, "SELECT iStart, iEnd FROM lnos " |
| 2076 | "WHERE iStart >= %d ORDER BY iStart", n); |
| 2077 | if( db_step(&q)==SQLITE_ROW ){ |
| 2078 | iStart = db_column_int(&q, 0); |
| 2079 | iEnd = db_column_int(&q, 1); |
| 2080 | if(!iTop){ |
| 2081 | iTop = iStart - 15 + (iEnd-iStart)/4; |
| 2082 | if( iTop>iStart - 2 ) iTop = iStart-2; |
| 2083 | } |
| 2084 | }else{ |
| 2085 | /* Note that overlapping multi-spans, e.g. 10-15+12-20, |
| 2086 | can cause us to miss a row. */ |
| 2087 | iStart = iEnd = 0; |
| 2088 | } |
| 2089 | db_finalize(&q); |
| 2090 | --nSpans; |
| 2091 | /*cgi_printf("<!-- iStart=%d, iEnd=%d -->", iStart, iEnd);*/ |
| 2092 | } |
| 2093 | if(n==iTop) { |
| 2094 | zId = " id='scrollToMe'"; |
| 2095 | } |
| 2096 | if(n==iStart){/*Figure out which CSS class(es) this line needs...*/ |
| 2097 | if(n==iEnd){ |
| 2098 | zAttr = " class='selected-line start end'"; |
| 2099 | iEnd = 0; |
| 2100 | }else{ |
| 2101 | zAttr = " class='selected-line start'"; |
| 2102 | } |
| 2103 | iStart = 0; |
| 2104 | }else if(n==iEnd){ |
| 2105 | zAttr = " class='selected-line end'"; |
| 2106 | iEnd = 0; |
| 2107 | }else if( n>iStart && n<iEnd ){ |
| 2108 | zAttr = " class='selected-line'"; |
| 2109 | } |
| 2110 | cgi_printf("<span%s%s>%6d</span>", zId, zAttr, n); |
| 2111 | } |
| 2112 | cgi_append_content("</td><td class='file-content'><pre>",-1); |
| 2113 | if(zExt && *zExt){ |
| 2114 | cgi_printf("<code class='language-%h'>",zExt); |
| 2115 | }else{ |
| 2116 | cgi_append_content("<code>", -1); |
| 2117 | } |
| 2118 | cgi_printf("%z", htmlize(z, nZ)); |
| 2119 | CX("</code></pre></td></tr></tbody></table>\n"); |
| 2120 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2121 | builtin_request_js("scroll.js"); |
| 2122 | } |
| 2123 | style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", |
| 2124 | "numbered-lines", 0); |
| 2125 | } |
| 2126 | |
| 2127 | /* |
| 2128 | ** COMMAND: test-line-numbers |
| 2129 | ** |
| 2130 | ** Usage: %fossil test-line-numbers FILE ?LN-SPEC? |
| 2131 | ** |
| 2132 | */ |
| 2133 | void cmd_test_line_numbers(void){ |
| 2134 | Blob content = empty_blob; |
| 2135 | const char * zLn = ""; |
| 2136 | const char * zFilename = 0; |
| 2137 | |
| 2138 | if(g.argc < 3){ |
| 2139 | usage("FILE"); |
| 2140 | }else if(g.argc>3){ |
| 2141 | zLn = g.argv[3]; |
| 2142 | } |
| 2143 | db_find_and_open_repository(0,0); |
| 2144 | zFilename = g.argv[2]; |
| 2145 | fossil_print("%s %s\n", zFilename, zLn); |
| 2146 | |
| 2147 | blob_read_from_file(&content, zFilename, ExtFILE); |
| 2148 | output_text_with_line_numbers(blob_str(&content), blob_size(&content), |
| 2149 | zFilename, zLn); |
| 2150 | blob_reset(&content); |
| 2151 | fossil_print("%b\n", cgi_output_blob()); |
| 2152 | } |
| 2153 | |
| 2154 | /* |
| 2155 | ** WEBPAGE: artifact |
| 2156 | ** WEBPAGE: file |
| 2157 | ** WEBPAGE: whatis |
| @@ -2387,25 +2442,26 @@ | |
| 2442 | if( zLn==0 || atoi(zLn)==0 ){ |
| 2443 | style_submenu_checkbox("ln", "Line Numbers", 0, 0); |
| 2444 | } |
| 2445 | blob_to_utf8_no_bom(&content, 0); |
| 2446 | zMime = mimetype_from_content(&content); |
| 2447 | @ <blockquote class="file-content"> |
| 2448 | if( zMime==0 ){ |
| 2449 | const char *z, *zFileName, *zExt; |
| 2450 | z = blob_str(&content); |
| 2451 | zFileName = db_text(0, |
| 2452 | "SELECT name FROM mlink, filename" |
| 2453 | " WHERE filename.fnid=mlink.fnid" |
| 2454 | " AND mlink.fid=%d", |
| 2455 | rid); |
| 2456 | zExt = file_extension(zFileName); |
| 2457 | if( zLn ){ |
| 2458 | output_text_with_line_numbers(z, blob_size(&content), |
| 2459 | zFileName, zLn); |
| 2460 | }else if( zExt && zExt[1] ){ |
| 2461 | @ <pre> |
| 2462 | @ <code class="language-%s(zExt)">%h(z)</code> |
| 2463 | @ </pre> |
| 2464 | }else{ |
| 2465 | @ <pre> |
| 2466 | @ %h(z) |
| 2467 | @ </pre> |
| @@ -3113,11 +3169,11 @@ | |
| 3169 | @ <input type="submit" name="apply" value="Apply Changes" /> |
| 3170 | } |
| 3171 | @ </td></tr> |
| 3172 | @ </table> |
| 3173 | @ </div></form> |
| 3174 | builtin_request_js("ci_edit.js"); |
| 3175 | style_footer(); |
| 3176 | } |
| 3177 | |
| 3178 | /* |
| 3179 | ** Prepare an ammended commit comment. Let the user modify it using the |
| 3180 |
+2
| --- src/json_config.c | ||
| +++ src/json_config.c | ||
| @@ -61,10 +61,12 @@ | ||
| 61 | 61 | { "details", CONFIGSET_SKIN }, |
| 62 | 62 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 63 | 63 | { "logo-image", CONFIGSET_SKIN }, |
| 64 | 64 | { "background-mimetype", CONFIGSET_SKIN }, |
| 65 | 65 | { "background-image", CONFIGSET_SKIN }, |
| 66 | +{ "icon-mimetype", CONFIGSET_SKIN }, | |
| 67 | +{ "icon-image", CONFIGSET_SKIN }, | |
| 66 | 68 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 67 | 69 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 68 | 70 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 69 | 71 | { "adunit", CONFIGSET_SKIN }, |
| 70 | 72 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 71 | 73 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -61,10 +61,12 @@ | |
| 61 | { "details", CONFIGSET_SKIN }, |
| 62 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 63 | { "logo-image", CONFIGSET_SKIN }, |
| 64 | { "background-mimetype", CONFIGSET_SKIN }, |
| 65 | { "background-image", CONFIGSET_SKIN }, |
| 66 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 67 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 68 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 69 | { "adunit", CONFIGSET_SKIN }, |
| 70 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 71 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -61,10 +61,12 @@ | |
| 61 | { "details", CONFIGSET_SKIN }, |
| 62 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 63 | { "logo-image", CONFIGSET_SKIN }, |
| 64 | { "background-mimetype", CONFIGSET_SKIN }, |
| 65 | { "background-image", CONFIGSET_SKIN }, |
| 66 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 67 | { "icon-image", CONFIGSET_SKIN }, |
| 68 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 69 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 70 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 71 | { "adunit", CONFIGSET_SKIN }, |
| 72 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 73 |
+5
-4
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -752,11 +752,11 @@ | ||
| 752 | 752 | @ %h(zCaptcha) |
| 753 | 753 | @ </pre></td></tr></table> |
| 754 | 754 | if( bAutoCaptcha ) { |
| 755 | 755 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 756 | 756 | @ data-af='%s(zDecoded)' /> |
| 757 | - style_load_one_js_file("login.js"); | |
| 757 | + builtin_request_js("login.js"); | |
| 758 | 758 | } |
| 759 | 759 | @ </div> |
| 760 | 760 | free(zCaptcha); |
| 761 | 761 | } |
| 762 | 762 | @ </form> |
| @@ -1591,13 +1591,14 @@ | ||
| 1591 | 1591 | /* If the email is found anywhere in USER.INFO... */ |
| 1592 | 1592 | db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) |
| 1593 | 1593 | || |
| 1594 | 1594 | /* Or if the email is a verify subscriber email with an associated |
| 1595 | 1595 | ** user... */ |
| 1596 | - db_exists( | |
| 1597 | - "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" | |
| 1598 | - " AND sverified",zEAddr) | |
| 1596 | + (alert_tables_exist() && | |
| 1597 | + db_exists( | |
| 1598 | + "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" | |
| 1599 | + " AND sverified",zEAddr)) | |
| 1599 | 1600 | ){ |
| 1600 | 1601 | iErrLine = 3; |
| 1601 | 1602 | zErr = "This email address is already claimed by another user"; |
| 1602 | 1603 | }else{ |
| 1603 | 1604 | /* If all of the tests above have passed, that means that the submitted |
| 1604 | 1605 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -752,11 +752,11 @@ | |
| 752 | @ %h(zCaptcha) |
| 753 | @ </pre></td></tr></table> |
| 754 | if( bAutoCaptcha ) { |
| 755 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 756 | @ data-af='%s(zDecoded)' /> |
| 757 | style_load_one_js_file("login.js"); |
| 758 | } |
| 759 | @ </div> |
| 760 | free(zCaptcha); |
| 761 | } |
| 762 | @ </form> |
| @@ -1591,13 +1591,14 @@ | |
| 1591 | /* If the email is found anywhere in USER.INFO... */ |
| 1592 | db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) |
| 1593 | || |
| 1594 | /* Or if the email is a verify subscriber email with an associated |
| 1595 | ** user... */ |
| 1596 | db_exists( |
| 1597 | "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" |
| 1598 | " AND sverified",zEAddr) |
| 1599 | ){ |
| 1600 | iErrLine = 3; |
| 1601 | zErr = "This email address is already claimed by another user"; |
| 1602 | }else{ |
| 1603 | /* If all of the tests above have passed, that means that the submitted |
| 1604 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -752,11 +752,11 @@ | |
| 752 | @ %h(zCaptcha) |
| 753 | @ </pre></td></tr></table> |
| 754 | if( bAutoCaptcha ) { |
| 755 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 756 | @ data-af='%s(zDecoded)' /> |
| 757 | builtin_request_js("login.js"); |
| 758 | } |
| 759 | @ </div> |
| 760 | free(zCaptcha); |
| 761 | } |
| 762 | @ </form> |
| @@ -1591,13 +1591,14 @@ | |
| 1591 | /* If the email is found anywhere in USER.INFO... */ |
| 1592 | db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) |
| 1593 | || |
| 1594 | /* Or if the email is a verify subscriber email with an associated |
| 1595 | ** user... */ |
| 1596 | (alert_tables_exist() && |
| 1597 | db_exists( |
| 1598 | "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" |
| 1599 | " AND sverified",zEAddr)) |
| 1600 | ){ |
| 1601 | iErrLine = 3; |
| 1602 | zErr = "This email address is already claimed by another user"; |
| 1603 | }else{ |
| 1604 | /* If all of the tests above have passed, that means that the submitted |
| 1605 |
+50
-4
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -2095,15 +2095,19 @@ | ||
| 2095 | 2095 | ** REPO for a check-in or ticket that matches the |
| 2096 | 2096 | ** value of "name", then redirect to URL. There |
| 2097 | 2097 | ** can be multiple "redirect:" lines that are |
| 2098 | 2098 | ** processed in order. If the REPO is "*", then |
| 2099 | 2099 | ** an unconditional redirect to URL is taken. |
| 2100 | +** | |
| 2101 | +** jsmode: VALUE Specifies the delivery mode for JavaScript | |
| 2102 | +** files. See the help text for the --jsmode | |
| 2103 | +** flag of the http command. | |
| 2100 | 2104 | ** |
| 2101 | 2105 | ** Most CGI files contain only a "repository:" line. It is uncommon to |
| 2102 | 2106 | ** use any other option. |
| 2103 | 2107 | ** |
| 2104 | -** See also: http, server, winsrv | |
| 2108 | +** See also: [[http]], [[server]], [[winsrv]] | |
| 2105 | 2109 | */ |
| 2106 | 2110 | void cmd_cgi(void){ |
| 2107 | 2111 | const char *zFile; |
| 2108 | 2112 | const char *zNotFound = 0; |
| 2109 | 2113 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| @@ -2270,10 +2274,25 @@ | ||
| 2270 | 2274 | ** this directive is a silent no-op. |
| 2271 | 2275 | */ |
| 2272 | 2276 | skin_use_alternative(blob_str(&value)); |
| 2273 | 2277 | blob_reset(&value); |
| 2274 | 2278 | continue; |
| 2279 | + } | |
| 2280 | + if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ | |
| 2281 | + /* jsmode: MODE | |
| 2282 | + ** | |
| 2283 | + ** Change how JavaScript resources are delivered with each HTML | |
| 2284 | + ** page. MODE is "inline" to put all JS inline, or "separate" to | |
| 2285 | + ** cause each JS file to be requested using a separate HTTP request, | |
| 2286 | + ** or "bundled" to have all JS files to be fetched with a single | |
| 2287 | + ** auxiliary HTTP request. Noting, however, that "single" might | |
| 2288 | + ** actually mean more than one, depending on the script-timing | |
| 2289 | + ** requirements of any given page. | |
| 2290 | + */ | |
| 2291 | + builtin_set_js_delivery_mode(blob_str(&value),0); | |
| 2292 | + blob_reset(&value); | |
| 2293 | + continue; | |
| 2275 | 2294 | } |
| 2276 | 2295 | if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ |
| 2277 | 2296 | /* cgi-debug: FILENAME |
| 2278 | 2297 | ** |
| 2279 | 2298 | ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go |
| @@ -2455,10 +2474,23 @@ | ||
| 2455 | 2474 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 2456 | 2475 | ** --host NAME specify hostname of the server |
| 2457 | 2476 | ** --https signal a request coming in via https |
| 2458 | 2477 | ** --in FILE Take input from FILE instead of standard input |
| 2459 | 2478 | ** --ipaddr ADDR Assume the request comes from the given IP address |
| 2479 | +** --jsmode MODE Determine how JavaScript is delivered with pages. | |
| 2480 | +** Mode can be one of: | |
| 2481 | +** inline All JavaScript is inserted inline at | |
| 2482 | +** one or more points in the HTML file. | |
| 2483 | +** separate Separate HTTP requests are made for | |
| 2484 | +** each JavaScript file. | |
| 2485 | +** bundled Groups JavaScript files into one or | |
| 2486 | +** more bundled requests which | |
| 2487 | +** concatenate scripts together. | |
| 2488 | +** Depending on the needs of any given page, inline | |
| 2489 | +** and bundled modes might result in a single | |
| 2490 | +** amalgamated script or several, but both approaches | |
| 2491 | +** result in fewer HTTP requests than the separate mode. | |
| 2460 | 2492 | ** --localauth enable automatic login for local connections |
| 2461 | 2493 | ** --nocompress do not compress HTTP replies |
| 2462 | 2494 | ** --nodelay omit backoffice processing if it would delay process exit |
| 2463 | 2495 | ** --nojail drop root privilege but do not enter the chroot jail |
| 2464 | 2496 | ** --nossl signal that no SSL connections are available |
| @@ -2469,11 +2501,11 @@ | ||
| 2469 | 2501 | ** --skin LABEL Use override skin LABEL |
| 2470 | 2502 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2471 | 2503 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2472 | 2504 | ** only necessary when using SEE on Windows. |
| 2473 | 2505 | ** |
| 2474 | -** See also: cgi, server, winsrv | |
| 2506 | +** See also: [[cgi]], [[server]], [[winsrv]] | |
| 2475 | 2507 | */ |
| 2476 | 2508 | void cmd_http(void){ |
| 2477 | 2509 | const char *zIpAddr = 0; |
| 2478 | 2510 | const char *zNotFound; |
| 2479 | 2511 | const char *zHost; |
| @@ -2484,10 +2516,11 @@ | ||
| 2484 | 2516 | int useSCGI; |
| 2485 | 2517 | int noJail; |
| 2486 | 2518 | int allowRepoList; |
| 2487 | 2519 | |
| 2488 | 2520 | Th_InitTraceLog(); |
| 2521 | + builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); | |
| 2489 | 2522 | |
| 2490 | 2523 | /* The winhttp module passes the --files option as --files-urlenc with |
| 2491 | 2524 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 2492 | 2525 | ** shell. This option is for internal use and is undocumented. |
| 2493 | 2526 | */ |
| @@ -2574,11 +2607,11 @@ | ||
| 2574 | 2607 | /* |
| 2575 | 2608 | ** Note that the following command is used by ssh:// processing. |
| 2576 | 2609 | ** |
| 2577 | 2610 | ** COMMAND: test-http |
| 2578 | 2611 | ** |
| 2579 | -** Works like the http command but gives setup permission to all users. | |
| 2612 | +** Works like the [[http]] command but gives setup permission to all users. | |
| 2580 | 2613 | ** |
| 2581 | 2614 | ** Options: |
| 2582 | 2615 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2583 | 2616 | ** --usercap CAP user capability string. (Default: "sx") |
| 2584 | 2617 | ** |
| @@ -2714,10 +2747,22 @@ | ||
| 2714 | 2747 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 2715 | 2748 | ** --localauth enable automatic login for requests from localhost |
| 2716 | 2749 | ** --localhost listen on 127.0.0.1 only (always true for "ui") |
| 2717 | 2750 | ** --https Indicates that the input is coming through a reverse |
| 2718 | 2751 | ** proxy that has already translated HTTPS into HTTP. |
| 2752 | +** --jsmode MODE Determine how JavaScript is delivered with pages. | |
| 2753 | +** Mode can be one of: | |
| 2754 | +** inline All JavaScript is inserted inline at | |
| 2755 | +** the end of the HTML file. | |
| 2756 | +** separate Separate HTTP requests are made for | |
| 2757 | +** each JavaScript file. | |
| 2758 | +** bundled One single separate HTTP fetches all | |
| 2759 | +** JavaScript concatenated together. | |
| 2760 | +** Depending on the needs of any given page, inline | |
| 2761 | +** and bundled modes might result in a single | |
| 2762 | +** amalgamated script or several, but both approaches | |
| 2763 | +** result in fewer HTTP requests than the separate mode. | |
| 2719 | 2764 | ** --max-latency N Do not let any single HTTP request run for more than N |
| 2720 | 2765 | ** seconds (only works on unix) |
| 2721 | 2766 | ** --nocompress Do not compress HTTP replies |
| 2722 | 2767 | ** --nojail Drop root privileges but do not enter the chroot jail |
| 2723 | 2768 | ** --nossl signal that no SSL connections are available (Always |
| @@ -2730,11 +2775,11 @@ | ||
| 2730 | 2775 | ** --scgi Accept SCGI rather than HTTP |
| 2731 | 2776 | ** --skin LABEL Use override skin LABEL |
| 2732 | 2777 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2733 | 2778 | ** only necessary when using SEE on Windows. |
| 2734 | 2779 | ** |
| 2735 | -** See also: cgi, http, winsrv | |
| 2780 | +** See also: [[cgi]], [[http]], [[winsrv]] | |
| 2736 | 2781 | */ |
| 2737 | 2782 | void cmd_webserver(void){ |
| 2738 | 2783 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 2739 | 2784 | const char *zPort; /* Value of the --port option */ |
| 2740 | 2785 | const char *zBrowser; /* Name of web browser program */ |
| @@ -2760,10 +2805,11 @@ | ||
| 2760 | 2805 | |
| 2761 | 2806 | if( g.zErrlog==0 ){ |
| 2762 | 2807 | g.zErrlog = "-"; |
| 2763 | 2808 | } |
| 2764 | 2809 | g.zExtRoot = find_option("extroot",0,1); |
| 2810 | + builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); | |
| 2765 | 2811 | zFileGlob = find_option("files-urlenc",0,1); |
| 2766 | 2812 | if( zFileGlob ){ |
| 2767 | 2813 | char *z = mprintf("%s", zFileGlob); |
| 2768 | 2814 | dehttpize(z); |
| 2769 | 2815 | zFileGlob = z; |
| 2770 | 2816 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2095,15 +2095,19 @@ | |
| 2095 | ** REPO for a check-in or ticket that matches the |
| 2096 | ** value of "name", then redirect to URL. There |
| 2097 | ** can be multiple "redirect:" lines that are |
| 2098 | ** processed in order. If the REPO is "*", then |
| 2099 | ** an unconditional redirect to URL is taken. |
| 2100 | ** |
| 2101 | ** Most CGI files contain only a "repository:" line. It is uncommon to |
| 2102 | ** use any other option. |
| 2103 | ** |
| 2104 | ** See also: http, server, winsrv |
| 2105 | */ |
| 2106 | void cmd_cgi(void){ |
| 2107 | const char *zFile; |
| 2108 | const char *zNotFound = 0; |
| 2109 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| @@ -2270,10 +2274,25 @@ | |
| 2270 | ** this directive is a silent no-op. |
| 2271 | */ |
| 2272 | skin_use_alternative(blob_str(&value)); |
| 2273 | blob_reset(&value); |
| 2274 | continue; |
| 2275 | } |
| 2276 | if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ |
| 2277 | /* cgi-debug: FILENAME |
| 2278 | ** |
| 2279 | ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go |
| @@ -2455,10 +2474,23 @@ | |
| 2455 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 2456 | ** --host NAME specify hostname of the server |
| 2457 | ** --https signal a request coming in via https |
| 2458 | ** --in FILE Take input from FILE instead of standard input |
| 2459 | ** --ipaddr ADDR Assume the request comes from the given IP address |
| 2460 | ** --localauth enable automatic login for local connections |
| 2461 | ** --nocompress do not compress HTTP replies |
| 2462 | ** --nodelay omit backoffice processing if it would delay process exit |
| 2463 | ** --nojail drop root privilege but do not enter the chroot jail |
| 2464 | ** --nossl signal that no SSL connections are available |
| @@ -2469,11 +2501,11 @@ | |
| 2469 | ** --skin LABEL Use override skin LABEL |
| 2470 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2471 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2472 | ** only necessary when using SEE on Windows. |
| 2473 | ** |
| 2474 | ** See also: cgi, server, winsrv |
| 2475 | */ |
| 2476 | void cmd_http(void){ |
| 2477 | const char *zIpAddr = 0; |
| 2478 | const char *zNotFound; |
| 2479 | const char *zHost; |
| @@ -2484,10 +2516,11 @@ | |
| 2484 | int useSCGI; |
| 2485 | int noJail; |
| 2486 | int allowRepoList; |
| 2487 | |
| 2488 | Th_InitTraceLog(); |
| 2489 | |
| 2490 | /* The winhttp module passes the --files option as --files-urlenc with |
| 2491 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 2492 | ** shell. This option is for internal use and is undocumented. |
| 2493 | */ |
| @@ -2574,11 +2607,11 @@ | |
| 2574 | /* |
| 2575 | ** Note that the following command is used by ssh:// processing. |
| 2576 | ** |
| 2577 | ** COMMAND: test-http |
| 2578 | ** |
| 2579 | ** Works like the http command but gives setup permission to all users. |
| 2580 | ** |
| 2581 | ** Options: |
| 2582 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2583 | ** --usercap CAP user capability string. (Default: "sx") |
| 2584 | ** |
| @@ -2714,10 +2747,22 @@ | |
| 2714 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 2715 | ** --localauth enable automatic login for requests from localhost |
| 2716 | ** --localhost listen on 127.0.0.1 only (always true for "ui") |
| 2717 | ** --https Indicates that the input is coming through a reverse |
| 2718 | ** proxy that has already translated HTTPS into HTTP. |
| 2719 | ** --max-latency N Do not let any single HTTP request run for more than N |
| 2720 | ** seconds (only works on unix) |
| 2721 | ** --nocompress Do not compress HTTP replies |
| 2722 | ** --nojail Drop root privileges but do not enter the chroot jail |
| 2723 | ** --nossl signal that no SSL connections are available (Always |
| @@ -2730,11 +2775,11 @@ | |
| 2730 | ** --scgi Accept SCGI rather than HTTP |
| 2731 | ** --skin LABEL Use override skin LABEL |
| 2732 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2733 | ** only necessary when using SEE on Windows. |
| 2734 | ** |
| 2735 | ** See also: cgi, http, winsrv |
| 2736 | */ |
| 2737 | void cmd_webserver(void){ |
| 2738 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 2739 | const char *zPort; /* Value of the --port option */ |
| 2740 | const char *zBrowser; /* Name of web browser program */ |
| @@ -2760,10 +2805,11 @@ | |
| 2760 | |
| 2761 | if( g.zErrlog==0 ){ |
| 2762 | g.zErrlog = "-"; |
| 2763 | } |
| 2764 | g.zExtRoot = find_option("extroot",0,1); |
| 2765 | zFileGlob = find_option("files-urlenc",0,1); |
| 2766 | if( zFileGlob ){ |
| 2767 | char *z = mprintf("%s", zFileGlob); |
| 2768 | dehttpize(z); |
| 2769 | zFileGlob = z; |
| 2770 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2095,15 +2095,19 @@ | |
| 2095 | ** REPO for a check-in or ticket that matches the |
| 2096 | ** value of "name", then redirect to URL. There |
| 2097 | ** can be multiple "redirect:" lines that are |
| 2098 | ** processed in order. If the REPO is "*", then |
| 2099 | ** an unconditional redirect to URL is taken. |
| 2100 | ** |
| 2101 | ** jsmode: VALUE Specifies the delivery mode for JavaScript |
| 2102 | ** files. See the help text for the --jsmode |
| 2103 | ** flag of the http command. |
| 2104 | ** |
| 2105 | ** Most CGI files contain only a "repository:" line. It is uncommon to |
| 2106 | ** use any other option. |
| 2107 | ** |
| 2108 | ** See also: [[http]], [[server]], [[winsrv]] |
| 2109 | */ |
| 2110 | void cmd_cgi(void){ |
| 2111 | const char *zFile; |
| 2112 | const char *zNotFound = 0; |
| 2113 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| @@ -2270,10 +2274,25 @@ | |
| 2274 | ** this directive is a silent no-op. |
| 2275 | */ |
| 2276 | skin_use_alternative(blob_str(&value)); |
| 2277 | blob_reset(&value); |
| 2278 | continue; |
| 2279 | } |
| 2280 | if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ |
| 2281 | /* jsmode: MODE |
| 2282 | ** |
| 2283 | ** Change how JavaScript resources are delivered with each HTML |
| 2284 | ** page. MODE is "inline" to put all JS inline, or "separate" to |
| 2285 | ** cause each JS file to be requested using a separate HTTP request, |
| 2286 | ** or "bundled" to have all JS files to be fetched with a single |
| 2287 | ** auxiliary HTTP request. Noting, however, that "single" might |
| 2288 | ** actually mean more than one, depending on the script-timing |
| 2289 | ** requirements of any given page. |
| 2290 | */ |
| 2291 | builtin_set_js_delivery_mode(blob_str(&value),0); |
| 2292 | blob_reset(&value); |
| 2293 | continue; |
| 2294 | } |
| 2295 | if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ |
| 2296 | /* cgi-debug: FILENAME |
| 2297 | ** |
| 2298 | ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go |
| @@ -2455,10 +2474,23 @@ | |
| 2474 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 2475 | ** --host NAME specify hostname of the server |
| 2476 | ** --https signal a request coming in via https |
| 2477 | ** --in FILE Take input from FILE instead of standard input |
| 2478 | ** --ipaddr ADDR Assume the request comes from the given IP address |
| 2479 | ** --jsmode MODE Determine how JavaScript is delivered with pages. |
| 2480 | ** Mode can be one of: |
| 2481 | ** inline All JavaScript is inserted inline at |
| 2482 | ** one or more points in the HTML file. |
| 2483 | ** separate Separate HTTP requests are made for |
| 2484 | ** each JavaScript file. |
| 2485 | ** bundled Groups JavaScript files into one or |
| 2486 | ** more bundled requests which |
| 2487 | ** concatenate scripts together. |
| 2488 | ** Depending on the needs of any given page, inline |
| 2489 | ** and bundled modes might result in a single |
| 2490 | ** amalgamated script or several, but both approaches |
| 2491 | ** result in fewer HTTP requests than the separate mode. |
| 2492 | ** --localauth enable automatic login for local connections |
| 2493 | ** --nocompress do not compress HTTP replies |
| 2494 | ** --nodelay omit backoffice processing if it would delay process exit |
| 2495 | ** --nojail drop root privilege but do not enter the chroot jail |
| 2496 | ** --nossl signal that no SSL connections are available |
| @@ -2469,11 +2501,11 @@ | |
| 2501 | ** --skin LABEL Use override skin LABEL |
| 2502 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2503 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2504 | ** only necessary when using SEE on Windows. |
| 2505 | ** |
| 2506 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2507 | */ |
| 2508 | void cmd_http(void){ |
| 2509 | const char *zIpAddr = 0; |
| 2510 | const char *zNotFound; |
| 2511 | const char *zHost; |
| @@ -2484,10 +2516,11 @@ | |
| 2516 | int useSCGI; |
| 2517 | int noJail; |
| 2518 | int allowRepoList; |
| 2519 | |
| 2520 | Th_InitTraceLog(); |
| 2521 | builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); |
| 2522 | |
| 2523 | /* The winhttp module passes the --files option as --files-urlenc with |
| 2524 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 2525 | ** shell. This option is for internal use and is undocumented. |
| 2526 | */ |
| @@ -2574,11 +2607,11 @@ | |
| 2607 | /* |
| 2608 | ** Note that the following command is used by ssh:// processing. |
| 2609 | ** |
| 2610 | ** COMMAND: test-http |
| 2611 | ** |
| 2612 | ** Works like the [[http]] command but gives setup permission to all users. |
| 2613 | ** |
| 2614 | ** Options: |
| 2615 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2616 | ** --usercap CAP user capability string. (Default: "sx") |
| 2617 | ** |
| @@ -2714,10 +2747,22 @@ | |
| 2747 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 2748 | ** --localauth enable automatic login for requests from localhost |
| 2749 | ** --localhost listen on 127.0.0.1 only (always true for "ui") |
| 2750 | ** --https Indicates that the input is coming through a reverse |
| 2751 | ** proxy that has already translated HTTPS into HTTP. |
| 2752 | ** --jsmode MODE Determine how JavaScript is delivered with pages. |
| 2753 | ** Mode can be one of: |
| 2754 | ** inline All JavaScript is inserted inline at |
| 2755 | ** the end of the HTML file. |
| 2756 | ** separate Separate HTTP requests are made for |
| 2757 | ** each JavaScript file. |
| 2758 | ** bundled One single separate HTTP fetches all |
| 2759 | ** JavaScript concatenated together. |
| 2760 | ** Depending on the needs of any given page, inline |
| 2761 | ** and bundled modes might result in a single |
| 2762 | ** amalgamated script or several, but both approaches |
| 2763 | ** result in fewer HTTP requests than the separate mode. |
| 2764 | ** --max-latency N Do not let any single HTTP request run for more than N |
| 2765 | ** seconds (only works on unix) |
| 2766 | ** --nocompress Do not compress HTTP replies |
| 2767 | ** --nojail Drop root privileges but do not enter the chroot jail |
| 2768 | ** --nossl signal that no SSL connections are available (Always |
| @@ -2730,11 +2775,11 @@ | |
| 2775 | ** --scgi Accept SCGI rather than HTTP |
| 2776 | ** --skin LABEL Use override skin LABEL |
| 2777 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2778 | ** only necessary when using SEE on Windows. |
| 2779 | ** |
| 2780 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 2781 | */ |
| 2782 | void cmd_webserver(void){ |
| 2783 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 2784 | const char *zPort; /* Value of the --port option */ |
| 2785 | const char *zBrowser; /* Name of web browser program */ |
| @@ -2760,10 +2805,11 @@ | |
| 2805 | |
| 2806 | if( g.zErrlog==0 ){ |
| 2807 | g.zErrlog = "-"; |
| 2808 | } |
| 2809 | g.zExtRoot = find_option("extroot",0,1); |
| 2810 | builtin_set_js_delivery_mode(find_option("jsmode",0,1),0); |
| 2811 | zFileGlob = find_option("files-urlenc",0,1); |
| 2812 | if( zFileGlob ){ |
| 2813 | char *z = mprintf("%s", zFileGlob); |
| 2814 | dehttpize(z); |
| 2815 | zFileGlob = z; |
| 2816 |
+9
-13
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -154,11 +154,10 @@ | ||
| 154 | 154 | $(SRCDIR)/webmail.c \ |
| 155 | 155 | $(SRCDIR)/wiki.c \ |
| 156 | 156 | $(SRCDIR)/wikiformat.c \ |
| 157 | 157 | $(SRCDIR)/winfile.c \ |
| 158 | 158 | $(SRCDIR)/winhttp.c \ |
| 159 | - $(SRCDIR)/wysiwyg.c \ | |
| 160 | 159 | $(SRCDIR)/xfer.c \ |
| 161 | 160 | $(SRCDIR)/xfersetup.c \ |
| 162 | 161 | $(SRCDIR)/zip.c |
| 163 | 162 | |
| 164 | 163 | EXTRA_FILES = \ |
| @@ -224,14 +223,18 @@ | ||
| 224 | 223 | $(SRCDIR)/default.css \ |
| 225 | 224 | $(SRCDIR)/diff.tcl \ |
| 226 | 225 | $(SRCDIR)/forum.js \ |
| 227 | 226 | $(SRCDIR)/fossil.bootstrap.js \ |
| 228 | 227 | $(SRCDIR)/fossil.confirmer.js \ |
| 228 | + $(SRCDIR)/fossil.copybutton.js \ | |
| 229 | 229 | $(SRCDIR)/fossil.dom.js \ |
| 230 | 230 | $(SRCDIR)/fossil.fetch.js \ |
| 231 | + $(SRCDIR)/fossil.numbered-lines.js \ | |
| 231 | 232 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 232 | 233 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 234 | + $(SRCDIR)/fossil.page.wikiedit.js \ | |
| 235 | + $(SRCDIR)/fossil.popupwidget.js \ | |
| 233 | 236 | $(SRCDIR)/fossil.storage.js \ |
| 234 | 237 | $(SRCDIR)/fossil.tabs.js \ |
| 235 | 238 | $(SRCDIR)/graph.js \ |
| 236 | 239 | $(SRCDIR)/href.js \ |
| 237 | 240 | $(SRCDIR)/login.js \ |
| @@ -257,10 +260,11 @@ | ||
| 257 | 260 | $(SRCDIR)/sounds/d.wav \ |
| 258 | 261 | $(SRCDIR)/sounds/e.wav \ |
| 259 | 262 | $(SRCDIR)/sounds/f.wav \ |
| 260 | 263 | $(SRCDIR)/style.admin_log.css \ |
| 261 | 264 | $(SRCDIR)/style.fileedit.css \ |
| 265 | + $(SRCDIR)/style.wikiedit.css \ | |
| 262 | 266 | $(SRCDIR)/tree.js \ |
| 263 | 267 | $(SRCDIR)/useredit.js \ |
| 264 | 268 | $(SRCDIR)/wiki.wiki |
| 265 | 269 | |
| 266 | 270 | TRANS_SRC = \ |
| @@ -402,11 +406,10 @@ | ||
| 402 | 406 | $(OBJDIR)/webmail_.c \ |
| 403 | 407 | $(OBJDIR)/wiki_.c \ |
| 404 | 408 | $(OBJDIR)/wikiformat_.c \ |
| 405 | 409 | $(OBJDIR)/winfile_.c \ |
| 406 | 410 | $(OBJDIR)/winhttp_.c \ |
| 407 | - $(OBJDIR)/wysiwyg_.c \ | |
| 408 | 411 | $(OBJDIR)/xfer_.c \ |
| 409 | 412 | $(OBJDIR)/xfersetup_.c \ |
| 410 | 413 | $(OBJDIR)/zip_.c |
| 411 | 414 | |
| 412 | 415 | OBJ = \ |
| @@ -548,11 +551,10 @@ | ||
| 548 | 551 | $(OBJDIR)/webmail.o \ |
| 549 | 552 | $(OBJDIR)/wiki.o \ |
| 550 | 553 | $(OBJDIR)/wikiformat.o \ |
| 551 | 554 | $(OBJDIR)/winfile.o \ |
| 552 | 555 | $(OBJDIR)/winhttp.o \ |
| 553 | - $(OBJDIR)/wysiwyg.o \ | |
| 554 | 556 | $(OBJDIR)/xfer.o \ |
| 555 | 557 | $(OBJDIR)/xfersetup.o \ |
| 556 | 558 | $(OBJDIR)/zip.o |
| 557 | 559 | all: $(OBJDIR) $(APPNAME) |
| 558 | 560 | |
| @@ -598,13 +600,16 @@ | ||
| 598 | 600 | # the run to just those test cases. |
| 599 | 601 | # |
| 600 | 602 | test: $(OBJDIR) $(APPNAME) |
| 601 | 603 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 602 | 604 | |
| 603 | -$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion | |
| 605 | +$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h | |
| 604 | 606 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 605 | 607 | |
| 608 | +$(OBJDIR)/phony.h: | |
| 609 | + # Force rebuild of VERSION.h every time we run "make" | |
| 610 | + | |
| 606 | 611 | # Setup the options used to compile the included SQLite library. |
| 607 | 612 | SQLITE_OPTIONS = -DNDEBUG=1 \ |
| 608 | 613 | -DSQLITE_DQS=0 \ |
| 609 | 614 | -DSQLITE_THREADSAFE=0 \ |
| 610 | 615 | -DSQLITE_DEFAULT_MEMSTATUS=0 \ |
| @@ -881,11 +886,10 @@ | ||
| 881 | 886 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 882 | 887 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 883 | 888 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 884 | 889 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 885 | 890 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 886 | - $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ | |
| 887 | 891 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 888 | 892 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 889 | 893 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 890 | 894 | $(SRCDIR)/sqlite3.h \ |
| 891 | 895 | $(SRCDIR)/th.h \ |
| @@ -2012,18 +2016,10 @@ | ||
| 2012 | 2016 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2013 | 2017 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2014 | 2018 | |
| 2015 | 2019 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2016 | 2020 | |
| 2017 | -$(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(OBJDIR)/translate | |
| 2018 | - $(OBJDIR)/translate $(SRCDIR)/wysiwyg.c >$@ | |
| 2019 | - | |
| 2020 | -$(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h | |
| 2021 | - $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c | |
| 2022 | - | |
| 2023 | -$(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers | |
| 2024 | - | |
| 2025 | 2021 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(OBJDIR)/translate |
| 2026 | 2022 | $(OBJDIR)/translate $(SRCDIR)/xfer.c >$@ |
| 2027 | 2023 | |
| 2028 | 2024 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2029 | 2025 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2030 | 2026 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/webmail.c \ |
| 155 | $(SRCDIR)/wiki.c \ |
| 156 | $(SRCDIR)/wikiformat.c \ |
| 157 | $(SRCDIR)/winfile.c \ |
| 158 | $(SRCDIR)/winhttp.c \ |
| 159 | $(SRCDIR)/wysiwyg.c \ |
| 160 | $(SRCDIR)/xfer.c \ |
| 161 | $(SRCDIR)/xfersetup.c \ |
| 162 | $(SRCDIR)/zip.c |
| 163 | |
| 164 | EXTRA_FILES = \ |
| @@ -224,14 +223,18 @@ | |
| 224 | $(SRCDIR)/default.css \ |
| 225 | $(SRCDIR)/diff.tcl \ |
| 226 | $(SRCDIR)/forum.js \ |
| 227 | $(SRCDIR)/fossil.bootstrap.js \ |
| 228 | $(SRCDIR)/fossil.confirmer.js \ |
| 229 | $(SRCDIR)/fossil.dom.js \ |
| 230 | $(SRCDIR)/fossil.fetch.js \ |
| 231 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 232 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 233 | $(SRCDIR)/fossil.storage.js \ |
| 234 | $(SRCDIR)/fossil.tabs.js \ |
| 235 | $(SRCDIR)/graph.js \ |
| 236 | $(SRCDIR)/href.js \ |
| 237 | $(SRCDIR)/login.js \ |
| @@ -257,10 +260,11 @@ | |
| 257 | $(SRCDIR)/sounds/d.wav \ |
| 258 | $(SRCDIR)/sounds/e.wav \ |
| 259 | $(SRCDIR)/sounds/f.wav \ |
| 260 | $(SRCDIR)/style.admin_log.css \ |
| 261 | $(SRCDIR)/style.fileedit.css \ |
| 262 | $(SRCDIR)/tree.js \ |
| 263 | $(SRCDIR)/useredit.js \ |
| 264 | $(SRCDIR)/wiki.wiki |
| 265 | |
| 266 | TRANS_SRC = \ |
| @@ -402,11 +406,10 @@ | |
| 402 | $(OBJDIR)/webmail_.c \ |
| 403 | $(OBJDIR)/wiki_.c \ |
| 404 | $(OBJDIR)/wikiformat_.c \ |
| 405 | $(OBJDIR)/winfile_.c \ |
| 406 | $(OBJDIR)/winhttp_.c \ |
| 407 | $(OBJDIR)/wysiwyg_.c \ |
| 408 | $(OBJDIR)/xfer_.c \ |
| 409 | $(OBJDIR)/xfersetup_.c \ |
| 410 | $(OBJDIR)/zip_.c |
| 411 | |
| 412 | OBJ = \ |
| @@ -548,11 +551,10 @@ | |
| 548 | $(OBJDIR)/webmail.o \ |
| 549 | $(OBJDIR)/wiki.o \ |
| 550 | $(OBJDIR)/wikiformat.o \ |
| 551 | $(OBJDIR)/winfile.o \ |
| 552 | $(OBJDIR)/winhttp.o \ |
| 553 | $(OBJDIR)/wysiwyg.o \ |
| 554 | $(OBJDIR)/xfer.o \ |
| 555 | $(OBJDIR)/xfersetup.o \ |
| 556 | $(OBJDIR)/zip.o |
| 557 | all: $(OBJDIR) $(APPNAME) |
| 558 | |
| @@ -598,13 +600,16 @@ | |
| 598 | # the run to just those test cases. |
| 599 | # |
| 600 | test: $(OBJDIR) $(APPNAME) |
| 601 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 602 | |
| 603 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion |
| 604 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 605 | |
| 606 | # Setup the options used to compile the included SQLite library. |
| 607 | SQLITE_OPTIONS = -DNDEBUG=1 \ |
| 608 | -DSQLITE_DQS=0 \ |
| 609 | -DSQLITE_THREADSAFE=0 \ |
| 610 | -DSQLITE_DEFAULT_MEMSTATUS=0 \ |
| @@ -881,11 +886,10 @@ | |
| 881 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 882 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 883 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 884 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 885 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 886 | $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ |
| 887 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 888 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 889 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 890 | $(SRCDIR)/sqlite3.h \ |
| 891 | $(SRCDIR)/th.h \ |
| @@ -2012,18 +2016,10 @@ | |
| 2012 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2013 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2014 | |
| 2015 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2016 | |
| 2017 | $(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(OBJDIR)/translate |
| 2018 | $(OBJDIR)/translate $(SRCDIR)/wysiwyg.c >$@ |
| 2019 | |
| 2020 | $(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h |
| 2021 | $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c |
| 2022 | |
| 2023 | $(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers |
| 2024 | |
| 2025 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(OBJDIR)/translate |
| 2026 | $(OBJDIR)/translate $(SRCDIR)/xfer.c >$@ |
| 2027 | |
| 2028 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2029 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2030 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -154,11 +154,10 @@ | |
| 154 | $(SRCDIR)/webmail.c \ |
| 155 | $(SRCDIR)/wiki.c \ |
| 156 | $(SRCDIR)/wikiformat.c \ |
| 157 | $(SRCDIR)/winfile.c \ |
| 158 | $(SRCDIR)/winhttp.c \ |
| 159 | $(SRCDIR)/xfer.c \ |
| 160 | $(SRCDIR)/xfersetup.c \ |
| 161 | $(SRCDIR)/zip.c |
| 162 | |
| 163 | EXTRA_FILES = \ |
| @@ -224,14 +223,18 @@ | |
| 223 | $(SRCDIR)/default.css \ |
| 224 | $(SRCDIR)/diff.tcl \ |
| 225 | $(SRCDIR)/forum.js \ |
| 226 | $(SRCDIR)/fossil.bootstrap.js \ |
| 227 | $(SRCDIR)/fossil.confirmer.js \ |
| 228 | $(SRCDIR)/fossil.copybutton.js \ |
| 229 | $(SRCDIR)/fossil.dom.js \ |
| 230 | $(SRCDIR)/fossil.fetch.js \ |
| 231 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 232 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 233 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 234 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 235 | $(SRCDIR)/fossil.popupwidget.js \ |
| 236 | $(SRCDIR)/fossil.storage.js \ |
| 237 | $(SRCDIR)/fossil.tabs.js \ |
| 238 | $(SRCDIR)/graph.js \ |
| 239 | $(SRCDIR)/href.js \ |
| 240 | $(SRCDIR)/login.js \ |
| @@ -257,10 +260,11 @@ | |
| 260 | $(SRCDIR)/sounds/d.wav \ |
| 261 | $(SRCDIR)/sounds/e.wav \ |
| 262 | $(SRCDIR)/sounds/f.wav \ |
| 263 | $(SRCDIR)/style.admin_log.css \ |
| 264 | $(SRCDIR)/style.fileedit.css \ |
| 265 | $(SRCDIR)/style.wikiedit.css \ |
| 266 | $(SRCDIR)/tree.js \ |
| 267 | $(SRCDIR)/useredit.js \ |
| 268 | $(SRCDIR)/wiki.wiki |
| 269 | |
| 270 | TRANS_SRC = \ |
| @@ -402,11 +406,10 @@ | |
| 406 | $(OBJDIR)/webmail_.c \ |
| 407 | $(OBJDIR)/wiki_.c \ |
| 408 | $(OBJDIR)/wikiformat_.c \ |
| 409 | $(OBJDIR)/winfile_.c \ |
| 410 | $(OBJDIR)/winhttp_.c \ |
| 411 | $(OBJDIR)/xfer_.c \ |
| 412 | $(OBJDIR)/xfersetup_.c \ |
| 413 | $(OBJDIR)/zip_.c |
| 414 | |
| 415 | OBJ = \ |
| @@ -548,11 +551,10 @@ | |
| 551 | $(OBJDIR)/webmail.o \ |
| 552 | $(OBJDIR)/wiki.o \ |
| 553 | $(OBJDIR)/wikiformat.o \ |
| 554 | $(OBJDIR)/winfile.o \ |
| 555 | $(OBJDIR)/winhttp.o \ |
| 556 | $(OBJDIR)/xfer.o \ |
| 557 | $(OBJDIR)/xfersetup.o \ |
| 558 | $(OBJDIR)/zip.o |
| 559 | all: $(OBJDIR) $(APPNAME) |
| 560 | |
| @@ -598,13 +600,16 @@ | |
| 600 | # the run to just those test cases. |
| 601 | # |
| 602 | test: $(OBJDIR) $(APPNAME) |
| 603 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 604 | |
| 605 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h |
| 606 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 607 | |
| 608 | $(OBJDIR)/phony.h: |
| 609 | # Force rebuild of VERSION.h every time we run "make" |
| 610 | |
| 611 | # Setup the options used to compile the included SQLite library. |
| 612 | SQLITE_OPTIONS = -DNDEBUG=1 \ |
| 613 | -DSQLITE_DQS=0 \ |
| 614 | -DSQLITE_THREADSAFE=0 \ |
| 615 | -DSQLITE_DEFAULT_MEMSTATUS=0 \ |
| @@ -881,11 +886,10 @@ | |
| 886 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 887 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 888 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 889 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 890 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 891 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 892 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 893 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 894 | $(SRCDIR)/sqlite3.h \ |
| 895 | $(SRCDIR)/th.h \ |
| @@ -2012,18 +2016,10 @@ | |
| 2016 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2017 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2018 | |
| 2019 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2020 | |
| 2021 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(OBJDIR)/translate |
| 2022 | $(OBJDIR)/translate $(SRCDIR)/xfer.c >$@ |
| 2023 | |
| 2024 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2025 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2026 |
+13
-5
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -164,11 +164,10 @@ | ||
| 164 | 164 | webmail |
| 165 | 165 | wiki |
| 166 | 166 | wikiformat |
| 167 | 167 | winfile |
| 168 | 168 | winhttp |
| 169 | - wysiwyg | |
| 170 | 169 | xfer |
| 171 | 170 | xfersetup |
| 172 | 171 | zip |
| 173 | 172 | http_ssl |
| 174 | 173 | } |
| @@ -370,15 +369,18 @@ | ||
| 370 | 369 | # the run to just those test cases. |
| 371 | 370 | # |
| 372 | 371 | test: $(OBJDIR) $(APPNAME) |
| 373 | 372 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 374 | 373 | |
| 375 | -$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion | |
| 374 | +$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h | |
| 376 | 375 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ |
| 377 | 376 | $(SRCDIR)/../manifest \ |
| 378 | 377 | $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 379 | 378 | |
| 379 | +$(OBJDIR)/phony.h: | |
| 380 | + # Force rebuild of VERSION.h every time we run "make" | |
| 381 | + | |
| 380 | 382 | # Setup the options used to compile the included SQLite library. |
| 381 | 383 | SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>> |
| 382 | 384 | |
| 383 | 385 | # Setup the options used to compile the included SQLite shell. |
| 384 | 386 | SHELL_OPTIONS = <<<SHELL_OPTIONS>>> |
| @@ -1081,13 +1083,16 @@ | ||
| 1081 | 1083 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1082 | 1084 | # the repository after running the tests. |
| 1083 | 1085 | test: $(OBJDIR) $(APPNAME) |
| 1084 | 1086 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1085 | 1087 | |
| 1086 | -$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) | |
| 1088 | +$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h | |
| 1087 | 1089 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1088 | 1090 | |
| 1091 | +$(OBJDIR)/phony.h: | |
| 1092 | + # Force rebuild of VERSION.h every time "make" is run | |
| 1093 | + | |
| 1089 | 1094 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1090 | 1095 | # to 1. If it is set to 1, then there is no need to build or link |
| 1091 | 1096 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1092 | 1097 | # using -lsqlite3. |
| 1093 | 1098 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1907,12 +1912,15 @@ | ||
| 1907 | 1912 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1908 | 1913 | |
| 1909 | 1914 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1910 | 1915 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1911 | 1916 | |
| 1912 | -"$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" | |
| 1913 | - $** > $@ | |
| 1917 | +"$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" | |
| 1918 | + "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ | |
| 1919 | + | |
| 1920 | +"$(B)\phony.h" : | |
| 1921 | + rem Force rebuild of VERSION.h whenever nmake is run | |
| 1914 | 1922 | |
| 1915 | 1923 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1916 | 1924 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1917 | 1925 | |
| 1918 | 1926 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| 1919 | 1927 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -164,11 +164,10 @@ | |
| 164 | webmail |
| 165 | wiki |
| 166 | wikiformat |
| 167 | winfile |
| 168 | winhttp |
| 169 | wysiwyg |
| 170 | xfer |
| 171 | xfersetup |
| 172 | zip |
| 173 | http_ssl |
| 174 | } |
| @@ -370,15 +369,18 @@ | |
| 370 | # the run to just those test cases. |
| 371 | # |
| 372 | test: $(OBJDIR) $(APPNAME) |
| 373 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 374 | |
| 375 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion |
| 376 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ |
| 377 | $(SRCDIR)/../manifest \ |
| 378 | $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 379 | |
| 380 | # Setup the options used to compile the included SQLite library. |
| 381 | SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>> |
| 382 | |
| 383 | # Setup the options used to compile the included SQLite shell. |
| 384 | SHELL_OPTIONS = <<<SHELL_OPTIONS>>> |
| @@ -1081,13 +1083,16 @@ | |
| 1081 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1082 | # the repository after running the tests. |
| 1083 | test: $(OBJDIR) $(APPNAME) |
| 1084 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1085 | |
| 1086 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) |
| 1087 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1088 | |
| 1089 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1090 | # to 1. If it is set to 1, then there is no need to build or link |
| 1091 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1092 | # using -lsqlite3. |
| 1093 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1907,12 +1912,15 @@ | |
| 1907 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1908 | |
| 1909 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1910 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1911 | |
| 1912 | "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" |
| 1913 | $** > $@ |
| 1914 | |
| 1915 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1916 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1917 | |
| 1918 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| 1919 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -164,11 +164,10 @@ | |
| 164 | webmail |
| 165 | wiki |
| 166 | wikiformat |
| 167 | winfile |
| 168 | winhttp |
| 169 | xfer |
| 170 | xfersetup |
| 171 | zip |
| 172 | http_ssl |
| 173 | } |
| @@ -370,15 +369,18 @@ | |
| 369 | # the run to just those test cases. |
| 370 | # |
| 371 | test: $(OBJDIR) $(APPNAME) |
| 372 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 373 | |
| 374 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h |
| 375 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ |
| 376 | $(SRCDIR)/../manifest \ |
| 377 | $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 378 | |
| 379 | $(OBJDIR)/phony.h: |
| 380 | # Force rebuild of VERSION.h every time we run "make" |
| 381 | |
| 382 | # Setup the options used to compile the included SQLite library. |
| 383 | SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>> |
| 384 | |
| 385 | # Setup the options used to compile the included SQLite shell. |
| 386 | SHELL_OPTIONS = <<<SHELL_OPTIONS>>> |
| @@ -1081,13 +1083,16 @@ | |
| 1083 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1084 | # the repository after running the tests. |
| 1085 | test: $(OBJDIR) $(APPNAME) |
| 1086 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1087 | |
| 1088 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h |
| 1089 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1090 | |
| 1091 | $(OBJDIR)/phony.h: |
| 1092 | # Force rebuild of VERSION.h every time "make" is run |
| 1093 | |
| 1094 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1095 | # to 1. If it is set to 1, then there is no need to build or link |
| 1096 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1097 | # using -lsqlite3. |
| 1098 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1907,12 +1912,15 @@ | |
| 1912 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1913 | |
| 1914 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1915 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1916 | |
| 1917 | "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" |
| 1918 | "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ |
| 1919 | |
| 1920 | "$(B)\phony.h" : |
| 1921 | rem Force rebuild of VERSION.h whenever nmake is run |
| 1922 | |
| 1923 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1924 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1925 | |
| 1926 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| 1927 |
+7
| --- src/mkbuiltin.c | ||
| +++ src/mkbuiltin.c | ||
| @@ -62,10 +62,11 @@ | ||
| 62 | 62 | fclose(in); |
| 63 | 63 | z[got] = 0; |
| 64 | 64 | return z; |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | +#ifndef FOSSIL_DEBUG | |
| 67 | 68 | /* |
| 68 | 69 | ** Try to compress a javascript file by removing unnecessary whitespace. |
| 69 | 70 | ** |
| 70 | 71 | ** Warning: This compression routine does not necessarily work for any |
| 71 | 72 | ** arbitrary Javascript source file. But it should work ok for the |
| @@ -88,10 +89,11 @@ | ||
| 88 | 89 | i = k-1; |
| 89 | 90 | continue; |
| 90 | 91 | } |
| 91 | 92 | } |
| 92 | 93 | if( c=='\n' ){ |
| 94 | + if( j==0 ) continue; | |
| 93 | 95 | while( j>0 && isspace(z[j-1]) ) j--; |
| 94 | 96 | z[j++] = '\n'; |
| 95 | 97 | while( i+1<n && isspace(z[i+1]) ) i++; |
| 96 | 98 | continue; |
| 97 | 99 | } |
| @@ -98,10 +100,11 @@ | ||
| 98 | 100 | z[j++] = c; |
| 99 | 101 | } |
| 100 | 102 | z[j] = 0; |
| 101 | 103 | *pn = j; |
| 102 | 104 | } |
| 105 | +#endif /* FOSSIL_DEBUG */ | |
| 103 | 106 | |
| 104 | 107 | /* |
| 105 | 108 | ** There is an instance of the following for each file translated. |
| 106 | 109 | */ |
| 107 | 110 | typedef struct Resource Resource; |
| @@ -278,11 +281,13 @@ | ||
| 278 | 281 | int nRes; |
| 279 | 282 | unsigned char *pData; |
| 280 | 283 | int nErr = 0; |
| 281 | 284 | int nSkip; |
| 282 | 285 | int nPrefix = 0; |
| 286 | +#ifndef FOSSIL_DEBUG | |
| 283 | 287 | int nName; |
| 288 | +#endif | |
| 284 | 289 | |
| 285 | 290 | if( argc==1 ){ |
| 286 | 291 | fprintf(stderr, "usage\t:%s " |
| 287 | 292 | "[--prefix path] [--reslist file] [resource-file1 ...]\n", |
| 288 | 293 | argv[0] |
| @@ -349,19 +354,21 @@ | ||
| 349 | 354 | while( pData[nSkip]=='#' ){ |
| 350 | 355 | while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } |
| 351 | 356 | if( pData[nSkip]=='\n' ) nSkip++; |
| 352 | 357 | } |
| 353 | 358 | |
| 359 | +#ifndef FOSSIL_DEBUG | |
| 354 | 360 | /* Compress javascript source files */ |
| 355 | 361 | nName = (int)strlen(aRes[i].zName); |
| 356 | 362 | if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) |
| 357 | 363 | || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) |
| 358 | 364 | ){ |
| 359 | 365 | int x = sz-nSkip; |
| 360 | 366 | compressJavascript(pData+nSkip, &x); |
| 361 | 367 | sz = x + nSkip; |
| 362 | 368 | } |
| 369 | +#endif | |
| 363 | 370 | |
| 364 | 371 | aRes[i].nByte = sz - nSkip; |
| 365 | 372 | aRes[i].idx = i; |
| 366 | 373 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 367 | 374 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 368 | 375 |
| --- src/mkbuiltin.c | |
| +++ src/mkbuiltin.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | fclose(in); |
| 63 | z[got] = 0; |
| 64 | return z; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 | ** Try to compress a javascript file by removing unnecessary whitespace. |
| 69 | ** |
| 70 | ** Warning: This compression routine does not necessarily work for any |
| 71 | ** arbitrary Javascript source file. But it should work ok for the |
| @@ -88,10 +89,11 @@ | |
| 88 | i = k-1; |
| 89 | continue; |
| 90 | } |
| 91 | } |
| 92 | if( c=='\n' ){ |
| 93 | while( j>0 && isspace(z[j-1]) ) j--; |
| 94 | z[j++] = '\n'; |
| 95 | while( i+1<n && isspace(z[i+1]) ) i++; |
| 96 | continue; |
| 97 | } |
| @@ -98,10 +100,11 @@ | |
| 98 | z[j++] = c; |
| 99 | } |
| 100 | z[j] = 0; |
| 101 | *pn = j; |
| 102 | } |
| 103 | |
| 104 | /* |
| 105 | ** There is an instance of the following for each file translated. |
| 106 | */ |
| 107 | typedef struct Resource Resource; |
| @@ -278,11 +281,13 @@ | |
| 278 | int nRes; |
| 279 | unsigned char *pData; |
| 280 | int nErr = 0; |
| 281 | int nSkip; |
| 282 | int nPrefix = 0; |
| 283 | int nName; |
| 284 | |
| 285 | if( argc==1 ){ |
| 286 | fprintf(stderr, "usage\t:%s " |
| 287 | "[--prefix path] [--reslist file] [resource-file1 ...]\n", |
| 288 | argv[0] |
| @@ -349,19 +354,21 @@ | |
| 349 | while( pData[nSkip]=='#' ){ |
| 350 | while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } |
| 351 | if( pData[nSkip]=='\n' ) nSkip++; |
| 352 | } |
| 353 | |
| 354 | /* Compress javascript source files */ |
| 355 | nName = (int)strlen(aRes[i].zName); |
| 356 | if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) |
| 357 | || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) |
| 358 | ){ |
| 359 | int x = sz-nSkip; |
| 360 | compressJavascript(pData+nSkip, &x); |
| 361 | sz = x + nSkip; |
| 362 | } |
| 363 | |
| 364 | aRes[i].nByte = sz - nSkip; |
| 365 | aRes[i].idx = i; |
| 366 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 367 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 368 |
| --- src/mkbuiltin.c | |
| +++ src/mkbuiltin.c | |
| @@ -62,10 +62,11 @@ | |
| 62 | fclose(in); |
| 63 | z[got] = 0; |
| 64 | return z; |
| 65 | } |
| 66 | |
| 67 | #ifndef FOSSIL_DEBUG |
| 68 | /* |
| 69 | ** Try to compress a javascript file by removing unnecessary whitespace. |
| 70 | ** |
| 71 | ** Warning: This compression routine does not necessarily work for any |
| 72 | ** arbitrary Javascript source file. But it should work ok for the |
| @@ -88,10 +89,11 @@ | |
| 89 | i = k-1; |
| 90 | continue; |
| 91 | } |
| 92 | } |
| 93 | if( c=='\n' ){ |
| 94 | if( j==0 ) continue; |
| 95 | while( j>0 && isspace(z[j-1]) ) j--; |
| 96 | z[j++] = '\n'; |
| 97 | while( i+1<n && isspace(z[i+1]) ) i++; |
| 98 | continue; |
| 99 | } |
| @@ -98,10 +100,11 @@ | |
| 100 | z[j++] = c; |
| 101 | } |
| 102 | z[j] = 0; |
| 103 | *pn = j; |
| 104 | } |
| 105 | #endif /* FOSSIL_DEBUG */ |
| 106 | |
| 107 | /* |
| 108 | ** There is an instance of the following for each file translated. |
| 109 | */ |
| 110 | typedef struct Resource Resource; |
| @@ -278,11 +281,13 @@ | |
| 281 | int nRes; |
| 282 | unsigned char *pData; |
| 283 | int nErr = 0; |
| 284 | int nSkip; |
| 285 | int nPrefix = 0; |
| 286 | #ifndef FOSSIL_DEBUG |
| 287 | int nName; |
| 288 | #endif |
| 289 | |
| 290 | if( argc==1 ){ |
| 291 | fprintf(stderr, "usage\t:%s " |
| 292 | "[--prefix path] [--reslist file] [resource-file1 ...]\n", |
| 293 | argv[0] |
| @@ -349,19 +354,21 @@ | |
| 354 | while( pData[nSkip]=='#' ){ |
| 355 | while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } |
| 356 | if( pData[nSkip]=='\n' ) nSkip++; |
| 357 | } |
| 358 | |
| 359 | #ifndef FOSSIL_DEBUG |
| 360 | /* Compress javascript source files */ |
| 361 | nName = (int)strlen(aRes[i].zName); |
| 362 | if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0) |
| 363 | || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0) |
| 364 | ){ |
| 365 | int x = sz-nSkip; |
| 366 | compressJavascript(pData+nSkip, &x); |
| 367 | sz = x + nSkip; |
| 368 | } |
| 369 | #endif |
| 370 | |
| 371 | aRes[i].nByte = sz - nSkip; |
| 372 | aRes[i].idx = i; |
| 373 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 374 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 375 |
-1
| --- src/mkindex.c | ||
| +++ src/mkindex.c | ||
| @@ -391,11 +391,10 @@ | ||
| 391 | 391 | */ |
| 392 | 392 | void build_table(void){ |
| 393 | 393 | int i; |
| 394 | 394 | int nWeb = 0; |
| 395 | 395 | int mxLen = 0; |
| 396 | - int len; | |
| 397 | 396 | |
| 398 | 397 | qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); |
| 399 | 398 | |
| 400 | 399 | printf( |
| 401 | 400 | "/* Automatically generated code\n" |
| 402 | 401 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -391,11 +391,10 @@ | |
| 391 | */ |
| 392 | void build_table(void){ |
| 393 | int i; |
| 394 | int nWeb = 0; |
| 395 | int mxLen = 0; |
| 396 | int len; |
| 397 | |
| 398 | qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); |
| 399 | |
| 400 | printf( |
| 401 | "/* Automatically generated code\n" |
| 402 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -391,11 +391,10 @@ | |
| 391 | */ |
| 392 | void build_table(void){ |
| 393 | int i; |
| 394 | int nWeb = 0; |
| 395 | int mxLen = 0; |
| 396 | |
| 397 | qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); |
| 398 | |
| 399 | printf( |
| 400 | "/* Automatically generated code\n" |
| 401 |
+4
-1
| --- src/piechart.c | ||
| +++ src/piechart.c | ||
| @@ -142,11 +142,14 @@ | ||
| 142 | 142 | if( r<0.33333*r2 ) r = 0.33333*r2; |
| 143 | 143 | h = 0; |
| 144 | 144 | zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; |
| 145 | 145 | |
| 146 | 146 | db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); |
| 147 | - if( db_step(&q)!=SQLITE_ROW ) return; | |
| 147 | + if( db_step(&q)!=SQLITE_ROW ){ | |
| 148 | + db_finalize(&q); | |
| 149 | + return; | |
| 150 | + } | |
| 148 | 151 | rTotal = db_column_double(&q, 0); |
| 149 | 152 | nTotal = db_column_int(&q, 1); |
| 150 | 153 | db_finalize(&q); |
| 151 | 154 | rTooSmall = 0.0; |
| 152 | 155 | nTooSmall = 0; |
| 153 | 156 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -142,11 +142,14 @@ | |
| 142 | if( r<0.33333*r2 ) r = 0.33333*r2; |
| 143 | h = 0; |
| 144 | zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; |
| 145 | |
| 146 | db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); |
| 147 | if( db_step(&q)!=SQLITE_ROW ) return; |
| 148 | rTotal = db_column_double(&q, 0); |
| 149 | nTotal = db_column_int(&q, 1); |
| 150 | db_finalize(&q); |
| 151 | rTooSmall = 0.0; |
| 152 | nTooSmall = 0; |
| 153 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -142,11 +142,14 @@ | |
| 142 | if( r<0.33333*r2 ) r = 0.33333*r2; |
| 143 | h = 0; |
| 144 | zFg = skin_detail_boolean("white-foreground") ? "white" : "black"; |
| 145 | |
| 146 | db_prepare(&q, "SELECT sum(amt), count(*) FROM piechart"); |
| 147 | if( db_step(&q)!=SQLITE_ROW ){ |
| 148 | db_finalize(&q); |
| 149 | return; |
| 150 | } |
| 151 | rTotal = db_column_double(&q, 0); |
| 152 | nTotal = db_column_int(&q, 1); |
| 153 | db_finalize(&q); |
| 154 | rTooSmall = 0.0; |
| 155 | nTooSmall = 0; |
| 156 |
+2
-2
| --- src/publish.c | ||
| +++ src/publish.c | ||
| @@ -21,11 +21,11 @@ | ||
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "publish.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 25 | /* |
| 26 | -** COMMAND: unpublished | |
| 26 | +** COMMAND: unpublished* | |
| 27 | 27 | ** |
| 28 | 28 | ** Usage: %fossil unpublished ?OPTIONS? |
| 29 | 29 | ** |
| 30 | 30 | ** Show a list of unpublished or "private" artifacts. Unpublished artifacts |
| 31 | 31 | ** will never push and hence will not be shared with collaborators. |
| @@ -50,11 +50,11 @@ | ||
| 50 | 50 | " AND event.type='ci')", 0); |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | /* |
| 55 | -** COMMAND: publish | |
| 55 | +** COMMAND: publish* | |
| 56 | 56 | ** |
| 57 | 57 | ** Usage: %fossil publish ?--only? TAGS... |
| 58 | 58 | ** |
| 59 | 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 | 61 |
| --- src/publish.c | |
| +++ src/publish.c | |
| @@ -21,11 +21,11 @@ | |
| 21 | #include "config.h" |
| 22 | #include "publish.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** COMMAND: unpublished |
| 27 | ** |
| 28 | ** Usage: %fossil unpublished ?OPTIONS? |
| 29 | ** |
| 30 | ** Show a list of unpublished or "private" artifacts. Unpublished artifacts |
| 31 | ** will never push and hence will not be shared with collaborators. |
| @@ -50,11 +50,11 @@ | |
| 50 | " AND event.type='ci')", 0); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /* |
| 55 | ** COMMAND: publish |
| 56 | ** |
| 57 | ** Usage: %fossil publish ?--only? TAGS... |
| 58 | ** |
| 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 |
| --- src/publish.c | |
| +++ src/publish.c | |
| @@ -21,11 +21,11 @@ | |
| 21 | #include "config.h" |
| 22 | #include "publish.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** COMMAND: unpublished* |
| 27 | ** |
| 28 | ** Usage: %fossil unpublished ?OPTIONS? |
| 29 | ** |
| 30 | ** Show a list of unpublished or "private" artifacts. Unpublished artifacts |
| 31 | ** will never push and hence will not be shared with collaborators. |
| @@ -50,11 +50,11 @@ | |
| 50 | " AND event.type='ci')", 0); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /* |
| 55 | ** COMMAND: publish* |
| 56 | ** |
| 57 | ** Usage: %fossil publish ?--only? TAGS... |
| 58 | ** |
| 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 |
-11
| --- src/purge.c | ||
| +++ src/purge.c | ||
| @@ -507,21 +507,10 @@ | ||
| 507 | 507 | ** |
| 508 | 508 | ** COMMON OPTIONS: |
| 509 | 509 | ** |
| 510 | 510 | ** --explain Make no changes, but show what would happen. |
| 511 | 511 | ** --dry-run An alias for --explain |
| 512 | -** | |
| 513 | -** SUMMARY: | |
| 514 | -** * fossil purge artifacts HASH.. [OPTIONS] | |
| 515 | -** * fossil purge cat HASH... | |
| 516 | -** * fossil purge checkins TAGS... [OPTIONS] | |
| 517 | -** * fossil purge files FILENAME... [OPTIONS] | |
| 518 | -** * fossil purge list | |
| 519 | -** * fossil purge obliterate ID... | |
| 520 | -** * fossil purge tickets NAME... [OPTIONS] | |
| 521 | -** * fossil purge undo ID | |
| 522 | -** * fossil purge wiki NAME... [OPTIONS] | |
| 523 | 512 | */ |
| 524 | 513 | void purge_cmd(void){ |
| 525 | 514 | int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; |
| 526 | 515 | const char *zSubcmd; |
| 527 | 516 | int n; |
| 528 | 517 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -507,21 +507,10 @@ | |
| 507 | ** |
| 508 | ** COMMON OPTIONS: |
| 509 | ** |
| 510 | ** --explain Make no changes, but show what would happen. |
| 511 | ** --dry-run An alias for --explain |
| 512 | ** |
| 513 | ** SUMMARY: |
| 514 | ** * fossil purge artifacts HASH.. [OPTIONS] |
| 515 | ** * fossil purge cat HASH... |
| 516 | ** * fossil purge checkins TAGS... [OPTIONS] |
| 517 | ** * fossil purge files FILENAME... [OPTIONS] |
| 518 | ** * fossil purge list |
| 519 | ** * fossil purge obliterate ID... |
| 520 | ** * fossil purge tickets NAME... [OPTIONS] |
| 521 | ** * fossil purge undo ID |
| 522 | ** * fossil purge wiki NAME... [OPTIONS] |
| 523 | */ |
| 524 | void purge_cmd(void){ |
| 525 | int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; |
| 526 | const char *zSubcmd; |
| 527 | int n; |
| 528 |
| --- src/purge.c | |
| +++ src/purge.c | |
| @@ -507,21 +507,10 @@ | |
| 507 | ** |
| 508 | ** COMMON OPTIONS: |
| 509 | ** |
| 510 | ** --explain Make no changes, but show what would happen. |
| 511 | ** --dry-run An alias for --explain |
| 512 | */ |
| 513 | void purge_cmd(void){ |
| 514 | int purgeFlags = PURGE_MOVETO_GRAVEYARD | PURGE_PRINT_SUMMARY; |
| 515 | const char *zSubcmd; |
| 516 | int n; |
| 517 |
-6
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -601,12 +601,10 @@ | ||
| 601 | 601 | ** --quiet Only show output if there are errors |
| 602 | 602 | ** --randomize Scan artifacts in a random order |
| 603 | 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | -** | |
| 607 | -** See also: deconstruct, reconstruct | |
| 608 | 606 | */ |
| 609 | 607 | void rebuild_database(void){ |
| 610 | 608 | int forceFlag; |
| 611 | 609 | int randomizeFlag; |
| 612 | 610 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | ||
| 1203 | 1201 | ** Options: |
| 1204 | 1202 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1205 | 1203 | ** file .rid in DIRECTORY. |
| 1206 | 1204 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1207 | 1205 | ** DIRECTORY as private in the new Fossil repository. |
| 1208 | -** | |
| 1209 | -** See also: deconstruct, rebuild | |
| 1210 | 1206 | */ |
| 1211 | 1207 | void reconstruct_cmd(void) { |
| 1212 | 1208 | char *zPassword; |
| 1213 | 1209 | int fKeepPrivate; |
| 1214 | 1210 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | ||
| 1278 | 1274 | ** subdirectories to N. |
| 1279 | 1275 | ** --private Include private artifacts. |
| 1280 | 1276 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1281 | 1277 | ** .private in the DESTINATION directory (implies |
| 1282 | 1278 | ** the --private option). |
| 1283 | -** | |
| 1284 | -** See also: reconstruct, rebuild | |
| 1285 | 1279 | */ |
| 1286 | 1280 | void deconstruct_cmd(void){ |
| 1287 | 1281 | const char *zPrefixOpt; |
| 1288 | 1282 | Stmt s; |
| 1289 | 1283 | int privateFlag; |
| 1290 | 1284 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -601,12 +601,10 @@ | |
| 601 | ** --quiet Only show output if there are errors |
| 602 | ** --randomize Scan artifacts in a random order |
| 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | ** |
| 607 | ** See also: deconstruct, reconstruct |
| 608 | */ |
| 609 | void rebuild_database(void){ |
| 610 | int forceFlag; |
| 611 | int randomizeFlag; |
| 612 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | |
| 1203 | ** Options: |
| 1204 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1205 | ** file .rid in DIRECTORY. |
| 1206 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1207 | ** DIRECTORY as private in the new Fossil repository. |
| 1208 | ** |
| 1209 | ** See also: deconstruct, rebuild |
| 1210 | */ |
| 1211 | void reconstruct_cmd(void) { |
| 1212 | char *zPassword; |
| 1213 | int fKeepPrivate; |
| 1214 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | |
| 1278 | ** subdirectories to N. |
| 1279 | ** --private Include private artifacts. |
| 1280 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1281 | ** .private in the DESTINATION directory (implies |
| 1282 | ** the --private option). |
| 1283 | ** |
| 1284 | ** See also: reconstruct, rebuild |
| 1285 | */ |
| 1286 | void deconstruct_cmd(void){ |
| 1287 | const char *zPrefixOpt; |
| 1288 | Stmt s; |
| 1289 | int privateFlag; |
| 1290 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -601,12 +601,10 @@ | |
| 601 | ** --quiet Only show output if there are errors |
| 602 | ** --randomize Scan artifacts in a random order |
| 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | */ |
| 607 | void rebuild_database(void){ |
| 608 | int forceFlag; |
| 609 | int randomizeFlag; |
| 610 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | |
| 1201 | ** Options: |
| 1202 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1203 | ** file .rid in DIRECTORY. |
| 1204 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1205 | ** DIRECTORY as private in the new Fossil repository. |
| 1206 | */ |
| 1207 | void reconstruct_cmd(void) { |
| 1208 | char *zPassword; |
| 1209 | int fKeepPrivate; |
| 1210 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | |
| 1274 | ** subdirectories to N. |
| 1275 | ** --private Include private artifacts. |
| 1276 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1277 | ** .private in the DESTINATION directory (implies |
| 1278 | ** the --private option). |
| 1279 | */ |
| 1280 | void deconstruct_cmd(void){ |
| 1281 | const char *zPrefixOpt; |
| 1282 | Stmt s; |
| 1283 | int privateFlag; |
| 1284 |
-6
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -601,12 +601,10 @@ | ||
| 601 | 601 | ** --quiet Only show output if there are errors |
| 602 | 602 | ** --randomize Scan artifacts in a random order |
| 603 | 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | -** | |
| 607 | -** See also: deconstruct, reconstruct | |
| 608 | 606 | */ |
| 609 | 607 | void rebuild_database(void){ |
| 610 | 608 | int forceFlag; |
| 611 | 609 | int randomizeFlag; |
| 612 | 610 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | ||
| 1203 | 1201 | ** Options: |
| 1204 | 1202 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1205 | 1203 | ** file .rid in DIRECTORY. |
| 1206 | 1204 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1207 | 1205 | ** DIRECTORY as private in the new Fossil repository. |
| 1208 | -** | |
| 1209 | -** See also: deconstruct, rebuild | |
| 1210 | 1206 | */ |
| 1211 | 1207 | void reconstruct_cmd(void) { |
| 1212 | 1208 | char *zPassword; |
| 1213 | 1209 | int fKeepPrivate; |
| 1214 | 1210 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | ||
| 1278 | 1274 | ** subdirectories to N. |
| 1279 | 1275 | ** --private Include private artifacts. |
| 1280 | 1276 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1281 | 1277 | ** .private in the DESTINATION directory (implies |
| 1282 | 1278 | ** the --private option). |
| 1283 | -** | |
| 1284 | -** See also: reconstruct, rebuild | |
| 1285 | 1279 | */ |
| 1286 | 1280 | void deconstruct_cmd(void){ |
| 1287 | 1281 | const char *zPrefixOpt; |
| 1288 | 1282 | Stmt s; |
| 1289 | 1283 | int privateFlag; |
| 1290 | 1284 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -601,12 +601,10 @@ | |
| 601 | ** --quiet Only show output if there are errors |
| 602 | ** --randomize Scan artifacts in a random order |
| 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | ** |
| 607 | ** See also: deconstruct, reconstruct |
| 608 | */ |
| 609 | void rebuild_database(void){ |
| 610 | int forceFlag; |
| 611 | int randomizeFlag; |
| 612 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | |
| 1203 | ** Options: |
| 1204 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1205 | ** file .rid in DIRECTORY. |
| 1206 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1207 | ** DIRECTORY as private in the new Fossil repository. |
| 1208 | ** |
| 1209 | ** See also: deconstruct, rebuild |
| 1210 | */ |
| 1211 | void reconstruct_cmd(void) { |
| 1212 | char *zPassword; |
| 1213 | int fKeepPrivate; |
| 1214 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | |
| 1278 | ** subdirectories to N. |
| 1279 | ** --private Include private artifacts. |
| 1280 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1281 | ** .private in the DESTINATION directory (implies |
| 1282 | ** the --private option). |
| 1283 | ** |
| 1284 | ** See also: reconstruct, rebuild |
| 1285 | */ |
| 1286 | void deconstruct_cmd(void){ |
| 1287 | const char *zPrefixOpt; |
| 1288 | Stmt s; |
| 1289 | int privateFlag; |
| 1290 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -601,12 +601,10 @@ | |
| 601 | ** --quiet Only show output if there are errors |
| 602 | ** --randomize Scan artifacts in a random order |
| 603 | ** --stats Show artifact statistics after rebuilding |
| 604 | ** --vacuum Run VACUUM on the database after rebuilding |
| 605 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 606 | */ |
| 607 | void rebuild_database(void){ |
| 608 | int forceFlag; |
| 609 | int randomizeFlag; |
| 610 | int errCnt = 0; |
| @@ -1203,12 +1201,10 @@ | |
| 1201 | ** Options: |
| 1202 | ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the |
| 1203 | ** file .rid in DIRECTORY. |
| 1204 | ** -P|--keep-private Mark the artifacts listed in the file .private in |
| 1205 | ** DIRECTORY as private in the new Fossil repository. |
| 1206 | */ |
| 1207 | void reconstruct_cmd(void) { |
| 1208 | char *zPassword; |
| 1209 | int fKeepPrivate; |
| 1210 | fKeepRid1 = find_option("keep-rid1","K",0)!=0; |
| @@ -1278,12 +1274,10 @@ | |
| 1274 | ** subdirectories to N. |
| 1275 | ** --private Include private artifacts. |
| 1276 | ** -P|--keep-private Save the list of private artifacts to the file |
| 1277 | ** .private in the DESTINATION directory (implies |
| 1278 | ** the --private option). |
| 1279 | */ |
| 1280 | void deconstruct_cmd(void){ |
| 1281 | const char *zPrefixOpt; |
| 1282 | Stmt s; |
| 1283 | int privateFlag; |
| 1284 |
+9
-1
| --- src/sbsdiff.js | ||
| +++ src/sbsdiff.js | ||
| @@ -22,12 +22,20 @@ | ||
| 22 | 22 | if( !len ) return; |
| 23 | 23 | txtCols[0].scrollLeft += len; |
| 24 | 24 | return false; |
| 25 | 25 | }; |
| 26 | 26 | } |
| 27 | - document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); | |
| 27 | + var i, diffs = document.querySelectorAll('.sbsdiffcols') | |
| 28 | + /* Maintenance reminder: using forEach() here breaks | |
| 29 | + MSIE<=11, and we need to keep those browsers working on | |
| 30 | + the /info page. */; | |
| 31 | + for(i=0; i<diffs.length; i++){ | |
| 32 | + initSbsDiff(diffs[i]); | |
| 33 | + } | |
| 28 | 34 | if(window.fossil && fossil.page){ |
| 35 | + /* Here we can use forEach() because the pages which use | |
| 36 | + fossil.pages only work in HTML5-compliant browsers. */ | |
| 29 | 37 | fossil.page.tweakSbsDiffs = function(){ |
| 30 | 38 | document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); |
| 31 | 39 | }; |
| 32 | 40 | } |
| 33 | 41 | })(); |
| 34 | 42 |
| --- src/sbsdiff.js | |
| +++ src/sbsdiff.js | |
| @@ -22,12 +22,20 @@ | |
| 22 | if( !len ) return; |
| 23 | txtCols[0].scrollLeft += len; |
| 24 | return false; |
| 25 | }; |
| 26 | } |
| 27 | document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); |
| 28 | if(window.fossil && fossil.page){ |
| 29 | fossil.page.tweakSbsDiffs = function(){ |
| 30 | document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); |
| 31 | }; |
| 32 | } |
| 33 | })(); |
| 34 |
| --- src/sbsdiff.js | |
| +++ src/sbsdiff.js | |
| @@ -22,12 +22,20 @@ | |
| 22 | if( !len ) return; |
| 23 | txtCols[0].scrollLeft += len; |
| 24 | return false; |
| 25 | }; |
| 26 | } |
| 27 | var i, diffs = document.querySelectorAll('.sbsdiffcols') |
| 28 | /* Maintenance reminder: using forEach() here breaks |
| 29 | MSIE<=11, and we need to keep those browsers working on |
| 30 | the /info page. */; |
| 31 | for(i=0; i<diffs.length; i++){ |
| 32 | initSbsDiff(diffs[i]); |
| 33 | } |
| 34 | if(window.fossil && fossil.page){ |
| 35 | /* Here we can use forEach() because the pages which use |
| 36 | fossil.pages only work in HTML5-compliant browsers. */ |
| 37 | fossil.page.tweakSbsDiffs = function(){ |
| 38 | document.querySelectorAll('.sbsdiffcols').forEach(initSbsDiff); |
| 39 | }; |
| 40 | } |
| 41 | })(); |
| 42 |
+1
-1
| --- src/scroll.js | ||
| +++ src/scroll.js | ||
| @@ -1,2 +1,2 @@ | ||
| 1 | 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | -document.getElementById('scrollToMe').scrollIntoView(true); | |
| 2 | +(document.getElementById('scrollToMe')||document.body).scrollIntoView(true); | |
| 3 | 3 |
| --- src/scroll.js | |
| +++ src/scroll.js | |
| @@ -1,2 +1,2 @@ | |
| 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | document.getElementById('scrollToMe').scrollIntoView(true); |
| 3 |
| --- src/scroll.js | |
| +++ src/scroll.js | |
| @@ -1,2 +1,2 @@ | |
| 1 | /* Cause the page to scroll so that the #scrollToMe is visible */ |
| 2 | (document.getElementById('scrollToMe')||document.body).scrollIntoView(true); |
| 3 |
+58
-12
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -1077,24 +1077,14 @@ | ||
| 1077 | 1077 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1078 | 1078 | @ empty string which means that Fossil never allows Markdown documents |
| 1079 | 1079 | @ to generate unsafe HTML. |
| 1080 | 1080 | @ (Property: "safe-html")</p> |
| 1081 | 1081 | @ <hr /> |
| 1082 | - @ <hr /> | |
| 1083 | - onoff_attribute("Enable WYSIWYG Wiki Editing", | |
| 1084 | - "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); | |
| 1085 | - @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. | |
| 1086 | - @ The WYSIWYG editor generates HTML instead of markup, which makes | |
| 1087 | - @ subsequent manual editing more difficult. | |
| 1088 | - @ (Property: "wysiwyg-wiki")</p> | |
| 1089 | - @ <hr /> | |
| 1090 | 1082 | onoff_attribute("Use HTML as wiki markup language", |
| 1091 | 1083 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1092 | 1084 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1093 | - @ but all other wiki formatting will be ignored. This option is helpful | |
| 1094 | - @ if you have chosen to use a rich HTML editor for wiki markup such as | |
| 1095 | - @ TinyMCE.</p> | |
| 1085 | + @ but all other wiki formatting will be ignored.</p> | |
| 1096 | 1086 | @ <p><strong>CAUTION:</strong> when |
| 1097 | 1087 | @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. |
| 1098 | 1088 | @ No sanitization is done. This means that it is very possible for malicious |
| 1099 | 1089 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1100 | 1090 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| @@ -1235,11 +1225,11 @@ | ||
| 1235 | 1225 | } |
| 1236 | 1226 | |
| 1237 | 1227 | /* |
| 1238 | 1228 | ** WEBPAGE: setup_logo |
| 1239 | 1229 | ** |
| 1240 | -** Administrative page for changing the logo image. | |
| 1230 | +** Administrative page for changing the logo, background, and icon images. | |
| 1241 | 1231 | */ |
| 1242 | 1232 | void setup_logo(void){ |
| 1243 | 1233 | const char *zLogoMtime = db_get_mtime("logo-image", 0, 0); |
| 1244 | 1234 | const char *zLogoMime = db_get("logo-mimetype","image/gif"); |
| 1245 | 1235 | const char *aLogoImg = P("logoim"); |
| @@ -1246,16 +1236,23 @@ | ||
| 1246 | 1236 | int szLogoImg = atoi(PD("logoim:bytes","0")); |
| 1247 | 1237 | const char *zBgMtime = db_get_mtime("background-image", 0, 0); |
| 1248 | 1238 | const char *zBgMime = db_get("background-mimetype","image/gif"); |
| 1249 | 1239 | const char *aBgImg = P("bgim"); |
| 1250 | 1240 | int szBgImg = atoi(PD("bgim:bytes","0")); |
| 1241 | + const char *zIconMtime = db_get_mtime("icon-image", 0, 0); | |
| 1242 | + const char *zIconMime = db_get("icon-mimetype","image/gif"); | |
| 1243 | + const char *aIconImg = P("iconim"); | |
| 1244 | + int szIconImg = atoi(PD("iconim:bytes","0")); | |
| 1251 | 1245 | if( szLogoImg>0 ){ |
| 1252 | 1246 | zLogoMime = PD("logoim:mimetype","image/gif"); |
| 1253 | 1247 | } |
| 1254 | 1248 | if( szBgImg>0 ){ |
| 1255 | 1249 | zBgMime = PD("bgim:mimetype","image/gif"); |
| 1256 | 1250 | } |
| 1251 | + if( szIconImg>0 ){ | |
| 1252 | + zIconMime = PD("iconim:mimetype","image/gif"); | |
| 1253 | + } | |
| 1257 | 1254 | login_check_credentials(); |
| 1258 | 1255 | if( !g.perm.Admin ){ |
| 1259 | 1256 | login_needed(0); |
| 1260 | 1257 | return; |
| 1261 | 1258 | } |
| @@ -1309,10 +1306,35 @@ | ||
| 1309 | 1306 | "DELETE FROM config WHERE name IN " |
| 1310 | 1307 | "('background-image','background-mimetype')" |
| 1311 | 1308 | ); |
| 1312 | 1309 | db_end_transaction(0); |
| 1313 | 1310 | cgi_redirect("setup_logo"); |
| 1311 | + }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ | |
| 1312 | + Blob img; | |
| 1313 | + Stmt ins; | |
| 1314 | + blob_init(&img, aIconImg, szIconImg); | |
| 1315 | + db_prepare(&ins, | |
| 1316 | + "REPLACE INTO config(name,value,mtime)" | |
| 1317 | + " VALUES('icon-image',:bytes,now())" | |
| 1318 | + ); | |
| 1319 | + db_bind_blob(&ins, ":bytes", &img); | |
| 1320 | + db_step(&ins); | |
| 1321 | + db_finalize(&ins); | |
| 1322 | + db_multi_exec( | |
| 1323 | + "REPLACE INTO config(name,value,mtime)" | |
| 1324 | + " VALUES('icon-mimetype',%Q,now())", | |
| 1325 | + zIconMime | |
| 1326 | + ); | |
| 1327 | + db_end_transaction(0); | |
| 1328 | + cgi_redirect("setup_logo"); | |
| 1329 | + }else if( P("clricon")!=0 ){ | |
| 1330 | + db_multi_exec( | |
| 1331 | + "DELETE FROM config WHERE name IN " | |
| 1332 | + "('icon-image','icon-mimetype')" | |
| 1333 | + ); | |
| 1334 | + db_end_transaction(0); | |
| 1335 | + cgi_redirect("setup_logo"); | |
| 1314 | 1336 | } |
| 1315 | 1337 | style_header("Edit Project Logo And Background"); |
| 1316 | 1338 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1317 | 1339 | @ and looks like this:</p> |
| 1318 | 1340 | @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ |
| @@ -1358,10 +1380,34 @@ | ||
| 1358 | 1380 | @ <input type="submit" name="setbg" value="Change Background" /> |
| 1359 | 1381 | @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
| 1360 | 1382 | @ </div></form> |
| 1361 | 1383 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1362 | 1384 | @ <hr /> |
| 1385 | + @ | |
| 1386 | + @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> | |
| 1387 | + @ and looks like this:</p> | |
| 1388 | + @ <blockquote><p><img src="%s(g.zTop)/favicon.ico/%z(zIconMtime)" \ | |
| 1389 | + @ alt="icon" border=1 /> | |
| 1390 | + @ </p></blockquote> | |
| 1391 | + @ | |
| 1392 | + @ <form action="%s(g.zTop)/setup_logo" method="post" | |
| 1393 | + @ enctype="multipart/form-data"><div> | |
| 1394 | + @ <p>The icon image is accessible to all users at this URL: | |
| 1395 | + @ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>. | |
| 1396 | + @ The icon image may or may not appear on each | |
| 1397 | + @ page depending on the web browser in use and the MIME-Types that it | |
| 1398 | + @ supports for icon images. | |
| 1399 | + @ To change the icon image, use the following form:</p> | |
| 1400 | + login_insert_csrf_secret(); | |
| 1401 | + @ Icon image file: | |
| 1402 | + @ <input type="file" name="iconim" size="60" accept="image/*" /> | |
| 1403 | + @ <p align="center"> | |
| 1404 | + @ <input type="submit" name="seticon" value="Change Icon" /> | |
| 1405 | + @ <input type="submit" name="clricon" value="Revert To Default" /></p> | |
| 1406 | + @ </div></form> | |
| 1407 | + @ <p>(Properties: "icon-image" and "icon-mimetype") | |
| 1408 | + @ <hr /> | |
| 1363 | 1409 | @ |
| 1364 | 1410 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1365 | 1411 | @ images, so you may need to press the Reload button before changes will |
| 1366 | 1412 | @ take effect. </p> |
| 1367 | 1413 | style_footer(); |
| 1368 | 1414 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -1077,24 +1077,14 @@ | |
| 1077 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1078 | @ empty string which means that Fossil never allows Markdown documents |
| 1079 | @ to generate unsafe HTML. |
| 1080 | @ (Property: "safe-html")</p> |
| 1081 | @ <hr /> |
| 1082 | @ <hr /> |
| 1083 | onoff_attribute("Enable WYSIWYG Wiki Editing", |
| 1084 | "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); |
| 1085 | @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. |
| 1086 | @ The WYSIWYG editor generates HTML instead of markup, which makes |
| 1087 | @ subsequent manual editing more difficult. |
| 1088 | @ (Property: "wysiwyg-wiki")</p> |
| 1089 | @ <hr /> |
| 1090 | onoff_attribute("Use HTML as wiki markup language", |
| 1091 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1092 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1093 | @ but all other wiki formatting will be ignored. This option is helpful |
| 1094 | @ if you have chosen to use a rich HTML editor for wiki markup such as |
| 1095 | @ TinyMCE.</p> |
| 1096 | @ <p><strong>CAUTION:</strong> when |
| 1097 | @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. |
| 1098 | @ No sanitization is done. This means that it is very possible for malicious |
| 1099 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1100 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| @@ -1235,11 +1225,11 @@ | |
| 1235 | } |
| 1236 | |
| 1237 | /* |
| 1238 | ** WEBPAGE: setup_logo |
| 1239 | ** |
| 1240 | ** Administrative page for changing the logo image. |
| 1241 | */ |
| 1242 | void setup_logo(void){ |
| 1243 | const char *zLogoMtime = db_get_mtime("logo-image", 0, 0); |
| 1244 | const char *zLogoMime = db_get("logo-mimetype","image/gif"); |
| 1245 | const char *aLogoImg = P("logoim"); |
| @@ -1246,16 +1236,23 @@ | |
| 1246 | int szLogoImg = atoi(PD("logoim:bytes","0")); |
| 1247 | const char *zBgMtime = db_get_mtime("background-image", 0, 0); |
| 1248 | const char *zBgMime = db_get("background-mimetype","image/gif"); |
| 1249 | const char *aBgImg = P("bgim"); |
| 1250 | int szBgImg = atoi(PD("bgim:bytes","0")); |
| 1251 | if( szLogoImg>0 ){ |
| 1252 | zLogoMime = PD("logoim:mimetype","image/gif"); |
| 1253 | } |
| 1254 | if( szBgImg>0 ){ |
| 1255 | zBgMime = PD("bgim:mimetype","image/gif"); |
| 1256 | } |
| 1257 | login_check_credentials(); |
| 1258 | if( !g.perm.Admin ){ |
| 1259 | login_needed(0); |
| 1260 | return; |
| 1261 | } |
| @@ -1309,10 +1306,35 @@ | |
| 1309 | "DELETE FROM config WHERE name IN " |
| 1310 | "('background-image','background-mimetype')" |
| 1311 | ); |
| 1312 | db_end_transaction(0); |
| 1313 | cgi_redirect("setup_logo"); |
| 1314 | } |
| 1315 | style_header("Edit Project Logo And Background"); |
| 1316 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1317 | @ and looks like this:</p> |
| 1318 | @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ |
| @@ -1358,10 +1380,34 @@ | |
| 1358 | @ <input type="submit" name="setbg" value="Change Background" /> |
| 1359 | @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
| 1360 | @ </div></form> |
| 1361 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1362 | @ <hr /> |
| 1363 | @ |
| 1364 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1365 | @ images, so you may need to press the Reload button before changes will |
| 1366 | @ take effect. </p> |
| 1367 | style_footer(); |
| 1368 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -1077,24 +1077,14 @@ | |
| 1077 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1078 | @ empty string which means that Fossil never allows Markdown documents |
| 1079 | @ to generate unsafe HTML. |
| 1080 | @ (Property: "safe-html")</p> |
| 1081 | @ <hr /> |
| 1082 | onoff_attribute("Use HTML as wiki markup language", |
| 1083 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1084 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1085 | @ but all other wiki formatting will be ignored.</p> |
| 1086 | @ <p><strong>CAUTION:</strong> when |
| 1087 | @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki. |
| 1088 | @ No sanitization is done. This means that it is very possible for malicious |
| 1089 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1090 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| @@ -1235,11 +1225,11 @@ | |
| 1225 | } |
| 1226 | |
| 1227 | /* |
| 1228 | ** WEBPAGE: setup_logo |
| 1229 | ** |
| 1230 | ** Administrative page for changing the logo, background, and icon images. |
| 1231 | */ |
| 1232 | void setup_logo(void){ |
| 1233 | const char *zLogoMtime = db_get_mtime("logo-image", 0, 0); |
| 1234 | const char *zLogoMime = db_get("logo-mimetype","image/gif"); |
| 1235 | const char *aLogoImg = P("logoim"); |
| @@ -1246,16 +1236,23 @@ | |
| 1236 | int szLogoImg = atoi(PD("logoim:bytes","0")); |
| 1237 | const char *zBgMtime = db_get_mtime("background-image", 0, 0); |
| 1238 | const char *zBgMime = db_get("background-mimetype","image/gif"); |
| 1239 | const char *aBgImg = P("bgim"); |
| 1240 | int szBgImg = atoi(PD("bgim:bytes","0")); |
| 1241 | const char *zIconMtime = db_get_mtime("icon-image", 0, 0); |
| 1242 | const char *zIconMime = db_get("icon-mimetype","image/gif"); |
| 1243 | const char *aIconImg = P("iconim"); |
| 1244 | int szIconImg = atoi(PD("iconim:bytes","0")); |
| 1245 | if( szLogoImg>0 ){ |
| 1246 | zLogoMime = PD("logoim:mimetype","image/gif"); |
| 1247 | } |
| 1248 | if( szBgImg>0 ){ |
| 1249 | zBgMime = PD("bgim:mimetype","image/gif"); |
| 1250 | } |
| 1251 | if( szIconImg>0 ){ |
| 1252 | zIconMime = PD("iconim:mimetype","image/gif"); |
| 1253 | } |
| 1254 | login_check_credentials(); |
| 1255 | if( !g.perm.Admin ){ |
| 1256 | login_needed(0); |
| 1257 | return; |
| 1258 | } |
| @@ -1309,10 +1306,35 @@ | |
| 1306 | "DELETE FROM config WHERE name IN " |
| 1307 | "('background-image','background-mimetype')" |
| 1308 | ); |
| 1309 | db_end_transaction(0); |
| 1310 | cgi_redirect("setup_logo"); |
| 1311 | }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ |
| 1312 | Blob img; |
| 1313 | Stmt ins; |
| 1314 | blob_init(&img, aIconImg, szIconImg); |
| 1315 | db_prepare(&ins, |
| 1316 | "REPLACE INTO config(name,value,mtime)" |
| 1317 | " VALUES('icon-image',:bytes,now())" |
| 1318 | ); |
| 1319 | db_bind_blob(&ins, ":bytes", &img); |
| 1320 | db_step(&ins); |
| 1321 | db_finalize(&ins); |
| 1322 | db_multi_exec( |
| 1323 | "REPLACE INTO config(name,value,mtime)" |
| 1324 | " VALUES('icon-mimetype',%Q,now())", |
| 1325 | zIconMime |
| 1326 | ); |
| 1327 | db_end_transaction(0); |
| 1328 | cgi_redirect("setup_logo"); |
| 1329 | }else if( P("clricon")!=0 ){ |
| 1330 | db_multi_exec( |
| 1331 | "DELETE FROM config WHERE name IN " |
| 1332 | "('icon-image','icon-mimetype')" |
| 1333 | ); |
| 1334 | db_end_transaction(0); |
| 1335 | cgi_redirect("setup_logo"); |
| 1336 | } |
| 1337 | style_header("Edit Project Logo And Background"); |
| 1338 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1339 | @ and looks like this:</p> |
| 1340 | @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ |
| @@ -1358,10 +1380,34 @@ | |
| 1380 | @ <input type="submit" name="setbg" value="Change Background" /> |
| 1381 | @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
| 1382 | @ </div></form> |
| 1383 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1384 | @ <hr /> |
| 1385 | @ |
| 1386 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1387 | @ and looks like this:</p> |
| 1388 | @ <blockquote><p><img src="%s(g.zTop)/favicon.ico/%z(zIconMtime)" \ |
| 1389 | @ alt="icon" border=1 /> |
| 1390 | @ </p></blockquote> |
| 1391 | @ |
| 1392 | @ <form action="%s(g.zTop)/setup_logo" method="post" |
| 1393 | @ enctype="multipart/form-data"><div> |
| 1394 | @ <p>The icon image is accessible to all users at this URL: |
| 1395 | @ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>. |
| 1396 | @ The icon image may or may not appear on each |
| 1397 | @ page depending on the web browser in use and the MIME-Types that it |
| 1398 | @ supports for icon images. |
| 1399 | @ To change the icon image, use the following form:</p> |
| 1400 | login_insert_csrf_secret(); |
| 1401 | @ Icon image file: |
| 1402 | @ <input type="file" name="iconim" size="60" accept="image/*" /> |
| 1403 | @ <p align="center"> |
| 1404 | @ <input type="submit" name="seticon" value="Change Icon" /> |
| 1405 | @ <input type="submit" name="clricon" value="Revert To Default" /></p> |
| 1406 | @ </div></form> |
| 1407 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1408 | @ <hr /> |
| 1409 | @ |
| 1410 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1411 | @ images, so you may need to press the Reload button before changes will |
| 1412 | @ take effect. </p> |
| 1413 | style_footer(); |
| 1414 |
+1
-1
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -702,11 +702,11 @@ | ||
| 702 | 702 | @ </tr> |
| 703 | 703 | } |
| 704 | 704 | @ </table> |
| 705 | 705 | @ </div></form> |
| 706 | 706 | @ </div> |
| 707 | - style_load_one_js_file("useredit.js"); | |
| 707 | + builtin_request_js("useredit.js"); | |
| 708 | 708 | @ <hr> |
| 709 | 709 | @ <h1>Notes On Privileges And Capabilities:</h1> |
| 710 | 710 | @ <ul> |
| 711 | 711 | if( higherUser ){ |
| 712 | 712 | @ <li><p class="missingPriv"> |
| 713 | 713 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -702,11 +702,11 @@ | |
| 702 | @ </tr> |
| 703 | } |
| 704 | @ </table> |
| 705 | @ </div></form> |
| 706 | @ </div> |
| 707 | style_load_one_js_file("useredit.js"); |
| 708 | @ <hr> |
| 709 | @ <h1>Notes On Privileges And Capabilities:</h1> |
| 710 | @ <ul> |
| 711 | if( higherUser ){ |
| 712 | @ <li><p class="missingPriv"> |
| 713 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -702,11 +702,11 @@ | |
| 702 | @ </tr> |
| 703 | } |
| 704 | @ </table> |
| 705 | @ </div></form> |
| 706 | @ </div> |
| 707 | builtin_request_js("useredit.js"); |
| 708 | @ <hr> |
| 709 | @ <h1>Notes On Privileges And Capabilities:</h1> |
| 710 | @ <ul> |
| 711 | if( higherUser ){ |
| 712 | @ <li><p class="missingPriv"> |
| 713 |
+2
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -507,10 +507,12 @@ | ||
| 507 | 507 | ** |
| 508 | 508 | ** -h, --dereference If FILE is a symbolic link, compute the hash |
| 509 | 509 | ** on the object that the link points to. Normally, |
| 510 | 510 | ** the hash is over the name of the object that |
| 511 | 511 | ** the link points to. |
| 512 | +** | |
| 513 | +** See also: [[md5sum]], [[sha3sum]] | |
| 512 | 514 | */ |
| 513 | 515 | void sha1sum_test(void){ |
| 514 | 516 | int i; |
| 515 | 517 | Blob in; |
| 516 | 518 | Blob cksum; |
| 517 | 519 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -507,10 +507,12 @@ | |
| 507 | ** |
| 508 | ** -h, --dereference If FILE is a symbolic link, compute the hash |
| 509 | ** on the object that the link points to. Normally, |
| 510 | ** the hash is over the name of the object that |
| 511 | ** the link points to. |
| 512 | */ |
| 513 | void sha1sum_test(void){ |
| 514 | int i; |
| 515 | Blob in; |
| 516 | Blob cksum; |
| 517 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -507,10 +507,12 @@ | |
| 507 | ** |
| 508 | ** -h, --dereference If FILE is a symbolic link, compute the hash |
| 509 | ** on the object that the link points to. Normally, |
| 510 | ** the hash is over the name of the object that |
| 511 | ** the link points to. |
| 512 | ** |
| 513 | ** See also: [[md5sum]], [[sha3sum]] |
| 514 | */ |
| 515 | void sha1sum_test(void){ |
| 516 | int i; |
| 517 | Blob in; |
| 518 | Blob cksum; |
| 519 |
+2
| --- src/sha3.c | ||
| +++ src/sha3.c | ||
| @@ -637,10 +637,12 @@ | ||
| 637 | 637 | ** --512 Compute a SHA3-512 hash |
| 638 | 638 | ** --size N An N-bit hash. N must be a multiple of 32 between |
| 639 | 639 | ** 128 and 512. |
| 640 | 640 | ** -h, --dereference If FILE is a symbolic link, compute the hash on |
| 641 | 641 | ** the object pointed to, not on the link itself. |
| 642 | +** | |
| 643 | +** See also: [[md5sum]], [[sha1sum]] | |
| 642 | 644 | */ |
| 643 | 645 | void sha3sum_test(void){ |
| 644 | 646 | int i; |
| 645 | 647 | Blob in; |
| 646 | 648 | Blob cksum; |
| 647 | 649 |
| --- src/sha3.c | |
| +++ src/sha3.c | |
| @@ -637,10 +637,12 @@ | |
| 637 | ** --512 Compute a SHA3-512 hash |
| 638 | ** --size N An N-bit hash. N must be a multiple of 32 between |
| 639 | ** 128 and 512. |
| 640 | ** -h, --dereference If FILE is a symbolic link, compute the hash on |
| 641 | ** the object pointed to, not on the link itself. |
| 642 | */ |
| 643 | void sha3sum_test(void){ |
| 644 | int i; |
| 645 | Blob in; |
| 646 | Blob cksum; |
| 647 |
| --- src/sha3.c | |
| +++ src/sha3.c | |
| @@ -637,10 +637,12 @@ | |
| 637 | ** --512 Compute a SHA3-512 hash |
| 638 | ** --size N An N-bit hash. N must be a multiple of 32 between |
| 639 | ** 128 and 512. |
| 640 | ** -h, --dereference If FILE is a symbolic link, compute the hash on |
| 641 | ** the object pointed to, not on the link itself. |
| 642 | ** |
| 643 | ** See also: [[md5sum]], [[sha1sum]] |
| 644 | */ |
| 645 | void sha3sum_test(void){ |
| 646 | int i; |
| 647 | Blob in; |
| 648 | Blob cksum; |
| 649 |
+54
-22
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -639,10 +639,25 @@ | ||
| 639 | 639 | if( (0xc0&*(z++))!=0x80 ) n++; |
| 640 | 640 | } |
| 641 | 641 | return n; |
| 642 | 642 | } |
| 643 | 643 | |
| 644 | +/* | |
| 645 | +** Return true if zFile does not exist or if it is not an ordinary file. | |
| 646 | +*/ | |
| 647 | +#ifdef _WIN32 | |
| 648 | +# define notNormalFile(X) 0 | |
| 649 | +#else | |
| 650 | +static int notNormalFile(const char *zFile){ | |
| 651 | + struct stat x; | |
| 652 | + int rc; | |
| 653 | + memset(&x, 0, sizeof(x)); | |
| 654 | + rc = stat(zFile, &x); | |
| 655 | + return rc || !S_ISREG(x.st_mode); | |
| 656 | +} | |
| 657 | +#endif | |
| 658 | + | |
| 644 | 659 | /* |
| 645 | 660 | ** This routine reads a line of text from FILE in, stores |
| 646 | 661 | ** the text in memory obtained from malloc() and returns a pointer |
| 647 | 662 | ** to the text. NULL is returned at end of file, or if malloc() |
| 648 | 663 | ** fails. |
| @@ -4045,11 +4060,11 @@ | ||
| 4045 | 4060 | return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); |
| 4046 | 4061 | } |
| 4047 | 4062 | p = (ApndFile*)pFile; |
| 4048 | 4063 | memset(p, 0, sizeof(*p)); |
| 4049 | 4064 | pSubFile = ORIGFILE(pFile); |
| 4050 | - p->base.pMethods = &apnd_io_methods; | |
| 4065 | + pFile->pMethods = &apnd_io_methods; | |
| 4051 | 4066 | rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); |
| 4052 | 4067 | if( rc ) goto apnd_open_done; |
| 4053 | 4068 | rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); |
| 4054 | 4069 | if( rc ){ |
| 4055 | 4070 | pSubFile->pMethods->xClose(pSubFile); |
| @@ -4979,11 +4994,10 @@ | ||
| 4979 | 4994 | sqlite3 *db, |
| 4980 | 4995 | char **pzErrMsg, |
| 4981 | 4996 | const sqlite3_api_routines *pApi |
| 4982 | 4997 | ){ |
| 4983 | 4998 | int rc = SQLITE_OK; |
| 4984 | - SQLITE_EXTENSION_INIT2(pApi); | |
| 4985 | 4999 | static const struct { |
| 4986 | 5000 | const char *zFuncName; |
| 4987 | 5001 | int nArg; |
| 4988 | 5002 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**); |
| 4989 | 5003 | } aFunc[] = { |
| @@ -4993,10 +5007,12 @@ | ||
| 4993 | 5007 | { "decimal_sub", 2, decimalSubFunc }, |
| 4994 | 5008 | { "decimal_mul", 2, decimalMulFunc }, |
| 4995 | 5009 | }; |
| 4996 | 5010 | unsigned int i; |
| 4997 | 5011 | (void)pzErrMsg; /* Unused parameter */ |
| 5012 | + | |
| 5013 | + SQLITE_EXTENSION_INIT2(pApi); | |
| 4998 | 5014 | |
| 4999 | 5015 | for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){ |
| 5000 | 5016 | rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg, |
| 5001 | 5017 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, |
| 5002 | 5018 | 0, aFunc[i].xFunc, 0, 0); |
| @@ -12449,30 +12465,30 @@ | ||
| 12449 | 12465 | |
| 12450 | 12466 | /* |
| 12451 | 12467 | ** Disable and restore .wheretrace and .selecttrace settings. |
| 12452 | 12468 | */ |
| 12453 | 12469 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12454 | -extern int sqlite3SelectTrace; | |
| 12470 | +extern unsigned int sqlite3_unsupported_selecttrace; | |
| 12455 | 12471 | static int savedSelectTrace; |
| 12456 | 12472 | #endif |
| 12457 | 12473 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12458 | 12474 | extern int sqlite3WhereTrace; |
| 12459 | 12475 | static int savedWhereTrace; |
| 12460 | 12476 | #endif |
| 12461 | 12477 | static void disable_debug_trace_modes(void){ |
| 12462 | 12478 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12463 | - savedSelectTrace = sqlite3SelectTrace; | |
| 12464 | - sqlite3SelectTrace = 0; | |
| 12479 | + savedSelectTrace = sqlite3_unsupported_selecttrace; | |
| 12480 | + sqlite3_unsupported_selecttrace = 0; | |
| 12465 | 12481 | #endif |
| 12466 | 12482 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12467 | 12483 | savedWhereTrace = sqlite3WhereTrace; |
| 12468 | 12484 | sqlite3WhereTrace = 0; |
| 12469 | 12485 | #endif |
| 12470 | 12486 | } |
| 12471 | 12487 | static void restore_debug_trace_modes(void){ |
| 12472 | 12488 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12473 | - sqlite3SelectTrace = savedSelectTrace; | |
| 12489 | + sqlite3_unsupported_selecttrace = savedSelectTrace; | |
| 12474 | 12490 | #endif |
| 12475 | 12491 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12476 | 12492 | sqlite3WhereTrace = savedWhereTrace; |
| 12477 | 12493 | #endif |
| 12478 | 12494 | } |
| @@ -12619,29 +12635,42 @@ | ||
| 12619 | 12635 | */ |
| 12620 | 12636 | static void exec_prepared_stmt_columnar( |
| 12621 | 12637 | ShellState *p, /* Pointer to ShellState */ |
| 12622 | 12638 | sqlite3_stmt *pStmt /* Statment to run */ |
| 12623 | 12639 | ){ |
| 12624 | - int nRow = 0; | |
| 12640 | + sqlite3_int64 nRow = 0; | |
| 12625 | 12641 | int nColumn = 0; |
| 12626 | 12642 | char **azData = 0; |
| 12627 | - char *zMsg = 0; | |
| 12643 | + sqlite3_int64 nAlloc = 0; | |
| 12628 | 12644 | const char *z; |
| 12629 | 12645 | int rc; |
| 12630 | - int i, j, nTotal, w, n; | |
| 12646 | + sqlite3_int64 i, nData; | |
| 12647 | + int j, nTotal, w, n; | |
| 12631 | 12648 | const char *colSep = 0; |
| 12632 | 12649 | const char *rowSep = 0; |
| 12633 | 12650 | |
| 12634 | - rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt), | |
| 12635 | - &azData, &nRow, &nColumn, &zMsg); | |
| 12636 | - if( rc ){ | |
| 12637 | - utf8_printf(p->out, "ERROR: %s\n", zMsg); | |
| 12638 | - sqlite3_free(zMsg); | |
| 12639 | - sqlite3_free_table(azData); | |
| 12640 | - return; | |
| 12641 | - } | |
| 12642 | - if( nRow==0 || nColumn==0 ) goto columnar_end; | |
| 12651 | + rc = sqlite3_step(pStmt); | |
| 12652 | + if( rc!=SQLITE_ROW ) return; | |
| 12653 | + nColumn = sqlite3_column_count(pStmt); | |
| 12654 | + nAlloc = nColumn*4; | |
| 12655 | + azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); | |
| 12656 | + if( azData==0 ) shell_out_of_memory(); | |
| 12657 | + for(i=0; i<nColumn; i++){ | |
| 12658 | + azData[i] = strdup(sqlite3_column_name(pStmt,i)); | |
| 12659 | + } | |
| 12660 | + do{ | |
| 12661 | + if( (nRow+2)*nColumn >= nAlloc ){ | |
| 12662 | + nAlloc *= 2; | |
| 12663 | + azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); | |
| 12664 | + if( azData==0 ) shell_out_of_memory(); | |
| 12665 | + } | |
| 12666 | + nRow++; | |
| 12667 | + for(i=0; i<nColumn; i++){ | |
| 12668 | + z = (const char*)sqlite3_column_text(pStmt,i); | |
| 12669 | + azData[nRow*nColumn + i] = z ? strdup(z) : 0; | |
| 12670 | + } | |
| 12671 | + }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ); | |
| 12643 | 12672 | if( nColumn>p->nWidth ){ |
| 12644 | 12673 | p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); |
| 12645 | 12674 | if( p->colWidth==0 ) shell_out_of_memory(); |
| 12646 | 12675 | for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; |
| 12647 | 12676 | p->nWidth = nColumn; |
| @@ -12747,11 +12776,13 @@ | ||
| 12747 | 12776 | } |
| 12748 | 12777 | columnar_end: |
| 12749 | 12778 | if( seenInterrupt ){ |
| 12750 | 12779 | utf8_printf(p->out, "Interrupt\n"); |
| 12751 | 12780 | } |
| 12752 | - sqlite3_free_table(azData); | |
| 12781 | + nData = (nRow+1)*nColumn; | |
| 12782 | + for(i=0; i<nData; i++) free(azData[i]); | |
| 12783 | + sqlite3_free(azData); | |
| 12753 | 12784 | } |
| 12754 | 12785 | |
| 12755 | 12786 | /* |
| 12756 | 12787 | ** Run a prepared statement |
| 12757 | 12788 | */ |
| @@ -18515,12 +18546,13 @@ | ||
| 18515 | 18546 | if( nArg!=2 ){ |
| 18516 | 18547 | raw_printf(stderr, "Usage: .read FILE\n"); |
| 18517 | 18548 | rc = 1; |
| 18518 | 18549 | goto meta_command_exit; |
| 18519 | 18550 | } |
| 18520 | - p->in = fopen(azArg[1], "rb"); | |
| 18521 | - if( p->in==0 ){ | |
| 18551 | + if( notNormalFile(azArg[1]) | |
| 18552 | + || (p->in = fopen(azArg[1], "rb"))==0 | |
| 18553 | + ){ | |
| 18522 | 18554 | utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 18523 | 18555 | rc = 1; |
| 18524 | 18556 | }else{ |
| 18525 | 18557 | rc = process_input(p); |
| 18526 | 18558 | fclose(p->in); |
| @@ -18722,11 +18754,11 @@ | ||
| 18722 | 18754 | } |
| 18723 | 18755 | }else |
| 18724 | 18756 | |
| 18725 | 18757 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 18726 | 18758 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 18727 | - sqlite3SelectTrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; | |
| 18759 | + sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; | |
| 18728 | 18760 | }else |
| 18729 | 18761 | #endif |
| 18730 | 18762 | |
| 18731 | 18763 | #if defined(SQLITE_ENABLE_SESSION) |
| 18732 | 18764 | if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ |
| 18733 | 18765 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -639,10 +639,25 @@ | |
| 639 | if( (0xc0&*(z++))!=0x80 ) n++; |
| 640 | } |
| 641 | return n; |
| 642 | } |
| 643 | |
| 644 | /* |
| 645 | ** This routine reads a line of text from FILE in, stores |
| 646 | ** the text in memory obtained from malloc() and returns a pointer |
| 647 | ** to the text. NULL is returned at end of file, or if malloc() |
| 648 | ** fails. |
| @@ -4045,11 +4060,11 @@ | |
| 4045 | return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); |
| 4046 | } |
| 4047 | p = (ApndFile*)pFile; |
| 4048 | memset(p, 0, sizeof(*p)); |
| 4049 | pSubFile = ORIGFILE(pFile); |
| 4050 | p->base.pMethods = &apnd_io_methods; |
| 4051 | rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); |
| 4052 | if( rc ) goto apnd_open_done; |
| 4053 | rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); |
| 4054 | if( rc ){ |
| 4055 | pSubFile->pMethods->xClose(pSubFile); |
| @@ -4979,11 +4994,10 @@ | |
| 4979 | sqlite3 *db, |
| 4980 | char **pzErrMsg, |
| 4981 | const sqlite3_api_routines *pApi |
| 4982 | ){ |
| 4983 | int rc = SQLITE_OK; |
| 4984 | SQLITE_EXTENSION_INIT2(pApi); |
| 4985 | static const struct { |
| 4986 | const char *zFuncName; |
| 4987 | int nArg; |
| 4988 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**); |
| 4989 | } aFunc[] = { |
| @@ -4993,10 +5007,12 @@ | |
| 4993 | { "decimal_sub", 2, decimalSubFunc }, |
| 4994 | { "decimal_mul", 2, decimalMulFunc }, |
| 4995 | }; |
| 4996 | unsigned int i; |
| 4997 | (void)pzErrMsg; /* Unused parameter */ |
| 4998 | |
| 4999 | for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){ |
| 5000 | rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg, |
| 5001 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, |
| 5002 | 0, aFunc[i].xFunc, 0, 0); |
| @@ -12449,30 +12465,30 @@ | |
| 12449 | |
| 12450 | /* |
| 12451 | ** Disable and restore .wheretrace and .selecttrace settings. |
| 12452 | */ |
| 12453 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12454 | extern int sqlite3SelectTrace; |
| 12455 | static int savedSelectTrace; |
| 12456 | #endif |
| 12457 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12458 | extern int sqlite3WhereTrace; |
| 12459 | static int savedWhereTrace; |
| 12460 | #endif |
| 12461 | static void disable_debug_trace_modes(void){ |
| 12462 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12463 | savedSelectTrace = sqlite3SelectTrace; |
| 12464 | sqlite3SelectTrace = 0; |
| 12465 | #endif |
| 12466 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12467 | savedWhereTrace = sqlite3WhereTrace; |
| 12468 | sqlite3WhereTrace = 0; |
| 12469 | #endif |
| 12470 | } |
| 12471 | static void restore_debug_trace_modes(void){ |
| 12472 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12473 | sqlite3SelectTrace = savedSelectTrace; |
| 12474 | #endif |
| 12475 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12476 | sqlite3WhereTrace = savedWhereTrace; |
| 12477 | #endif |
| 12478 | } |
| @@ -12619,29 +12635,42 @@ | |
| 12619 | */ |
| 12620 | static void exec_prepared_stmt_columnar( |
| 12621 | ShellState *p, /* Pointer to ShellState */ |
| 12622 | sqlite3_stmt *pStmt /* Statment to run */ |
| 12623 | ){ |
| 12624 | int nRow = 0; |
| 12625 | int nColumn = 0; |
| 12626 | char **azData = 0; |
| 12627 | char *zMsg = 0; |
| 12628 | const char *z; |
| 12629 | int rc; |
| 12630 | int i, j, nTotal, w, n; |
| 12631 | const char *colSep = 0; |
| 12632 | const char *rowSep = 0; |
| 12633 | |
| 12634 | rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt), |
| 12635 | &azData, &nRow, &nColumn, &zMsg); |
| 12636 | if( rc ){ |
| 12637 | utf8_printf(p->out, "ERROR: %s\n", zMsg); |
| 12638 | sqlite3_free(zMsg); |
| 12639 | sqlite3_free_table(azData); |
| 12640 | return; |
| 12641 | } |
| 12642 | if( nRow==0 || nColumn==0 ) goto columnar_end; |
| 12643 | if( nColumn>p->nWidth ){ |
| 12644 | p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); |
| 12645 | if( p->colWidth==0 ) shell_out_of_memory(); |
| 12646 | for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; |
| 12647 | p->nWidth = nColumn; |
| @@ -12747,11 +12776,13 @@ | |
| 12747 | } |
| 12748 | columnar_end: |
| 12749 | if( seenInterrupt ){ |
| 12750 | utf8_printf(p->out, "Interrupt\n"); |
| 12751 | } |
| 12752 | sqlite3_free_table(azData); |
| 12753 | } |
| 12754 | |
| 12755 | /* |
| 12756 | ** Run a prepared statement |
| 12757 | */ |
| @@ -18515,12 +18546,13 @@ | |
| 18515 | if( nArg!=2 ){ |
| 18516 | raw_printf(stderr, "Usage: .read FILE\n"); |
| 18517 | rc = 1; |
| 18518 | goto meta_command_exit; |
| 18519 | } |
| 18520 | p->in = fopen(azArg[1], "rb"); |
| 18521 | if( p->in==0 ){ |
| 18522 | utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 18523 | rc = 1; |
| 18524 | }else{ |
| 18525 | rc = process_input(p); |
| 18526 | fclose(p->in); |
| @@ -18722,11 +18754,11 @@ | |
| 18722 | } |
| 18723 | }else |
| 18724 | |
| 18725 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 18726 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 18727 | sqlite3SelectTrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; |
| 18728 | }else |
| 18729 | #endif |
| 18730 | |
| 18731 | #if defined(SQLITE_ENABLE_SESSION) |
| 18732 | if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ |
| 18733 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -639,10 +639,25 @@ | |
| 639 | if( (0xc0&*(z++))!=0x80 ) n++; |
| 640 | } |
| 641 | return n; |
| 642 | } |
| 643 | |
| 644 | /* |
| 645 | ** Return true if zFile does not exist or if it is not an ordinary file. |
| 646 | */ |
| 647 | #ifdef _WIN32 |
| 648 | # define notNormalFile(X) 0 |
| 649 | #else |
| 650 | static int notNormalFile(const char *zFile){ |
| 651 | struct stat x; |
| 652 | int rc; |
| 653 | memset(&x, 0, sizeof(x)); |
| 654 | rc = stat(zFile, &x); |
| 655 | return rc || !S_ISREG(x.st_mode); |
| 656 | } |
| 657 | #endif |
| 658 | |
| 659 | /* |
| 660 | ** This routine reads a line of text from FILE in, stores |
| 661 | ** the text in memory obtained from malloc() and returns a pointer |
| 662 | ** to the text. NULL is returned at end of file, or if malloc() |
| 663 | ** fails. |
| @@ -4045,11 +4060,11 @@ | |
| 4060 | return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); |
| 4061 | } |
| 4062 | p = (ApndFile*)pFile; |
| 4063 | memset(p, 0, sizeof(*p)); |
| 4064 | pSubFile = ORIGFILE(pFile); |
| 4065 | pFile->pMethods = &apnd_io_methods; |
| 4066 | rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); |
| 4067 | if( rc ) goto apnd_open_done; |
| 4068 | rc = pSubFile->pMethods->xFileSize(pSubFile, &sz); |
| 4069 | if( rc ){ |
| 4070 | pSubFile->pMethods->xClose(pSubFile); |
| @@ -4979,11 +4994,10 @@ | |
| 4994 | sqlite3 *db, |
| 4995 | char **pzErrMsg, |
| 4996 | const sqlite3_api_routines *pApi |
| 4997 | ){ |
| 4998 | int rc = SQLITE_OK; |
| 4999 | static const struct { |
| 5000 | const char *zFuncName; |
| 5001 | int nArg; |
| 5002 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**); |
| 5003 | } aFunc[] = { |
| @@ -4993,10 +5007,12 @@ | |
| 5007 | { "decimal_sub", 2, decimalSubFunc }, |
| 5008 | { "decimal_mul", 2, decimalMulFunc }, |
| 5009 | }; |
| 5010 | unsigned int i; |
| 5011 | (void)pzErrMsg; /* Unused parameter */ |
| 5012 | |
| 5013 | SQLITE_EXTENSION_INIT2(pApi); |
| 5014 | |
| 5015 | for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){ |
| 5016 | rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg, |
| 5017 | SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, |
| 5018 | 0, aFunc[i].xFunc, 0, 0); |
| @@ -12449,30 +12465,30 @@ | |
| 12465 | |
| 12466 | /* |
| 12467 | ** Disable and restore .wheretrace and .selecttrace settings. |
| 12468 | */ |
| 12469 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12470 | extern unsigned int sqlite3_unsupported_selecttrace; |
| 12471 | static int savedSelectTrace; |
| 12472 | #endif |
| 12473 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12474 | extern int sqlite3WhereTrace; |
| 12475 | static int savedWhereTrace; |
| 12476 | #endif |
| 12477 | static void disable_debug_trace_modes(void){ |
| 12478 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12479 | savedSelectTrace = sqlite3_unsupported_selecttrace; |
| 12480 | sqlite3_unsupported_selecttrace = 0; |
| 12481 | #endif |
| 12482 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12483 | savedWhereTrace = sqlite3WhereTrace; |
| 12484 | sqlite3WhereTrace = 0; |
| 12485 | #endif |
| 12486 | } |
| 12487 | static void restore_debug_trace_modes(void){ |
| 12488 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 12489 | sqlite3_unsupported_selecttrace = savedSelectTrace; |
| 12490 | #endif |
| 12491 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) |
| 12492 | sqlite3WhereTrace = savedWhereTrace; |
| 12493 | #endif |
| 12494 | } |
| @@ -12619,29 +12635,42 @@ | |
| 12635 | */ |
| 12636 | static void exec_prepared_stmt_columnar( |
| 12637 | ShellState *p, /* Pointer to ShellState */ |
| 12638 | sqlite3_stmt *pStmt /* Statment to run */ |
| 12639 | ){ |
| 12640 | sqlite3_int64 nRow = 0; |
| 12641 | int nColumn = 0; |
| 12642 | char **azData = 0; |
| 12643 | sqlite3_int64 nAlloc = 0; |
| 12644 | const char *z; |
| 12645 | int rc; |
| 12646 | sqlite3_int64 i, nData; |
| 12647 | int j, nTotal, w, n; |
| 12648 | const char *colSep = 0; |
| 12649 | const char *rowSep = 0; |
| 12650 | |
| 12651 | rc = sqlite3_step(pStmt); |
| 12652 | if( rc!=SQLITE_ROW ) return; |
| 12653 | nColumn = sqlite3_column_count(pStmt); |
| 12654 | nAlloc = nColumn*4; |
| 12655 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 12656 | if( azData==0 ) shell_out_of_memory(); |
| 12657 | for(i=0; i<nColumn; i++){ |
| 12658 | azData[i] = strdup(sqlite3_column_name(pStmt,i)); |
| 12659 | } |
| 12660 | do{ |
| 12661 | if( (nRow+2)*nColumn >= nAlloc ){ |
| 12662 | nAlloc *= 2; |
| 12663 | azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); |
| 12664 | if( azData==0 ) shell_out_of_memory(); |
| 12665 | } |
| 12666 | nRow++; |
| 12667 | for(i=0; i<nColumn; i++){ |
| 12668 | z = (const char*)sqlite3_column_text(pStmt,i); |
| 12669 | azData[nRow*nColumn + i] = z ? strdup(z) : 0; |
| 12670 | } |
| 12671 | }while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ); |
| 12672 | if( nColumn>p->nWidth ){ |
| 12673 | p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); |
| 12674 | if( p->colWidth==0 ) shell_out_of_memory(); |
| 12675 | for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0; |
| 12676 | p->nWidth = nColumn; |
| @@ -12747,11 +12776,13 @@ | |
| 12776 | } |
| 12777 | columnar_end: |
| 12778 | if( seenInterrupt ){ |
| 12779 | utf8_printf(p->out, "Interrupt\n"); |
| 12780 | } |
| 12781 | nData = (nRow+1)*nColumn; |
| 12782 | for(i=0; i<nData; i++) free(azData[i]); |
| 12783 | sqlite3_free(azData); |
| 12784 | } |
| 12785 | |
| 12786 | /* |
| 12787 | ** Run a prepared statement |
| 12788 | */ |
| @@ -18515,12 +18546,13 @@ | |
| 18546 | if( nArg!=2 ){ |
| 18547 | raw_printf(stderr, "Usage: .read FILE\n"); |
| 18548 | rc = 1; |
| 18549 | goto meta_command_exit; |
| 18550 | } |
| 18551 | if( notNormalFile(azArg[1]) |
| 18552 | || (p->in = fopen(azArg[1], "rb"))==0 |
| 18553 | ){ |
| 18554 | utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
| 18555 | rc = 1; |
| 18556 | }else{ |
| 18557 | rc = process_input(p); |
| 18558 | fclose(p->in); |
| @@ -18722,11 +18754,11 @@ | |
| 18754 | } |
| 18755 | }else |
| 18756 | |
| 18757 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 18758 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 18759 | sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff; |
| 18760 | }else |
| 18761 | #endif |
| 18762 | |
| 18763 | #if defined(SQLITE_ENABLE_SESSION) |
| 18764 | if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ |
| 18765 |
+1
-1
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -1101,8 +1101,8 @@ | ||
| 1101 | 1101 | @ undo a prior publish. |
| 1102 | 1102 | }else{ |
| 1103 | 1103 | @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page |
| 1104 | 1104 | @ for cleanup and recovery actions. |
| 1105 | 1105 | } |
| 1106 | - style_load_one_js_file("skin.js"); | |
| 1106 | + builtin_request_js("skin.js"); | |
| 1107 | 1107 | style_footer(); |
| 1108 | 1108 | } |
| 1109 | 1109 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -1101,8 +1101,8 @@ | |
| 1101 | @ undo a prior publish. |
| 1102 | }else{ |
| 1103 | @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page |
| 1104 | @ for cleanup and recovery actions. |
| 1105 | } |
| 1106 | style_load_one_js_file("skin.js"); |
| 1107 | style_footer(); |
| 1108 | } |
| 1109 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -1101,8 +1101,8 @@ | |
| 1101 | @ undo a prior publish. |
| 1102 | }else{ |
| 1103 | @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page |
| 1104 | @ for cleanup and recovery actions. |
| 1105 | } |
| 1106 | builtin_request_js("skin.js"); |
| 1107 | style_footer(); |
| 1108 | } |
| 1109 |
+9
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -172,10 +172,11 @@ | ||
| 172 | 172 | re_add_sql_func(db); |
| 173 | 173 | search_sql_setup(db); |
| 174 | 174 | foci_register(db); |
| 175 | 175 | deltafunc_init(db); |
| 176 | 176 | helptext_vtab_register(db); |
| 177 | + builtin_vtab_register(db); | |
| 177 | 178 | g.repositoryOpen = 1; |
| 178 | 179 | g.db = db; |
| 179 | 180 | sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); |
| 180 | 181 | db_maybe_set_encryption_key(db, g.zRepositoryName); |
| 181 | 182 | if( g.zLocalDbName ){ |
| @@ -291,10 +292,14 @@ | ||
| 291 | 292 | ** All of the standard sqlite3 command-line shell options should also |
| 292 | 293 | ** work. |
| 293 | 294 | ** |
| 294 | 295 | ** The following SQL extensions are provided with this Fossil-enhanced |
| 295 | 296 | ** version of the sqlite3 command-line shell: |
| 297 | +** | |
| 298 | +** builtin A virtual table that contains one row for | |
| 299 | +** each datafile that is built into the Fossil | |
| 300 | +** binary. | |
| 296 | 301 | ** |
| 297 | 302 | ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) |
| 298 | 303 | ** found in check-in X (another BLOB.RID value). |
| 299 | 304 | ** |
| 300 | 305 | ** compress(X) Compress text X with the same algorithm used |
| @@ -323,10 +328,14 @@ | ||
| 323 | 328 | ** |
| 324 | 329 | ** files_of_checkin(X) A table-valued function that returns info on |
| 325 | 330 | ** all files contained in check-in X. Example: |
| 326 | 331 | ** |
| 327 | 332 | ** SELECT * FROM files_of_checkin('trunk'); |
| 333 | +** | |
| 334 | +** helptext A virtual table with one row for each command, | |
| 335 | +** webpage, and setting together with the built-in | |
| 336 | +** help text. | |
| 328 | 337 | ** |
| 329 | 338 | ** now() Return the number of seconds since 1970. |
| 330 | 339 | ** |
| 331 | 340 | ** obscure(T) Obfuscate the text password T so that its |
| 332 | 341 | ** original value is not readily visible. Fossil |
| 333 | 342 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -172,10 +172,11 @@ | |
| 172 | re_add_sql_func(db); |
| 173 | search_sql_setup(db); |
| 174 | foci_register(db); |
| 175 | deltafunc_init(db); |
| 176 | helptext_vtab_register(db); |
| 177 | g.repositoryOpen = 1; |
| 178 | g.db = db; |
| 179 | sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); |
| 180 | db_maybe_set_encryption_key(db, g.zRepositoryName); |
| 181 | if( g.zLocalDbName ){ |
| @@ -291,10 +292,14 @@ | |
| 291 | ** All of the standard sqlite3 command-line shell options should also |
| 292 | ** work. |
| 293 | ** |
| 294 | ** The following SQL extensions are provided with this Fossil-enhanced |
| 295 | ** version of the sqlite3 command-line shell: |
| 296 | ** |
| 297 | ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) |
| 298 | ** found in check-in X (another BLOB.RID value). |
| 299 | ** |
| 300 | ** compress(X) Compress text X with the same algorithm used |
| @@ -323,10 +328,14 @@ | |
| 323 | ** |
| 324 | ** files_of_checkin(X) A table-valued function that returns info on |
| 325 | ** all files contained in check-in X. Example: |
| 326 | ** |
| 327 | ** SELECT * FROM files_of_checkin('trunk'); |
| 328 | ** |
| 329 | ** now() Return the number of seconds since 1970. |
| 330 | ** |
| 331 | ** obscure(T) Obfuscate the text password T so that its |
| 332 | ** original value is not readily visible. Fossil |
| 333 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -172,10 +172,11 @@ | |
| 172 | re_add_sql_func(db); |
| 173 | search_sql_setup(db); |
| 174 | foci_register(db); |
| 175 | deltafunc_init(db); |
| 176 | helptext_vtab_register(db); |
| 177 | builtin_vtab_register(db); |
| 178 | g.repositoryOpen = 1; |
| 179 | g.db = db; |
| 180 | sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository"); |
| 181 | db_maybe_set_encryption_key(db, g.zRepositoryName); |
| 182 | if( g.zLocalDbName ){ |
| @@ -291,10 +292,14 @@ | |
| 292 | ** All of the standard sqlite3 command-line shell options should also |
| 293 | ** work. |
| 294 | ** |
| 295 | ** The following SQL extensions are provided with this Fossil-enhanced |
| 296 | ** version of the sqlite3 command-line shell: |
| 297 | ** |
| 298 | ** builtin A virtual table that contains one row for |
| 299 | ** each datafile that is built into the Fossil |
| 300 | ** binary. |
| 301 | ** |
| 302 | ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) |
| 303 | ** found in check-in X (another BLOB.RID value). |
| 304 | ** |
| 305 | ** compress(X) Compress text X with the same algorithm used |
| @@ -323,10 +328,14 @@ | |
| 328 | ** |
| 329 | ** files_of_checkin(X) A table-valued function that returns info on |
| 330 | ** all files contained in check-in X. Example: |
| 331 | ** |
| 332 | ** SELECT * FROM files_of_checkin('trunk'); |
| 333 | ** |
| 334 | ** helptext A virtual table with one row for each command, |
| 335 | ** webpage, and setting together with the built-in |
| 336 | ** help text. |
| 337 | ** |
| 338 | ** now() Return the number of seconds since 1970. |
| 339 | ** |
| 340 | ** obscure(T) Obfuscate the text password T so that its |
| 341 | ** original value is not readily visible. Fossil |
| 342 |
+772
-531
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -997,10 +997,19 @@ | ||
| 997 | 997 | |
| 998 | 998 | #if defined(__OpenBSD__) && !defined(_BSD_SOURCE) |
| 999 | 999 | # define _BSD_SOURCE |
| 1000 | 1000 | #endif |
| 1001 | 1001 | |
| 1002 | +/* | |
| 1003 | +** Macro to disable warnings about missing "break" at the end of a "case". | |
| 1004 | +*/ | |
| 1005 | +#if GCC_VERSION>=7000000 | |
| 1006 | +# define deliberate_fall_through __attribute__((fallthrough)); | |
| 1007 | +#else | |
| 1008 | +# define deliberate_fall_through | |
| 1009 | +#endif | |
| 1010 | + | |
| 1002 | 1011 | /* |
| 1003 | 1012 | ** For MinGW, check to see if we can include the header file containing its |
| 1004 | 1013 | ** version information, among other things. Normally, this internal MinGW |
| 1005 | 1014 | ** header file would [only] be included automatically by other MinGW header |
| 1006 | 1015 | ** files; however, the contained version information is now required by this |
| @@ -1162,11 +1171,11 @@ | ||
| 1162 | 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | 1173 | */ |
| 1165 | 1174 | #define SQLITE_VERSION "3.33.0" |
| 1166 | 1175 | #define SQLITE_VERSION_NUMBER 3033000 |
| 1167 | -#define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3" | |
| 1176 | +#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" | |
| 1168 | 1177 | |
| 1169 | 1178 | /* |
| 1170 | 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | 1181 | ** |
| @@ -14532,11 +14541,11 @@ | ||
| 14532 | 14541 | # define SELECTTRACE_ENABLED 0 |
| 14533 | 14542 | #endif |
| 14534 | 14543 | #if defined(SQLITE_ENABLE_SELECTTRACE) |
| 14535 | 14544 | # define SELECTTRACE_ENABLED 1 |
| 14536 | 14545 | # define SELECTTRACE(K,P,S,X) \ |
| 14537 | - if(sqlite3SelectTrace&(K)) \ | |
| 14546 | + if(sqlite3_unsupported_selecttrace&(K)) \ | |
| 14538 | 14547 | sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ |
| 14539 | 14548 | sqlite3DebugPrintf X |
| 14540 | 14549 | #else |
| 14541 | 14550 | # define SELECTTRACE(K,P,S,X) |
| 14542 | 14551 | # define SELECTTRACE_ENABLED 0 |
| @@ -14595,11 +14604,11 @@ | ||
| 14595 | 14604 | ** one parameter that destructors normally want. So we have to introduce |
| 14596 | 14605 | ** this magic value that the code knows to handle differently. Any |
| 14597 | 14606 | ** pointer will work here as long as it is distinct from SQLITE_STATIC |
| 14598 | 14607 | ** and SQLITE_TRANSIENT. |
| 14599 | 14608 | */ |
| 14600 | -#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) | |
| 14609 | +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) | |
| 14601 | 14610 | |
| 14602 | 14611 | /* |
| 14603 | 14612 | ** When SQLITE_OMIT_WSD is defined, it means that the target platform does |
| 14604 | 14613 | ** not support Writable Static Data (WSD) such as global and static variables. |
| 14605 | 14614 | ** All variables must either be on the stack or dynamically allocated from |
| @@ -14735,10 +14744,257 @@ | ||
| 14735 | 14744 | /* |
| 14736 | 14745 | ** Defer sourcing vdbe.h and btree.h until after the "u8" and |
| 14737 | 14746 | ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque |
| 14738 | 14747 | ** pointer types (i.e. FuncDef) defined above. |
| 14739 | 14748 | */ |
| 14749 | +/************** Include pager.h in the middle of sqliteInt.h *****************/ | |
| 14750 | +/************** Begin file pager.h *******************************************/ | |
| 14751 | +/* | |
| 14752 | +** 2001 September 15 | |
| 14753 | +** | |
| 14754 | +** The author disclaims copyright to this source code. In place of | |
| 14755 | +** a legal notice, here is a blessing: | |
| 14756 | +** | |
| 14757 | +** May you do good and not evil. | |
| 14758 | +** May you find forgiveness for yourself and forgive others. | |
| 14759 | +** May you share freely, never taking more than you give. | |
| 14760 | +** | |
| 14761 | +************************************************************************* | |
| 14762 | +** This header file defines the interface that the sqlite page cache | |
| 14763 | +** subsystem. The page cache subsystem reads and writes a file a page | |
| 14764 | +** at a time and provides a journal for rollback. | |
| 14765 | +*/ | |
| 14766 | + | |
| 14767 | +#ifndef SQLITE_PAGER_H | |
| 14768 | +#define SQLITE_PAGER_H | |
| 14769 | + | |
| 14770 | +/* | |
| 14771 | +** Default maximum size for persistent journal files. A negative | |
| 14772 | +** value means no limit. This value may be overridden using the | |
| 14773 | +** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". | |
| 14774 | +*/ | |
| 14775 | +#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT | |
| 14776 | + #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 | |
| 14777 | +#endif | |
| 14778 | + | |
| 14779 | +/* | |
| 14780 | +** The type used to represent a page number. The first page in a file | |
| 14781 | +** is called page 1. 0 is used to represent "not a page". | |
| 14782 | +*/ | |
| 14783 | +typedef u32 Pgno; | |
| 14784 | + | |
| 14785 | +/* | |
| 14786 | +** Each open file is managed by a separate instance of the "Pager" structure. | |
| 14787 | +*/ | |
| 14788 | +typedef struct Pager Pager; | |
| 14789 | + | |
| 14790 | +/* | |
| 14791 | +** Handle type for pages. | |
| 14792 | +*/ | |
| 14793 | +typedef struct PgHdr DbPage; | |
| 14794 | + | |
| 14795 | +/* | |
| 14796 | +** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is | |
| 14797 | +** reserved for working around a windows/posix incompatibility). It is | |
| 14798 | +** used in the journal to signify that the remainder of the journal file | |
| 14799 | +** is devoted to storing a super-journal name - there are no more pages to | |
| 14800 | +** roll back. See comments for function writeSuperJournal() in pager.c | |
| 14801 | +** for details. | |
| 14802 | +*/ | |
| 14803 | +#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) | |
| 14804 | + | |
| 14805 | +/* | |
| 14806 | +** Allowed values for the flags parameter to sqlite3PagerOpen(). | |
| 14807 | +** | |
| 14808 | +** NOTE: These values must match the corresponding BTREE_ values in btree.h. | |
| 14809 | +*/ | |
| 14810 | +#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ | |
| 14811 | +#define PAGER_MEMORY 0x0002 /* In-memory database */ | |
| 14812 | + | |
| 14813 | +/* | |
| 14814 | +** Valid values for the second argument to sqlite3PagerLockingMode(). | |
| 14815 | +*/ | |
| 14816 | +#define PAGER_LOCKINGMODE_QUERY -1 | |
| 14817 | +#define PAGER_LOCKINGMODE_NORMAL 0 | |
| 14818 | +#define PAGER_LOCKINGMODE_EXCLUSIVE 1 | |
| 14819 | + | |
| 14820 | +/* | |
| 14821 | +** Numeric constants that encode the journalmode. | |
| 14822 | +** | |
| 14823 | +** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY) | |
| 14824 | +** are exposed in the API via the "PRAGMA journal_mode" command and | |
| 14825 | +** therefore cannot be changed without a compatibility break. | |
| 14826 | +*/ | |
| 14827 | +#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ | |
| 14828 | +#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ | |
| 14829 | +#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ | |
| 14830 | +#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ | |
| 14831 | +#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ | |
| 14832 | +#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ | |
| 14833 | +#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ | |
| 14834 | + | |
| 14835 | +/* | |
| 14836 | +** Flags that make up the mask passed to sqlite3PagerGet(). | |
| 14837 | +*/ | |
| 14838 | +#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ | |
| 14839 | +#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ | |
| 14840 | + | |
| 14841 | +/* | |
| 14842 | +** Flags for sqlite3PagerSetFlags() | |
| 14843 | +** | |
| 14844 | +** Value constraints (enforced via assert()): | |
| 14845 | +** PAGER_FULLFSYNC == SQLITE_FullFSync | |
| 14846 | +** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync | |
| 14847 | +** PAGER_CACHE_SPILL == SQLITE_CacheSpill | |
| 14848 | +*/ | |
| 14849 | +#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */ | |
| 14850 | +#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */ | |
| 14851 | +#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */ | |
| 14852 | +#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */ | |
| 14853 | +#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */ | |
| 14854 | +#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */ | |
| 14855 | +#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */ | |
| 14856 | +#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */ | |
| 14857 | +#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */ | |
| 14858 | + | |
| 14859 | +/* | |
| 14860 | +** The remainder of this file contains the declarations of the functions | |
| 14861 | +** that make up the Pager sub-system API. See source code comments for | |
| 14862 | +** a detailed description of each routine. | |
| 14863 | +*/ | |
| 14864 | + | |
| 14865 | +/* Open and close a Pager connection. */ | |
| 14866 | +SQLITE_PRIVATE int sqlite3PagerOpen( | |
| 14867 | + sqlite3_vfs*, | |
| 14868 | + Pager **ppPager, | |
| 14869 | + const char*, | |
| 14870 | + int, | |
| 14871 | + int, | |
| 14872 | + int, | |
| 14873 | + void(*)(DbPage*) | |
| 14874 | +); | |
| 14875 | +SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*); | |
| 14876 | +SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); | |
| 14877 | + | |
| 14878 | +/* Functions used to configure a Pager object. */ | |
| 14879 | +SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); | |
| 14880 | +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); | |
| 14881 | +SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); | |
| 14882 | +SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); | |
| 14883 | +SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); | |
| 14884 | +SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); | |
| 14885 | +SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); | |
| 14886 | +SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned); | |
| 14887 | +SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); | |
| 14888 | +SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); | |
| 14889 | +SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); | |
| 14890 | +SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); | |
| 14891 | +SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); | |
| 14892 | +SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); | |
| 14893 | +SQLITE_PRIVATE int sqlite3PagerFlush(Pager*); | |
| 14894 | + | |
| 14895 | +/* Functions used to obtain and release page references. */ | |
| 14896 | +SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); | |
| 14897 | +SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); | |
| 14898 | +SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); | |
| 14899 | +SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); | |
| 14900 | +SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); | |
| 14901 | +SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*); | |
| 14902 | + | |
| 14903 | +/* Operations on page references. */ | |
| 14904 | +SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); | |
| 14905 | +SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); | |
| 14906 | +SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); | |
| 14907 | +SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); | |
| 14908 | +SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); | |
| 14909 | +SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); | |
| 14910 | + | |
| 14911 | +/* Functions used to manage pager transactions and savepoints. */ | |
| 14912 | +SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); | |
| 14913 | +SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); | |
| 14914 | +SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); | |
| 14915 | +SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); | |
| 14916 | +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper); | |
| 14917 | +SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); | |
| 14918 | +SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); | |
| 14919 | +SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); | |
| 14920 | +SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); | |
| 14921 | +SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); | |
| 14922 | + | |
| 14923 | +#ifndef SQLITE_OMIT_WAL | |
| 14924 | +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); | |
| 14925 | +SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); | |
| 14926 | +SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); | |
| 14927 | +SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); | |
| 14928 | +SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); | |
| 14929 | +# ifdef SQLITE_ENABLE_SNAPSHOT | |
| 14930 | +SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); | |
| 14931 | +SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); | |
| 14932 | +SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); | |
| 14933 | +SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); | |
| 14934 | +SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); | |
| 14935 | +# endif | |
| 14936 | +#endif | |
| 14937 | + | |
| 14938 | +#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) | |
| 14939 | +SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); | |
| 14940 | +SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); | |
| 14941 | +#else | |
| 14942 | +# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK | |
| 14943 | +# define sqlite3PagerWalDb(x,y) | |
| 14944 | +#endif | |
| 14945 | + | |
| 14946 | +#ifdef SQLITE_DIRECT_OVERFLOW_READ | |
| 14947 | +SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); | |
| 14948 | +#endif | |
| 14949 | + | |
| 14950 | +#ifdef SQLITE_ENABLE_ZIPVFS | |
| 14951 | +SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); | |
| 14952 | +#endif | |
| 14953 | + | |
| 14954 | +/* Functions used to query pager state and configuration. */ | |
| 14955 | +SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); | |
| 14956 | +SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); | |
| 14957 | +#ifdef SQLITE_DEBUG | |
| 14958 | +SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); | |
| 14959 | +#endif | |
| 14960 | +SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); | |
| 14961 | +SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); | |
| 14962 | +SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); | |
| 14963 | +SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); | |
| 14964 | +SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); | |
| 14965 | +SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); | |
| 14966 | +SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); | |
| 14967 | +SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); | |
| 14968 | +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); | |
| 14969 | +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); | |
| 14970 | +SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); | |
| 14971 | + | |
| 14972 | +/* Functions used to truncate the database file. */ | |
| 14973 | +SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); | |
| 14974 | + | |
| 14975 | +SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); | |
| 14976 | + | |
| 14977 | +/* Functions to support testing and debugging. */ | |
| 14978 | +#if !defined(NDEBUG) || defined(SQLITE_TEST) | |
| 14979 | +SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); | |
| 14980 | +SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); | |
| 14981 | +#endif | |
| 14982 | +#ifdef SQLITE_TEST | |
| 14983 | +SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); | |
| 14984 | +SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); | |
| 14985 | + void disable_simulated_io_errors(void); | |
| 14986 | + void enable_simulated_io_errors(void); | |
| 14987 | +#else | |
| 14988 | +# define disable_simulated_io_errors() | |
| 14989 | +# define enable_simulated_io_errors() | |
| 14990 | +#endif | |
| 14991 | + | |
| 14992 | +#endif /* SQLITE_PAGER_H */ | |
| 14993 | + | |
| 14994 | +/************** End of pager.h ***********************************************/ | |
| 14995 | +/************** Continuing where we left off in sqliteInt.h ******************/ | |
| 14740 | 14996 | /************** Include btree.h in the middle of sqliteInt.h *****************/ |
| 14741 | 14997 | /************** Begin file btree.h *******************************************/ |
| 14742 | 14998 | /* |
| 14743 | 14999 | ** 2001 September 15 |
| 14744 | 15000 | ** |
| @@ -14810,12 +15066,12 @@ | ||
| 14810 | 15066 | SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); |
| 14811 | 15067 | #endif |
| 14812 | 15068 | SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned); |
| 14813 | 15069 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 14814 | 15070 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 14815 | -SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); | |
| 14816 | -SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); | |
| 15071 | +SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno); | |
| 15072 | +SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree*); | |
| 14817 | 15073 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 14818 | 15074 | SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); |
| 14819 | 15075 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 14820 | 15076 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 14821 | 15077 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| @@ -14823,11 +15079,11 @@ | ||
| 14823 | 15079 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char*); |
| 14824 | 15080 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); |
| 14825 | 15081 | SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); |
| 14826 | 15082 | SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); |
| 14827 | 15083 | SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); |
| 14828 | -SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); | |
| 15084 | +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags); | |
| 14829 | 15085 | SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); |
| 14830 | 15086 | SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); |
| 14831 | 15087 | SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); |
| 14832 | 15088 | SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); |
| 14833 | 15089 | SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); |
| @@ -14964,11 +15220,11 @@ | ||
| 14964 | 15220 | #define BTREE_WRCSR 0x00000004 /* read-write cursor */ |
| 14965 | 15221 | #define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */ |
| 14966 | 15222 | |
| 14967 | 15223 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 14968 | 15224 | Btree*, /* BTree containing table to open */ |
| 14969 | - int iTable, /* Index of root page */ | |
| 15225 | + Pgno iTable, /* Index of root page */ | |
| 14970 | 15226 | int wrFlag, /* 1 for writing. 0 for read-only */ |
| 14971 | 15227 | struct KeyInfo*, /* First argument to compare function */ |
| 14972 | 15228 | BtCursor *pCursor /* Space to write cursor structure */ |
| 14973 | 15229 | ); |
| 14974 | 15230 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| @@ -15055,11 +15311,11 @@ | ||
| 15055 | 15311 | SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); |
| 15056 | 15312 | SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); |
| 15057 | 15313 | SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); |
| 15058 | 15314 | SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); |
| 15059 | 15315 | |
| 15060 | -SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,int*aRoot,int nRoot,int,int*); | |
| 15316 | +SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); | |
| 15061 | 15317 | SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); |
| 15062 | 15318 | SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); |
| 15063 | 15319 | |
| 15064 | 15320 | #ifndef SQLITE_OMIT_INCRBLOB |
| 15065 | 15321 | SQLITE_PRIVATE int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*); |
| @@ -15192,11 +15448,11 @@ | ||
| 15192 | 15448 | sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */ |
| 15193 | 15449 | CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ |
| 15194 | 15450 | Mem *pMem; /* Used when p4type is P4_MEM */ |
| 15195 | 15451 | VTable *pVtab; /* Used when p4type is P4_VTAB */ |
| 15196 | 15452 | KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ |
| 15197 | - int *ai; /* Used when p4type is P4_INTARRAY */ | |
| 15453 | + u32 *ai; /* Used when p4type is P4_INTARRAY */ | |
| 15198 | 15454 | SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ |
| 15199 | 15455 | Table *pTab; /* Used when p4type is P4_TABLE */ |
| 15200 | 15456 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 15201 | 15457 | Expr *pExpr; /* Used when p4type is P4_EXPR */ |
| 15202 | 15458 | #endif |
| @@ -15756,257 +16012,10 @@ | ||
| 15756 | 16012 | #endif |
| 15757 | 16013 | |
| 15758 | 16014 | #endif /* SQLITE_VDBE_H */ |
| 15759 | 16015 | |
| 15760 | 16016 | /************** End of vdbe.h ************************************************/ |
| 15761 | -/************** Continuing where we left off in sqliteInt.h ******************/ | |
| 15762 | -/************** Include pager.h in the middle of sqliteInt.h *****************/ | |
| 15763 | -/************** Begin file pager.h *******************************************/ | |
| 15764 | -/* | |
| 15765 | -** 2001 September 15 | |
| 15766 | -** | |
| 15767 | -** The author disclaims copyright to this source code. In place of | |
| 15768 | -** a legal notice, here is a blessing: | |
| 15769 | -** | |
| 15770 | -** May you do good and not evil. | |
| 15771 | -** May you find forgiveness for yourself and forgive others. | |
| 15772 | -** May you share freely, never taking more than you give. | |
| 15773 | -** | |
| 15774 | -************************************************************************* | |
| 15775 | -** This header file defines the interface that the sqlite page cache | |
| 15776 | -** subsystem. The page cache subsystem reads and writes a file a page | |
| 15777 | -** at a time and provides a journal for rollback. | |
| 15778 | -*/ | |
| 15779 | - | |
| 15780 | -#ifndef SQLITE_PAGER_H | |
| 15781 | -#define SQLITE_PAGER_H | |
| 15782 | - | |
| 15783 | -/* | |
| 15784 | -** Default maximum size for persistent journal files. A negative | |
| 15785 | -** value means no limit. This value may be overridden using the | |
| 15786 | -** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". | |
| 15787 | -*/ | |
| 15788 | -#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT | |
| 15789 | - #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 | |
| 15790 | -#endif | |
| 15791 | - | |
| 15792 | -/* | |
| 15793 | -** The type used to represent a page number. The first page in a file | |
| 15794 | -** is called page 1. 0 is used to represent "not a page". | |
| 15795 | -*/ | |
| 15796 | -typedef u32 Pgno; | |
| 15797 | - | |
| 15798 | -/* | |
| 15799 | -** Each open file is managed by a separate instance of the "Pager" structure. | |
| 15800 | -*/ | |
| 15801 | -typedef struct Pager Pager; | |
| 15802 | - | |
| 15803 | -/* | |
| 15804 | -** Handle type for pages. | |
| 15805 | -*/ | |
| 15806 | -typedef struct PgHdr DbPage; | |
| 15807 | - | |
| 15808 | -/* | |
| 15809 | -** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is | |
| 15810 | -** reserved for working around a windows/posix incompatibility). It is | |
| 15811 | -** used in the journal to signify that the remainder of the journal file | |
| 15812 | -** is devoted to storing a super-journal name - there are no more pages to | |
| 15813 | -** roll back. See comments for function writeSuperJournal() in pager.c | |
| 15814 | -** for details. | |
| 15815 | -*/ | |
| 15816 | -#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) | |
| 15817 | - | |
| 15818 | -/* | |
| 15819 | -** Allowed values for the flags parameter to sqlite3PagerOpen(). | |
| 15820 | -** | |
| 15821 | -** NOTE: These values must match the corresponding BTREE_ values in btree.h. | |
| 15822 | -*/ | |
| 15823 | -#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ | |
| 15824 | -#define PAGER_MEMORY 0x0002 /* In-memory database */ | |
| 15825 | - | |
| 15826 | -/* | |
| 15827 | -** Valid values for the second argument to sqlite3PagerLockingMode(). | |
| 15828 | -*/ | |
| 15829 | -#define PAGER_LOCKINGMODE_QUERY -1 | |
| 15830 | -#define PAGER_LOCKINGMODE_NORMAL 0 | |
| 15831 | -#define PAGER_LOCKINGMODE_EXCLUSIVE 1 | |
| 15832 | - | |
| 15833 | -/* | |
| 15834 | -** Numeric constants that encode the journalmode. | |
| 15835 | -** | |
| 15836 | -** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY) | |
| 15837 | -** are exposed in the API via the "PRAGMA journal_mode" command and | |
| 15838 | -** therefore cannot be changed without a compatibility break. | |
| 15839 | -*/ | |
| 15840 | -#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ | |
| 15841 | -#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ | |
| 15842 | -#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ | |
| 15843 | -#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ | |
| 15844 | -#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ | |
| 15845 | -#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ | |
| 15846 | -#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ | |
| 15847 | - | |
| 15848 | -/* | |
| 15849 | -** Flags that make up the mask passed to sqlite3PagerGet(). | |
| 15850 | -*/ | |
| 15851 | -#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ | |
| 15852 | -#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ | |
| 15853 | - | |
| 15854 | -/* | |
| 15855 | -** Flags for sqlite3PagerSetFlags() | |
| 15856 | -** | |
| 15857 | -** Value constraints (enforced via assert()): | |
| 15858 | -** PAGER_FULLFSYNC == SQLITE_FullFSync | |
| 15859 | -** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync | |
| 15860 | -** PAGER_CACHE_SPILL == SQLITE_CacheSpill | |
| 15861 | -*/ | |
| 15862 | -#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */ | |
| 15863 | -#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */ | |
| 15864 | -#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */ | |
| 15865 | -#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */ | |
| 15866 | -#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */ | |
| 15867 | -#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */ | |
| 15868 | -#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */ | |
| 15869 | -#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */ | |
| 15870 | -#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */ | |
| 15871 | - | |
| 15872 | -/* | |
| 15873 | -** The remainder of this file contains the declarations of the functions | |
| 15874 | -** that make up the Pager sub-system API. See source code comments for | |
| 15875 | -** a detailed description of each routine. | |
| 15876 | -*/ | |
| 15877 | - | |
| 15878 | -/* Open and close a Pager connection. */ | |
| 15879 | -SQLITE_PRIVATE int sqlite3PagerOpen( | |
| 15880 | - sqlite3_vfs*, | |
| 15881 | - Pager **ppPager, | |
| 15882 | - const char*, | |
| 15883 | - int, | |
| 15884 | - int, | |
| 15885 | - int, | |
| 15886 | - void(*)(DbPage*) | |
| 15887 | -); | |
| 15888 | -SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*); | |
| 15889 | -SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); | |
| 15890 | - | |
| 15891 | -/* Functions used to configure a Pager object. */ | |
| 15892 | -SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); | |
| 15893 | -SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); | |
| 15894 | -SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); | |
| 15895 | -SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); | |
| 15896 | -SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); | |
| 15897 | -SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); | |
| 15898 | -SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); | |
| 15899 | -SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned); | |
| 15900 | -SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); | |
| 15901 | -SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); | |
| 15902 | -SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); | |
| 15903 | -SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); | |
| 15904 | -SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); | |
| 15905 | -SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); | |
| 15906 | -SQLITE_PRIVATE int sqlite3PagerFlush(Pager*); | |
| 15907 | - | |
| 15908 | -/* Functions used to obtain and release page references. */ | |
| 15909 | -SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); | |
| 15910 | -SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); | |
| 15911 | -SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); | |
| 15912 | -SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); | |
| 15913 | -SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); | |
| 15914 | -SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*); | |
| 15915 | - | |
| 15916 | -/* Operations on page references. */ | |
| 15917 | -SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); | |
| 15918 | -SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); | |
| 15919 | -SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); | |
| 15920 | -SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); | |
| 15921 | -SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); | |
| 15922 | -SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); | |
| 15923 | - | |
| 15924 | -/* Functions used to manage pager transactions and savepoints. */ | |
| 15925 | -SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); | |
| 15926 | -SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); | |
| 15927 | -SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); | |
| 15928 | -SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); | |
| 15929 | -SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper); | |
| 15930 | -SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); | |
| 15931 | -SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); | |
| 15932 | -SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); | |
| 15933 | -SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); | |
| 15934 | -SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); | |
| 15935 | - | |
| 15936 | -#ifndef SQLITE_OMIT_WAL | |
| 15937 | -SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); | |
| 15938 | -SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); | |
| 15939 | -SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); | |
| 15940 | -SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); | |
| 15941 | -SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); | |
| 15942 | -# ifdef SQLITE_ENABLE_SNAPSHOT | |
| 15943 | -SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); | |
| 15944 | -SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); | |
| 15945 | -SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); | |
| 15946 | -SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); | |
| 15947 | -SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); | |
| 15948 | -# endif | |
| 15949 | -#endif | |
| 15950 | - | |
| 15951 | -#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) | |
| 15952 | -SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); | |
| 15953 | -SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); | |
| 15954 | -#else | |
| 15955 | -# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK | |
| 15956 | -# define sqlite3PagerWalDb(x,y) | |
| 15957 | -#endif | |
| 15958 | - | |
| 15959 | -#ifdef SQLITE_DIRECT_OVERFLOW_READ | |
| 15960 | -SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); | |
| 15961 | -#endif | |
| 15962 | - | |
| 15963 | -#ifdef SQLITE_ENABLE_ZIPVFS | |
| 15964 | -SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); | |
| 15965 | -#endif | |
| 15966 | - | |
| 15967 | -/* Functions used to query pager state and configuration. */ | |
| 15968 | -SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); | |
| 15969 | -SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); | |
| 15970 | -#ifdef SQLITE_DEBUG | |
| 15971 | -SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); | |
| 15972 | -#endif | |
| 15973 | -SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); | |
| 15974 | -SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); | |
| 15975 | -SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); | |
| 15976 | -SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); | |
| 15977 | -SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); | |
| 15978 | -SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); | |
| 15979 | -SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); | |
| 15980 | -SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); | |
| 15981 | -SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); | |
| 15982 | -SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); | |
| 15983 | -SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); | |
| 15984 | - | |
| 15985 | -/* Functions used to truncate the database file. */ | |
| 15986 | -SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); | |
| 15987 | - | |
| 15988 | -SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); | |
| 15989 | - | |
| 15990 | -/* Functions to support testing and debugging. */ | |
| 15991 | -#if !defined(NDEBUG) || defined(SQLITE_TEST) | |
| 15992 | -SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); | |
| 15993 | -SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); | |
| 15994 | -#endif | |
| 15995 | -#ifdef SQLITE_TEST | |
| 15996 | -SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); | |
| 15997 | -SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); | |
| 15998 | - void disable_simulated_io_errors(void); | |
| 15999 | - void enable_simulated_io_errors(void); | |
| 16000 | -#else | |
| 16001 | -# define disable_simulated_io_errors() | |
| 16002 | -# define enable_simulated_io_errors() | |
| 16003 | -#endif | |
| 16004 | - | |
| 16005 | -#endif /* SQLITE_PAGER_H */ | |
| 16006 | - | |
| 16007 | -/************** End of pager.h ***********************************************/ | |
| 16008 | 16017 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 16009 | 16018 | /************** Include pcache.h in the middle of sqliteInt.h ****************/ |
| 16010 | 16019 | /************** Begin file pcache.h ******************************************/ |
| 16011 | 16020 | /* |
| 16012 | 16021 | ** 2008 August 05 |
| @@ -16843,11 +16852,11 @@ | ||
| 16843 | 16852 | int nChange; /* Value returned by sqlite3_changes() */ |
| 16844 | 16853 | int nTotalChange; /* Value returned by sqlite3_total_changes() */ |
| 16845 | 16854 | int aLimit[SQLITE_N_LIMIT]; /* Limits */ |
| 16846 | 16855 | int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ |
| 16847 | 16856 | struct sqlite3InitInfo { /* Information used during initialization */ |
| 16848 | - int newTnum; /* Rootpage of table being initialized */ | |
| 16857 | + Pgno newTnum; /* Rootpage of table being initialized */ | |
| 16849 | 16858 | u8 iDb; /* Which db file is being initialized */ |
| 16850 | 16859 | u8 busy; /* TRUE if currently initializing */ |
| 16851 | 16860 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 16852 | 16861 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 16853 | 16862 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| @@ -16858,11 +16867,14 @@ | ||
| 16858 | 16867 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 16859 | 16868 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| 16860 | 16869 | int nVDestroy; /* Number of active OP_VDestroy operations */ |
| 16861 | 16870 | int nExtension; /* Number of loaded extensions */ |
| 16862 | 16871 | void **aExtension; /* Array of shared library handles */ |
| 16863 | - int (*xTrace)(u32,void*,void*,void*); /* Trace function */ | |
| 16872 | + union { | |
| 16873 | + void (*xLegacy)(void*,const char*); /* Legacy trace function */ | |
| 16874 | + int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ | |
| 16875 | + } trace; | |
| 16864 | 16876 | void *pTraceArg; /* Argument to the trace function */ |
| 16865 | 16877 | #ifndef SQLITE_OMIT_DEPRECATED |
| 16866 | 16878 | void (*xProfile)(void*,const char*,u64); /* Profiling function */ |
| 16867 | 16879 | void *pProfileArg; /* Argument to profile function */ |
| 16868 | 16880 | #endif |
| @@ -17482,11 +17494,11 @@ | ||
| 17482 | 17494 | Select *pSelect; /* NULL for tables. Points to definition if a view. */ |
| 17483 | 17495 | FKey *pFKey; /* Linked list of all foreign keys in this table */ |
| 17484 | 17496 | char *zColAff; /* String defining the affinity of each column */ |
| 17485 | 17497 | ExprList *pCheck; /* All CHECK constraints */ |
| 17486 | 17498 | /* ... also used as column name list in a VIEW */ |
| 17487 | - int tnum; /* Root BTree page for this table */ | |
| 17499 | + Pgno tnum; /* Root BTree page for this table */ | |
| 17488 | 17500 | u32 nTabRef; /* Number of pointers to this Table */ |
| 17489 | 17501 | u32 tabFlags; /* Mask of TF_* values */ |
| 17490 | 17502 | i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ |
| 17491 | 17503 | i16 nCol; /* Number of columns in this table */ |
| 17492 | 17504 | i16 nNVCol; /* Number of columns that are not VIRTUAL */ |
| @@ -17775,11 +17787,11 @@ | ||
| 17775 | 17787 | Schema *pSchema; /* Schema containing this index */ |
| 17776 | 17788 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 17777 | 17789 | const char **azColl; /* Array of collation sequence names for index */ |
| 17778 | 17790 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 17779 | 17791 | ExprList *aColExpr; /* Column expressions */ |
| 17780 | - int tnum; /* DB Page containing root of this index */ | |
| 17792 | + Pgno tnum; /* DB Page containing root of this index */ | |
| 17781 | 17793 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 17782 | 17794 | u16 nKeyCol; /* Number of columns forming the key */ |
| 17783 | 17795 | u16 nColumn; /* Number of columns stored in the index */ |
| 17784 | 17796 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 17785 | 17797 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| @@ -17901,15 +17913,10 @@ | ||
| 17901 | 17913 | int nFunc; /* Number of entries in aFunc[] */ |
| 17902 | 17914 | u32 selId; /* Select to which this AggInfo belongs */ |
| 17903 | 17915 | AggInfo *pNext; /* Next in list of them all */ |
| 17904 | 17916 | }; |
| 17905 | 17917 | |
| 17906 | -/* | |
| 17907 | -** Value for AggInfo.iAggMagic when the structure is valid | |
| 17908 | -*/ | |
| 17909 | -#define AggInfoMagic 0x2059e99e | |
| 17910 | - | |
| 17911 | 17918 | /* |
| 17912 | 17919 | ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. |
| 17913 | 17920 | ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater |
| 17914 | 17921 | ** than 32767 we have to make it 32-bit. 16-bit is preferred because |
| 17915 | 17922 | ** it uses less memory in the Expr object, which is a big memory user |
| @@ -18744,13 +18751,11 @@ | ||
| 18744 | 18751 | |
| 18745 | 18752 | Token sLastToken; /* The last token parsed */ |
| 18746 | 18753 | ynVar nVar; /* Number of '?' variables seen in the SQL so far */ |
| 18747 | 18754 | u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ |
| 18748 | 18755 | u8 explain; /* True if the EXPLAIN flag is found on the query */ |
| 18749 | -#if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)) | |
| 18750 | 18756 | u8 eParseMode; /* PARSE_MODE_XXX constant */ |
| 18751 | -#endif | |
| 18752 | 18757 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 18753 | 18758 | int nVtabLock; /* Number of virtual tables to lock */ |
| 18754 | 18759 | #endif |
| 18755 | 18760 | int nHeight; /* Expression tree height of current sub-select */ |
| 18756 | 18761 | #ifndef SQLITE_OMIT_EXPLAIN |
| @@ -18990,10 +18995,11 @@ | ||
| 18990 | 18995 | char **pzErrMsg; /* Error message stored here */ |
| 18991 | 18996 | int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ |
| 18992 | 18997 | int rc; /* Result code stored here */ |
| 18993 | 18998 | u32 mInitFlags; /* Flags controlling error messages */ |
| 18994 | 18999 | u32 nInitRow; /* Number of rows processed */ |
| 19000 | + Pgno mxPage; /* Maximum page number. 0 for no limit. */ | |
| 18995 | 19001 | } InitData; |
| 18996 | 19002 | |
| 18997 | 19003 | /* |
| 18998 | 19004 | ** Allowed values for mInitFlags |
| 18999 | 19005 | */ |
| @@ -19823,12 +19829,14 @@ | ||
| 19823 | 19829 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 19824 | 19830 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 19825 | 19831 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 19826 | 19832 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 19827 | 19833 | SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); |
| 19834 | +SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); | |
| 19828 | 19835 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 19829 | 19836 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 19837 | +SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); | |
| 19830 | 19838 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 19831 | 19839 | #ifndef SQLITE_OMIT_UTF16 |
| 19832 | 19840 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 19833 | 19841 | #endif |
| 19834 | 19842 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| @@ -19944,19 +19952,19 @@ | ||
| 19944 | 19952 | SQLITE_PRIVATE const char sqlite3StrBINARY[]; |
| 19945 | 19953 | SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; |
| 19946 | 19954 | SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; |
| 19947 | 19955 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; |
| 19948 | 19956 | SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; |
| 19949 | -SQLITE_PRIVATE u32 sqlite3SelectTrace; | |
| 19957 | +SQLITE_API extern u32 sqlite3_unsupported_selecttrace; | |
| 19950 | 19958 | #ifndef SQLITE_OMIT_WSD |
| 19951 | 19959 | SQLITE_PRIVATE int sqlite3PendingByte; |
| 19952 | 19960 | #endif |
| 19953 | 19961 | #endif /* SQLITE_AMALGAMATION */ |
| 19954 | 19962 | #ifdef VDBE_PROFILE |
| 19955 | 19963 | SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt; |
| 19956 | 19964 | #endif |
| 19957 | -SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int); | |
| 19965 | +SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno); | |
| 19958 | 19966 | SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); |
| 19959 | 19967 | SQLITE_PRIVATE void sqlite3AlterFunctions(void); |
| 19960 | 19968 | SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); |
| 19961 | 19969 | SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); |
| 19962 | 19970 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| @@ -20066,11 +20074,11 @@ | ||
| 20066 | 20074 | #else |
| 20067 | 20075 | # define sqlite3CloseExtensions(X) |
| 20068 | 20076 | #endif |
| 20069 | 20077 | |
| 20070 | 20078 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 20071 | -SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, int, u8, const char *); | |
| 20079 | +SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, Pgno, u8, const char *); | |
| 20072 | 20080 | #else |
| 20073 | 20081 | #define sqlite3TableLock(v,w,x,y,z) |
| 20074 | 20082 | #endif |
| 20075 | 20083 | |
| 20076 | 20084 | #ifdef SQLITE_TEST |
| @@ -20661,11 +20669,11 @@ | ||
| 20661 | 20669 | #endif |
| 20662 | 20670 | |
| 20663 | 20671 | /* |
| 20664 | 20672 | ** Flags for select tracing and the ".selecttrace" macro of the CLI |
| 20665 | 20673 | */ |
| 20666 | -/**/ u32 sqlite3SelectTrace = 0; | |
| 20674 | +SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; | |
| 20667 | 20675 | |
| 20668 | 20676 | /* #include "opcodes.h" */ |
| 20669 | 20677 | /* |
| 20670 | 20678 | ** Properties of opcodes. The OPFLG_INITIALIZER macro is |
| 20671 | 20679 | ** created by mkopcodeh.awk during compilation. Data is obtained |
| @@ -20788,11 +20796,11 @@ | ||
| 20788 | 20796 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 20789 | 20797 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 20790 | 20798 | Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 20791 | 20799 | Btree *pBtx; /* Separate file holding temporary table */ |
| 20792 | 20800 | i64 seqCount; /* Sequence counter */ |
| 20793 | - int *aAltMap; /* Mapping from table to index column numbers */ | |
| 20801 | + u32 *aAltMap; /* Mapping from table to index column numbers */ | |
| 20794 | 20802 | |
| 20795 | 20803 | /* Cached OP_Column parse information is only valid if cacheStatus matches |
| 20796 | 20804 | ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of |
| 20797 | 20805 | ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that |
| 20798 | 20806 | ** the cache is out of date. */ |
| @@ -21184,11 +21192,11 @@ | ||
| 21184 | 21192 | */ |
| 21185 | 21193 | SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); |
| 21186 | 21194 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); |
| 21187 | 21195 | void sqliteVdbePopStack(Vdbe*,int); |
| 21188 | 21196 | SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); |
| 21189 | -SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); | |
| 21197 | +SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); | |
| 21190 | 21198 | SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); |
| 21191 | 21199 | SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); |
| 21192 | 21200 | SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); |
| 21193 | 21201 | SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); |
| 21194 | 21202 | SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); |
| @@ -21660,11 +21668,11 @@ | ||
| 21660 | 21668 | ** pagers the database handle is connected to. *pHighwater is always set |
| 21661 | 21669 | ** to zero. |
| 21662 | 21670 | */ |
| 21663 | 21671 | case SQLITE_DBSTATUS_CACHE_SPILL: |
| 21664 | 21672 | op = SQLITE_DBSTATUS_CACHE_WRITE+1; |
| 21665 | - /* Fall through into the next case */ | |
| 21673 | + /* no break */ deliberate_fall_through | |
| 21666 | 21674 | case SQLITE_DBSTATUS_CACHE_HIT: |
| 21667 | 21675 | case SQLITE_DBSTATUS_CACHE_MISS: |
| 21668 | 21676 | case SQLITE_DBSTATUS_CACHE_WRITE:{ |
| 21669 | 21677 | int i; |
| 21670 | 21678 | int nRet = 0; |
| @@ -22816,12 +22824,12 @@ | ||
| 22816 | 22824 | break; |
| 22817 | 22825 | } |
| 22818 | 22826 | case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; |
| 22819 | 22827 | case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; |
| 22820 | 22828 | case 's': { |
| 22821 | - sqlite3_snprintf(30,&z[j],"%lld", | |
| 22822 | - (i64)(x.iJD/1000 - 21086676*(i64)10000)); | |
| 22829 | + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); | |
| 22830 | + sqlite3Int64ToText(iS, &z[j]); | |
| 22823 | 22831 | j += sqlite3Strlen30(&z[j]); |
| 22824 | 22832 | break; |
| 22825 | 22833 | } |
| 22826 | 22834 | case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; |
| 22827 | 22835 | case 'w': { |
| @@ -28537,15 +28545,15 @@ | ||
| 28537 | 28545 | assert( precision>=(-1) ); |
| 28538 | 28546 | switch( xtype ){ |
| 28539 | 28547 | case etPOINTER: |
| 28540 | 28548 | flag_long = sizeof(char*)==sizeof(i64) ? 2 : |
| 28541 | 28549 | sizeof(char*)==sizeof(long int) ? 1 : 0; |
| 28542 | - /* Fall through into the next case */ | |
| 28550 | + /* no break */ deliberate_fall_through | |
| 28543 | 28551 | case etORDINAL: |
| 28544 | 28552 | case etRADIX: |
| 28545 | 28553 | cThousand = 0; |
| 28546 | - /* Fall through into the next case */ | |
| 28554 | + /* no break */ deliberate_fall_through | |
| 28547 | 28555 | case etDECIMAL: |
| 28548 | 28556 | if( infop->flags & FLAG_SIGNED ){ |
| 28549 | 28557 | i64 v; |
| 28550 | 28558 | if( bArgList ){ |
| 28551 | 28559 | v = getIntArg(pArgList); |
| @@ -31770,10 +31778,34 @@ | ||
| 31770 | 31778 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 31771 | 31779 | } |
| 31772 | 31780 | #if defined(_MSC_VER) |
| 31773 | 31781 | #pragma warning(default : 4756) |
| 31774 | 31782 | #endif |
| 31783 | + | |
| 31784 | +/* | |
| 31785 | +** Render an signed 64-bit integer as text. Store the result in zOut[]. | |
| 31786 | +** | |
| 31787 | +** The caller must ensure that zOut[] is at least 21 bytes in size. | |
| 31788 | +*/ | |
| 31789 | +SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ | |
| 31790 | + int i; | |
| 31791 | + u64 x; | |
| 31792 | + char zTemp[22]; | |
| 31793 | + if( v<0 ){ | |
| 31794 | + x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; | |
| 31795 | + }else{ | |
| 31796 | + x = v; | |
| 31797 | + } | |
| 31798 | + i = sizeof(zTemp)-2; | |
| 31799 | + zTemp[sizeof(zTemp)-1] = 0; | |
| 31800 | + do{ | |
| 31801 | + zTemp[i--] = (x%10) + '0'; | |
| 31802 | + x = x/10; | |
| 31803 | + }while( x ); | |
| 31804 | + if( v<0 ) zTemp[i--] = '-'; | |
| 31805 | + memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); | |
| 31806 | +} | |
| 31775 | 31807 | |
| 31776 | 31808 | /* |
| 31777 | 31809 | ** Compare the 19-character string zNum against the text representation |
| 31778 | 31810 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 31779 | 31811 | ** if zNum is less than, equal to, or greater than the string. |
| @@ -32011,13 +32043,31 @@ | ||
| 32011 | 32043 | ** Return a 32-bit integer value extracted from a string. If the |
| 32012 | 32044 | ** string is not an integer, just return 0. |
| 32013 | 32045 | */ |
| 32014 | 32046 | SQLITE_PRIVATE int sqlite3Atoi(const char *z){ |
| 32015 | 32047 | int x = 0; |
| 32016 | - if( z ) sqlite3GetInt32(z, &x); | |
| 32048 | + sqlite3GetInt32(z, &x); | |
| 32017 | 32049 | return x; |
| 32018 | 32050 | } |
| 32051 | + | |
| 32052 | +/* | |
| 32053 | +** Try to convert z into an unsigned 32-bit integer. Return true on | |
| 32054 | +** success and false if there is an error. | |
| 32055 | +** | |
| 32056 | +** Only decimal notation is accepted. | |
| 32057 | +*/ | |
| 32058 | +SQLITE_PRIVATE int sqlite3GetUInt32(const char *z, u32 *pI){ | |
| 32059 | + u64 v = 0; | |
| 32060 | + int i; | |
| 32061 | + for(i=0; sqlite3Isdigit(z[i]); i++){ | |
| 32062 | + v = v*10 + z[i] - '0'; | |
| 32063 | + if( v>4294967296LL ){ *pI = 0; return 0; } | |
| 32064 | + } | |
| 32065 | + if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } | |
| 32066 | + *pI = (u32)v; | |
| 32067 | + return 1; | |
| 32068 | +} | |
| 32019 | 32069 | |
| 32020 | 32070 | /* |
| 32021 | 32071 | ** The variable-length integer encoding is as follows: |
| 32022 | 32072 | ** |
| 32023 | 32073 | ** KEY: |
| @@ -39188,11 +39238,11 @@ | ||
| 39188 | 39238 | } |
| 39189 | 39239 | #endif |
| 39190 | 39240 | if( rc!=SQLITE_OK ){ |
| 39191 | 39241 | if( h>=0 ) robust_close(pNew, h, __LINE__); |
| 39192 | 39242 | }else{ |
| 39193 | - pNew->pMethod = pLockingStyle; | |
| 39243 | + pId->pMethods = pLockingStyle; | |
| 39194 | 39244 | OpenCounter(+1); |
| 39195 | 39245 | verifyDbFile(pNew); |
| 39196 | 39246 | } |
| 39197 | 39247 | return rc; |
| 39198 | 39248 | } |
| @@ -46899,11 +46949,11 @@ | ||
| 46899 | 46949 | { |
| 46900 | 46950 | sqlite3_free(zConverted); |
| 46901 | 46951 | } |
| 46902 | 46952 | |
| 46903 | 46953 | sqlite3_free(zTmpname); |
| 46904 | - pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod; | |
| 46954 | + id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; | |
| 46905 | 46955 | pFile->pVfs = pVfs; |
| 46906 | 46956 | pFile->h = h; |
| 46907 | 46957 | if( isReadonly ){ |
| 46908 | 46958 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46909 | 46959 | } |
| @@ -48125,11 +48175,11 @@ | ||
| 48125 | 48175 | } |
| 48126 | 48176 | memset(p, 0, sizeof(*p)); |
| 48127 | 48177 | p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; |
| 48128 | 48178 | assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ |
| 48129 | 48179 | *pOutFlags = flags | SQLITE_OPEN_MEMORY; |
| 48130 | - p->base.pMethods = &memdb_io_methods; | |
| 48180 | + pFile->pMethods = &memdb_io_methods; | |
| 48131 | 48181 | p->szMax = sqlite3GlobalConfig.mxMemdbSize; |
| 48132 | 48182 | return SQLITE_OK; |
| 48133 | 48183 | } |
| 48134 | 48184 | |
| 48135 | 48185 | #if 0 /* Only used to delete rollback journals, super-journals, and WAL |
| @@ -52442,15 +52492,10 @@ | ||
| 52442 | 52492 | # define USEFETCH(x) ((x)->bUseFetch) |
| 52443 | 52493 | #else |
| 52444 | 52494 | # define USEFETCH(x) 0 |
| 52445 | 52495 | #endif |
| 52446 | 52496 | |
| 52447 | -/* | |
| 52448 | -** The maximum legal page number is (2^31 - 1). | |
| 52449 | -*/ | |
| 52450 | -#define PAGER_MAX_PGNO 2147483647 | |
| 52451 | - | |
| 52452 | 52497 | /* |
| 52453 | 52498 | ** The argument to this macro is a file descriptor (type sqlite3_file*). |
| 52454 | 52499 | ** Return 0 if it is not open, or non-zero (but not 1) if it is. |
| 52455 | 52500 | ** |
| 52456 | 52501 | ** This is so that expressions can be written as: |
| @@ -54153,16 +54198,17 @@ | ||
| 54153 | 54198 | |
| 54154 | 54199 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54155 | 54200 | ** If successful, open the super-journal file for reading. |
| 54156 | 54201 | */ |
| 54157 | 54202 | pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); |
| 54158 | - pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); | |
| 54159 | 54203 | if( !pSuper ){ |
| 54160 | 54204 | rc = SQLITE_NOMEM_BKPT; |
| 54205 | + pJournal = 0; | |
| 54161 | 54206 | }else{ |
| 54162 | 54207 | const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); |
| 54163 | 54208 | rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); |
| 54209 | + pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); | |
| 54164 | 54210 | } |
| 54165 | 54211 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54166 | 54212 | |
| 54167 | 54213 | /* Load the entire super-journal file into space obtained from |
| 54168 | 54214 | ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain |
| @@ -55419,11 +55465,11 @@ | ||
| 55419 | 55465 | ** Make no changes if mxPage is zero or negative. And never reduce the |
| 55420 | 55466 | ** maximum page count below the current size of the database. |
| 55421 | 55467 | ** |
| 55422 | 55468 | ** Regardless of mxPage, return the current maximum page count. |
| 55423 | 55469 | */ |
| 55424 | -SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ | |
| 55470 | +SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ | |
| 55425 | 55471 | if( mxPage>0 ){ |
| 55426 | 55472 | pPager->mxPgno = mxPage; |
| 55427 | 55473 | } |
| 55428 | 55474 | assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ |
| 55429 | 55475 | /* assert( pPager->mxPgno>=pPager->dbSize ); */ |
| @@ -57146,22 +57192,22 @@ | ||
| 57146 | 57192 | |
| 57147 | 57193 | noContent = (flags & PAGER_GET_NOCONTENT)!=0; |
| 57148 | 57194 | if( pPg->pPager && !noContent ){ |
| 57149 | 57195 | /* In this case the pcache already contains an initialized copy of |
| 57150 | 57196 | ** the page. Return without further ado. */ |
| 57151 | - assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); | |
| 57197 | + assert( pgno!=PAGER_MJ_PGNO(pPager) ); | |
| 57152 | 57198 | pPager->aStat[PAGER_STAT_HIT]++; |
| 57153 | 57199 | return SQLITE_OK; |
| 57154 | 57200 | |
| 57155 | 57201 | }else{ |
| 57156 | 57202 | /* The pager cache has created a new page. Its content needs to |
| 57157 | 57203 | ** be initialized. But first some error checks: |
| 57158 | 57204 | ** |
| 57159 | - ** (1) The maximum page number is 2^31 | |
| 57205 | + ** (*) obsolete. Was: maximum page number is 2^31 | |
| 57160 | 57206 | ** (2) Never try to fetch the locking page |
| 57161 | 57207 | */ |
| 57162 | - if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ | |
| 57208 | + if( pgno==PAGER_MJ_PGNO(pPager) ){ | |
| 57163 | 57209 | rc = SQLITE_CORRUPT_BKPT; |
| 57164 | 57210 | goto pager_acquire_err; |
| 57165 | 57211 | } |
| 57166 | 57212 | |
| 57167 | 57213 | pPg->pPager = pPager; |
| @@ -59863,11 +59909,11 @@ | ||
| 59863 | 59909 | ** walIteratorFree() - Free an iterator. |
| 59864 | 59910 | ** |
| 59865 | 59911 | ** This functionality is used by the checkpoint code (see walCheckpoint()). |
| 59866 | 59912 | */ |
| 59867 | 59913 | struct WalIterator { |
| 59868 | - int iPrior; /* Last result returned from the iterator */ | |
| 59914 | + u32 iPrior; /* Last result returned from the iterator */ | |
| 59869 | 59915 | int nSegment; /* Number of entries in aSegment[] */ |
| 59870 | 59916 | struct WalSegment { |
| 59871 | 59917 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 59872 | 59918 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 59873 | 59919 | u32 *aPgno; /* Array of page numbers. */ |
| @@ -59945,11 +59991,13 @@ | ||
| 59945 | 59991 | rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, |
| 59946 | 59992 | pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] |
| 59947 | 59993 | ); |
| 59948 | 59994 | assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); |
| 59949 | 59995 | testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); |
| 59950 | - if( (rc&0xff)==SQLITE_READONLY ){ | |
| 59996 | + if( rc==SQLITE_OK ){ | |
| 59997 | + if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; | |
| 59998 | + }else if( (rc&0xff)==SQLITE_READONLY ){ | |
| 59951 | 59999 | pWal->readOnly |= WAL_SHM_RDONLY; |
| 59952 | 60000 | if( rc==SQLITE_READONLY ){ |
| 59953 | 60001 | rc = SQLITE_OK; |
| 59954 | 60002 | } |
| 59955 | 60003 | } |
| @@ -60320,10 +60368,11 @@ | ||
| 60320 | 60368 | && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) |
| 60321 | 60369 | && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) |
| 60322 | 60370 | && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) |
| 60323 | 60371 | && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) |
| 60324 | 60372 | ); |
| 60373 | + assert( iHash>=0 ); | |
| 60325 | 60374 | return iHash; |
| 60326 | 60375 | } |
| 60327 | 60376 | |
| 60328 | 60377 | /* |
| 60329 | 60378 | ** Return the page number associated with frame iFrame in this WAL. |
| @@ -60516,16 +60565,10 @@ | ||
| 60516 | 60565 | assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); |
| 60517 | 60566 | assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); |
| 60518 | 60567 | assert( pWal->writeLock ); |
| 60519 | 60568 | iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; |
| 60520 | 60569 | rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60521 | - if( rc==SQLITE_OK ){ | |
| 60522 | - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); | |
| 60523 | - if( rc!=SQLITE_OK ){ | |
| 60524 | - walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); | |
| 60525 | - } | |
| 60526 | - } | |
| 60527 | 60570 | if( rc ){ |
| 60528 | 60571 | return rc; |
| 60529 | 60572 | } |
| 60530 | 60573 | |
| 60531 | 60574 | WALTRACE(("WAL%p: recovery begin...\n", pWal)); |
| @@ -60537,19 +60580,20 @@ | ||
| 60537 | 60580 | goto recovery_error; |
| 60538 | 60581 | } |
| 60539 | 60582 | |
| 60540 | 60583 | if( nSize>WAL_HDRSIZE ){ |
| 60541 | 60584 | u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ |
| 60585 | + u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ | |
| 60542 | 60586 | u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ |
| 60543 | 60587 | int szFrame; /* Number of bytes in buffer aFrame[] */ |
| 60544 | 60588 | u8 *aData; /* Pointer to data part of aFrame buffer */ |
| 60545 | - int iFrame; /* Index of last frame read */ | |
| 60546 | - i64 iOffset; /* Next offset to read from log file */ | |
| 60547 | 60589 | int szPage; /* Page size according to the log */ |
| 60548 | 60590 | u32 magic; /* Magic value read from WAL header */ |
| 60549 | 60591 | u32 version; /* Magic value read from WAL header */ |
| 60550 | 60592 | int isValid; /* True if this frame is valid */ |
| 60593 | + u32 iPg; /* Current 32KB wal-index page */ | |
| 60594 | + u32 iLastFrame; /* Last frame in wal, based on nSize alone */ | |
| 60551 | 60595 | |
| 60552 | 60596 | /* Read in the WAL header. */ |
| 60553 | 60597 | rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); |
| 60554 | 60598 | if( rc!=SQLITE_OK ){ |
| 60555 | 60599 | goto recovery_error; |
| @@ -60592,42 +60636,86 @@ | ||
| 60592 | 60636 | goto finished; |
| 60593 | 60637 | } |
| 60594 | 60638 | |
| 60595 | 60639 | /* Malloc a buffer to read frames into. */ |
| 60596 | 60640 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 60597 | - aFrame = (u8 *)sqlite3_malloc64(szFrame); | |
| 60641 | + aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ); | |
| 60598 | 60642 | if( !aFrame ){ |
| 60599 | 60643 | rc = SQLITE_NOMEM_BKPT; |
| 60600 | 60644 | goto recovery_error; |
| 60601 | 60645 | } |
| 60602 | 60646 | aData = &aFrame[WAL_FRAME_HDRSIZE]; |
| 60647 | + aPrivate = (u32*)&aData[szPage]; | |
| 60603 | 60648 | |
| 60604 | 60649 | /* Read all frames from the log file. */ |
| 60605 | - iFrame = 0; | |
| 60606 | - for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ | |
| 60607 | - u32 pgno; /* Database page number for frame */ | |
| 60608 | - u32 nTruncate; /* dbsize field from frame header */ | |
| 60609 | - | |
| 60610 | - /* Read and decode the next log frame. */ | |
| 60611 | - iFrame++; | |
| 60612 | - rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); | |
| 60613 | - if( rc!=SQLITE_OK ) break; | |
| 60614 | - isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); | |
| 60615 | - if( !isValid ) break; | |
| 60616 | - rc = walIndexAppend(pWal, iFrame, pgno); | |
| 60617 | - if( rc!=SQLITE_OK ) break; | |
| 60618 | - | |
| 60619 | - /* If nTruncate is non-zero, this is a commit record. */ | |
| 60620 | - if( nTruncate ){ | |
| 60621 | - pWal->hdr.mxFrame = iFrame; | |
| 60622 | - pWal->hdr.nPage = nTruncate; | |
| 60623 | - pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); | |
| 60624 | - testcase( szPage<=32768 ); | |
| 60625 | - testcase( szPage>=65536 ); | |
| 60626 | - aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; | |
| 60627 | - aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; | |
| 60628 | - } | |
| 60650 | + iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; | |
| 60651 | + for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ | |
| 60652 | + u32 *aShare; | |
| 60653 | + u32 iFrame; /* Index of last frame read */ | |
| 60654 | + u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); | |
| 60655 | + u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); | |
| 60656 | + u32 nHdr, nHdr32; | |
| 60657 | + rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); | |
| 60658 | + if( rc ) break; | |
| 60659 | + pWal->apWiData[iPg] = aPrivate; | |
| 60660 | + | |
| 60661 | + for(iFrame=iFirst; iFrame<=iLast; iFrame++){ | |
| 60662 | + i64 iOffset = walFrameOffset(iFrame, szPage); | |
| 60663 | + u32 pgno; /* Database page number for frame */ | |
| 60664 | + u32 nTruncate; /* dbsize field from frame header */ | |
| 60665 | + | |
| 60666 | + /* Read and decode the next log frame. */ | |
| 60667 | + rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); | |
| 60668 | + if( rc!=SQLITE_OK ) break; | |
| 60669 | + isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); | |
| 60670 | + if( !isValid ) break; | |
| 60671 | + rc = walIndexAppend(pWal, iFrame, pgno); | |
| 60672 | + if( NEVER(rc!=SQLITE_OK) ) break; | |
| 60673 | + | |
| 60674 | + /* If nTruncate is non-zero, this is a commit record. */ | |
| 60675 | + if( nTruncate ){ | |
| 60676 | + pWal->hdr.mxFrame = iFrame; | |
| 60677 | + pWal->hdr.nPage = nTruncate; | |
| 60678 | + pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); | |
| 60679 | + testcase( szPage<=32768 ); | |
| 60680 | + testcase( szPage>=65536 ); | |
| 60681 | + aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; | |
| 60682 | + aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; | |
| 60683 | + } | |
| 60684 | + } | |
| 60685 | + pWal->apWiData[iPg] = aShare; | |
| 60686 | + nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); | |
| 60687 | + nHdr32 = nHdr / sizeof(u32); | |
| 60688 | +#ifndef SQLITE_SAFER_WALINDEX_RECOVERY | |
| 60689 | + /* Memcpy() should work fine here, on all reasonable implementations. | |
| 60690 | + ** Technically, memcpy() might change the destination to some | |
| 60691 | + ** intermediate value before setting to the final value, and that might | |
| 60692 | + ** cause a concurrent reader to malfunction. Memcpy() is allowed to | |
| 60693 | + ** do that, according to the spec, but no memcpy() implementation that | |
| 60694 | + ** we know of actually does that, which is why we say that memcpy() | |
| 60695 | + ** is safe for this. Memcpy() is certainly a lot faster. | |
| 60696 | + */ | |
| 60697 | + memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); | |
| 60698 | +#else | |
| 60699 | + /* In the event that some platform is found for which memcpy() | |
| 60700 | + ** changes the destination to some intermediate value before | |
| 60701 | + ** setting the final value, this alternative copy routine is | |
| 60702 | + ** provided. | |
| 60703 | + */ | |
| 60704 | + { | |
| 60705 | + int i; | |
| 60706 | + for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){ | |
| 60707 | + if( aShare[i]!=aPrivate[i] ){ | |
| 60708 | + /* Atomic memory operations are not required here because if | |
| 60709 | + ** the value needs to be changed, that means it is not being | |
| 60710 | + ** accessed concurrently. */ | |
| 60711 | + aShare[i] = aPrivate[i]; | |
| 60712 | + } | |
| 60713 | + } | |
| 60714 | + } | |
| 60715 | +#endif | |
| 60716 | + if( iFrame<=iLast ) break; | |
| 60629 | 60717 | } |
| 60630 | 60718 | |
| 60631 | 60719 | sqlite3_free(aFrame); |
| 60632 | 60720 | } |
| 60633 | 60721 | |
| @@ -60638,19 +60726,30 @@ | ||
| 60638 | 60726 | pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; |
| 60639 | 60727 | pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; |
| 60640 | 60728 | walIndexWriteHdr(pWal); |
| 60641 | 60729 | |
| 60642 | 60730 | /* Reset the checkpoint-header. This is safe because this thread is |
| 60643 | - ** currently holding locks that exclude all other readers, writers and | |
| 60644 | - ** checkpointers. | |
| 60731 | + ** currently holding locks that exclude all other writers and | |
| 60732 | + ** checkpointers. Then set the values of read-mark slots 1 through N. | |
| 60645 | 60733 | */ |
| 60646 | 60734 | pInfo = walCkptInfo(pWal); |
| 60647 | 60735 | pInfo->nBackfill = 0; |
| 60648 | 60736 | pInfo->nBackfillAttempted = pWal->hdr.mxFrame; |
| 60649 | 60737 | pInfo->aReadMark[0] = 0; |
| 60650 | - for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; | |
| 60651 | - if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame; | |
| 60738 | + for(i=1; i<WAL_NREADER; i++){ | |
| 60739 | + rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 60740 | + if( rc==SQLITE_OK ){ | |
| 60741 | + if( i==1 && pWal->hdr.mxFrame ){ | |
| 60742 | + pInfo->aReadMark[i] = pWal->hdr.mxFrame; | |
| 60743 | + }else{ | |
| 60744 | + pInfo->aReadMark[i] = READMARK_NOT_USED; | |
| 60745 | + } | |
| 60746 | + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 60747 | + }else if( rc!=SQLITE_BUSY ){ | |
| 60748 | + goto recovery_error; | |
| 60749 | + } | |
| 60750 | + } | |
| 60652 | 60751 | |
| 60653 | 60752 | /* If more than one frame was recovered from the log file, report an |
| 60654 | 60753 | ** event via sqlite3_log(). This is to help with identifying performance |
| 60655 | 60754 | ** problems caused by applications routinely shutting down without |
| 60656 | 60755 | ** checkpointing the log file. |
| @@ -60664,11 +60763,10 @@ | ||
| 60664 | 60763 | } |
| 60665 | 60764 | |
| 60666 | 60765 | recovery_error: |
| 60667 | 60766 | WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); |
| 60668 | 60767 | walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60669 | - walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); | |
| 60670 | 60768 | return rc; |
| 60671 | 60769 | } |
| 60672 | 60770 | |
| 60673 | 60771 | /* |
| 60674 | 60772 | ** Close an open wal-index. |
| @@ -61312,14 +61410,22 @@ | ||
| 61312 | 61410 | i64 nReq = ((i64)mxPage * szPage); |
| 61313 | 61411 | i64 nSize; /* Current size of database file */ |
| 61314 | 61412 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); |
| 61315 | 61413 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 61316 | 61414 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 61317 | - sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); | |
| 61415 | + if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){ | |
| 61416 | + /* If the size of the final database is larger than the current | |
| 61417 | + ** database plus the amount of data in the wal file, plus the | |
| 61418 | + ** maximum size of the pending-byte page (65536 bytes), then | |
| 61419 | + ** must be corruption somewhere. */ | |
| 61420 | + rc = SQLITE_CORRUPT_BKPT; | |
| 61421 | + }else{ | |
| 61422 | + sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); | |
| 61423 | + } | |
| 61318 | 61424 | } |
| 61425 | + | |
| 61319 | 61426 | } |
| 61320 | - | |
| 61321 | 61427 | |
| 61322 | 61428 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 61323 | 61429 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 61324 | 61430 | i64 iOffset; |
| 61325 | 61431 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| @@ -64048,11 +64154,12 @@ | ||
| 64048 | 64154 | Pgno nPage; /* Number of pages in the database */ |
| 64049 | 64155 | int mxErr; /* Stop accumulating errors when this reaches zero */ |
| 64050 | 64156 | int nErr; /* Number of messages written to zErrMsg so far */ |
| 64051 | 64157 | int bOomFault; /* A memory allocation error has occurred */ |
| 64052 | 64158 | const char *zPfx; /* Error message prefix */ |
| 64053 | - int v1, v2; /* Values for up to two %d fields in zPfx */ | |
| 64159 | + Pgno v1; /* Value for first %u substitution in zPfx */ | |
| 64160 | + int v2; /* Value for second %d substitution in zPfx */ | |
| 64054 | 64161 | StrAccum errMsg; /* Accumulate the error message text here */ |
| 64055 | 64162 | u32 *heap; /* Min-heap used for analyzing cell coverage */ |
| 64056 | 64163 | sqlite3 *db; /* Database connection running the check */ |
| 64057 | 64164 | }; |
| 64058 | 64165 | |
| @@ -66513,16 +66620,15 @@ | ||
| 66513 | 66620 | /* |
| 66514 | 66621 | ** Return the size of the database file in pages. If there is any kind of |
| 66515 | 66622 | ** error, return ((unsigned int)-1). |
| 66516 | 66623 | */ |
| 66517 | 66624 | static Pgno btreePagecount(BtShared *pBt){ |
| 66518 | - assert( (pBt->nPage & 0x80000000)==0 || CORRUPT_DB ); | |
| 66519 | 66625 | return pBt->nPage; |
| 66520 | 66626 | } |
| 66521 | -SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){ | |
| 66627 | +SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ | |
| 66522 | 66628 | assert( sqlite3BtreeHoldsMutex(p) ); |
| 66523 | - return btreePagecount(p->pBt) & 0x7fffffff; | |
| 66629 | + return btreePagecount(p->pBt); | |
| 66524 | 66630 | } |
| 66525 | 66631 | |
| 66526 | 66632 | /* |
| 66527 | 66633 | ** Get a page from the pager and initialize it. |
| 66528 | 66634 | ** |
| @@ -67306,12 +67412,12 @@ | ||
| 67306 | 67412 | /* |
| 67307 | 67413 | ** Set the maximum page count for a database if mxPage is positive. |
| 67308 | 67414 | ** No changes are made if mxPage is 0 or negative. |
| 67309 | 67415 | ** Regardless of the value of mxPage, return the maximum page count. |
| 67310 | 67416 | */ |
| 67311 | -SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){ | |
| 67312 | - int n; | |
| 67417 | +SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){ | |
| 67418 | + Pgno n; | |
| 67313 | 67419 | sqlite3BtreeEnter(p); |
| 67314 | 67420 | n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); |
| 67315 | 67421 | sqlite3BtreeLeave(p); |
| 67316 | 67422 | return n; |
| 67317 | 67423 | } |
| @@ -68746,11 +68852,11 @@ | ||
| 68746 | 68852 | ** It is assumed that the sqlite3BtreeCursorZero() has been called |
| 68747 | 68853 | ** on pCur to initialize the memory space prior to invoking this routine. |
| 68748 | 68854 | */ |
| 68749 | 68855 | static int btreeCursor( |
| 68750 | 68856 | Btree *p, /* The btree */ |
| 68751 | - int iTable, /* Root page of table to open */ | |
| 68857 | + Pgno iTable, /* Root page of table to open */ | |
| 68752 | 68858 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68753 | 68859 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68754 | 68860 | BtCursor *pCur /* Space for new cursor */ |
| 68755 | 68861 | ){ |
| 68756 | 68862 | BtShared *pBt = p->pBt; /* Shared b-tree handle */ |
| @@ -68789,21 +68895,21 @@ | ||
| 68789 | 68895 | } |
| 68790 | 68896 | } |
| 68791 | 68897 | |
| 68792 | 68898 | /* Now that no other errors can occur, finish filling in the BtCursor |
| 68793 | 68899 | ** variables and link the cursor into the BtShared list. */ |
| 68794 | - pCur->pgnoRoot = (Pgno)iTable; | |
| 68900 | + pCur->pgnoRoot = iTable; | |
| 68795 | 68901 | pCur->iPage = -1; |
| 68796 | 68902 | pCur->pKeyInfo = pKeyInfo; |
| 68797 | 68903 | pCur->pBtree = p; |
| 68798 | 68904 | pCur->pBt = pBt; |
| 68799 | 68905 | pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; |
| 68800 | 68906 | pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; |
| 68801 | 68907 | /* If there are two or more cursors on the same btree, then all such |
| 68802 | 68908 | ** cursors *must* have the BTCF_Multiple flag set. */ |
| 68803 | 68909 | for(pX=pBt->pCursor; pX; pX=pX->pNext){ |
| 68804 | - if( pX->pgnoRoot==(Pgno)iTable ){ | |
| 68910 | + if( pX->pgnoRoot==iTable ){ | |
| 68805 | 68911 | pX->curFlags |= BTCF_Multiple; |
| 68806 | 68912 | pCur->curFlags |= BTCF_Multiple; |
| 68807 | 68913 | } |
| 68808 | 68914 | } |
| 68809 | 68915 | pCur->pNext = pBt->pCursor; |
| @@ -68811,11 +68917,11 @@ | ||
| 68811 | 68917 | pCur->eState = CURSOR_INVALID; |
| 68812 | 68918 | return SQLITE_OK; |
| 68813 | 68919 | } |
| 68814 | 68920 | static int btreeCursorWithLock( |
| 68815 | 68921 | Btree *p, /* The btree */ |
| 68816 | - int iTable, /* Root page of table to open */ | |
| 68922 | + Pgno iTable, /* Root page of table to open */ | |
| 68817 | 68923 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68818 | 68924 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68819 | 68925 | BtCursor *pCur /* Space for new cursor */ |
| 68820 | 68926 | ){ |
| 68821 | 68927 | int rc; |
| @@ -68824,11 +68930,11 @@ | ||
| 68824 | 68930 | sqlite3BtreeLeave(p); |
| 68825 | 68931 | return rc; |
| 68826 | 68932 | } |
| 68827 | 68933 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 68828 | 68934 | Btree *p, /* The btree */ |
| 68829 | - int iTable, /* Root page of table to open */ | |
| 68935 | + Pgno iTable, /* Root page of table to open */ | |
| 68830 | 68936 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68831 | 68937 | struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ |
| 68832 | 68938 | BtCursor *pCur /* Write new cursor here */ |
| 68833 | 68939 | ){ |
| 68834 | 68940 | if( p->sharable ){ |
| @@ -69249,10 +69355,11 @@ | ||
| 69249 | 69355 | } |
| 69250 | 69356 | |
| 69251 | 69357 | assert( rc==SQLITE_OK && amt>0 ); |
| 69252 | 69358 | while( nextPage ){ |
| 69253 | 69359 | /* If required, populate the overflow page-list cache. */ |
| 69360 | + if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT; | |
| 69254 | 69361 | assert( pCur->aOverflow[iIdx]==0 |
| 69255 | 69362 | || pCur->aOverflow[iIdx]==nextPage |
| 69256 | 69363 | || CORRUPT_DB ); |
| 69257 | 69364 | pCur->aOverflow[iIdx] = nextPage; |
| 69258 | 69365 | |
| @@ -70664,10 +70771,14 @@ | ||
| 70664 | 70771 | */ |
| 70665 | 70772 | if( nFree!=0 ){ |
| 70666 | 70773 | u32 nLeaf; /* Initial number of leaf cells on trunk page */ |
| 70667 | 70774 | |
| 70668 | 70775 | iTrunk = get4byte(&pPage1->aData[32]); |
| 70776 | + if( iTrunk>btreePagecount(pBt) ){ | |
| 70777 | + rc = SQLITE_CORRUPT_BKPT; | |
| 70778 | + goto freepage_out; | |
| 70779 | + } | |
| 70669 | 70780 | rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); |
| 70670 | 70781 | if( rc!=SQLITE_OK ){ |
| 70671 | 70782 | goto freepage_out; |
| 70672 | 70783 | } |
| 70673 | 70784 | |
| @@ -73468,11 +73579,11 @@ | ||
| 73468 | 73579 | ** flags might not work: |
| 73469 | 73580 | ** |
| 73470 | 73581 | ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys |
| 73471 | 73582 | ** BTREE_ZERODATA Used for SQL indices |
| 73472 | 73583 | */ |
| 73473 | -static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ | |
| 73584 | +static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ | |
| 73474 | 73585 | BtShared *pBt = p->pBt; |
| 73475 | 73586 | MemPage *pRoot; |
| 73476 | 73587 | Pgno pgnoRoot; |
| 73477 | 73588 | int rc; |
| 73478 | 73589 | int ptfFlags; /* Page-type flage for the root page of new table */ |
| @@ -73501,21 +73612,23 @@ | ||
| 73501 | 73612 | /* Read the value of meta[3] from the database to determine where the |
| 73502 | 73613 | ** root page of the new table should go. meta[3] is the largest root-page |
| 73503 | 73614 | ** created so far, so the new root-page is (meta[3]+1). |
| 73504 | 73615 | */ |
| 73505 | 73616 | sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); |
| 73617 | + if( pgnoRoot>btreePagecount(pBt) ){ | |
| 73618 | + return SQLITE_CORRUPT_BKPT; | |
| 73619 | + } | |
| 73506 | 73620 | pgnoRoot++; |
| 73507 | 73621 | |
| 73508 | 73622 | /* The new root-page may not be allocated on a pointer-map page, or the |
| 73509 | 73623 | ** PENDING_BYTE page. |
| 73510 | 73624 | */ |
| 73511 | 73625 | while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || |
| 73512 | 73626 | pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ |
| 73513 | 73627 | pgnoRoot++; |
| 73514 | 73628 | } |
| 73515 | - assert( pgnoRoot>=3 || CORRUPT_DB ); | |
| 73516 | - testcase( pgnoRoot<3 ); | |
| 73629 | + assert( pgnoRoot>=3 ); | |
| 73517 | 73630 | |
| 73518 | 73631 | /* Allocate a page. The page that currently resides at pgnoRoot will |
| 73519 | 73632 | ** be moved to the allocated page (unless the allocated page happens |
| 73520 | 73633 | ** to reside at pgnoRoot). |
| 73521 | 73634 | */ |
| @@ -73608,14 +73721,14 @@ | ||
| 73608 | 73721 | ptfFlags = PTF_ZERODATA | PTF_LEAF; |
| 73609 | 73722 | } |
| 73610 | 73723 | zeroPage(pRoot, ptfFlags); |
| 73611 | 73724 | sqlite3PagerUnref(pRoot->pDbPage); |
| 73612 | 73725 | assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); |
| 73613 | - *piTable = (int)pgnoRoot; | |
| 73726 | + *piTable = pgnoRoot; | |
| 73614 | 73727 | return SQLITE_OK; |
| 73615 | 73728 | } |
| 73616 | -SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ | |
| 73729 | +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){ | |
| 73617 | 73730 | int rc; |
| 73618 | 73731 | sqlite3BtreeEnter(p); |
| 73619 | 73732 | rc = btreeCreateTable(p, piTable, flags); |
| 73620 | 73733 | sqlite3BtreeLeave(p); |
| 73621 | 73734 | return rc; |
| @@ -74095,11 +74208,11 @@ | ||
| 74095 | 74208 | ** Verify that the number of pages on the list is N. |
| 74096 | 74209 | */ |
| 74097 | 74210 | static void checkList( |
| 74098 | 74211 | IntegrityCk *pCheck, /* Integrity checking context */ |
| 74099 | 74212 | int isFreeList, /* True for a freelist. False for overflow page list */ |
| 74100 | - int iPage, /* Page number for first page in the list */ | |
| 74213 | + Pgno iPage, /* Page number for first page in the list */ | |
| 74101 | 74214 | u32 N /* Expected number of pages in the list */ |
| 74102 | 74215 | ){ |
| 74103 | 74216 | int i; |
| 74104 | 74217 | u32 expected = N; |
| 74105 | 74218 | int nErrAtStart = pCheck->nErr; |
| @@ -74227,11 +74340,11 @@ | ||
| 74227 | 74340 | ** 4. Recursively call checkTreePage on all children. |
| 74228 | 74341 | ** 5. Verify that the depth of all children is the same. |
| 74229 | 74342 | */ |
| 74230 | 74343 | static int checkTreePage( |
| 74231 | 74344 | IntegrityCk *pCheck, /* Context for the sanity check */ |
| 74232 | - int iPage, /* Page number of the page to check */ | |
| 74345 | + Pgno iPage, /* Page number of the page to check */ | |
| 74233 | 74346 | i64 *piMinKey, /* Write minimum integer primary key here */ |
| 74234 | 74347 | i64 maxKey /* Error if integer primary key greater than this */ |
| 74235 | 74348 | ){ |
| 74236 | 74349 | MemPage *pPage = 0; /* The page being analyzed */ |
| 74237 | 74350 | int i; /* Loop counter */ |
| @@ -74263,13 +74376,13 @@ | ||
| 74263 | 74376 | */ |
| 74264 | 74377 | pBt = pCheck->pBt; |
| 74265 | 74378 | usableSize = pBt->usableSize; |
| 74266 | 74379 | if( iPage==0 ) return 0; |
| 74267 | 74380 | if( checkRef(pCheck, iPage) ) return 0; |
| 74268 | - pCheck->zPfx = "Page %d: "; | |
| 74381 | + pCheck->zPfx = "Page %u: "; | |
| 74269 | 74382 | pCheck->v1 = iPage; |
| 74270 | - if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ | |
| 74383 | + if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ | |
| 74271 | 74384 | checkAppendMsg(pCheck, |
| 74272 | 74385 | "unable to get the page. error code=%d", rc); |
| 74273 | 74386 | goto end_of_check; |
| 74274 | 74387 | } |
| 74275 | 74388 | |
| @@ -74290,11 +74403,11 @@ | ||
| 74290 | 74403 | } |
| 74291 | 74404 | data = pPage->aData; |
| 74292 | 74405 | hdr = pPage->hdrOffset; |
| 74293 | 74406 | |
| 74294 | 74407 | /* Set up for cell analysis */ |
| 74295 | - pCheck->zPfx = "On tree page %d cell %d: "; | |
| 74408 | + pCheck->zPfx = "On tree page %u cell %d: "; | |
| 74296 | 74409 | contentOffset = get2byteNotZero(&data[hdr+5]); |
| 74297 | 74410 | assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ |
| 74298 | 74411 | |
| 74299 | 74412 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 74300 | 74413 | ** number of cells on the page. */ |
| @@ -74310,11 +74423,11 @@ | ||
| 74310 | 74423 | if( !pPage->leaf ){ |
| 74311 | 74424 | /* Analyze the right-child page of internal pages */ |
| 74312 | 74425 | pgno = get4byte(&data[hdr+8]); |
| 74313 | 74426 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74314 | 74427 | if( pBt->autoVacuum ){ |
| 74315 | - pCheck->zPfx = "On page %d at right child: "; | |
| 74428 | + pCheck->zPfx = "On page %u at right child: "; | |
| 74316 | 74429 | checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); |
| 74317 | 74430 | } |
| 74318 | 74431 | #endif |
| 74319 | 74432 | depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); |
| 74320 | 74433 | keyCanBeEqual = 0; |
| @@ -74451,11 +74564,11 @@ | ||
| 74451 | 74564 | nFrag = 0; |
| 74452 | 74565 | prev = contentOffset - 1; /* Implied first min-heap entry */ |
| 74453 | 74566 | while( btreeHeapPull(heap,&x) ){ |
| 74454 | 74567 | if( (prev&0xffff)>=(x>>16) ){ |
| 74455 | 74568 | checkAppendMsg(pCheck, |
| 74456 | - "Multiple uses for byte %u of page %d", x>>16, iPage); | |
| 74569 | + "Multiple uses for byte %u of page %u", x>>16, iPage); | |
| 74457 | 74570 | break; |
| 74458 | 74571 | }else{ |
| 74459 | 74572 | nFrag += (x>>16) - (prev&0xffff) - 1; |
| 74460 | 74573 | prev = x; |
| 74461 | 74574 | } |
| @@ -74466,11 +74579,11 @@ | ||
| 74466 | 74579 | ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the |
| 74467 | 74580 | ** number of fragmented free bytes within the cell content area. |
| 74468 | 74581 | */ |
| 74469 | 74582 | if( heap[0]==0 && nFrag!=data[hdr+7] ){ |
| 74470 | 74583 | checkAppendMsg(pCheck, |
| 74471 | - "Fragmentation of %d bytes reported as %d on page %d", | |
| 74584 | + "Fragmentation of %d bytes reported as %d on page %u", | |
| 74472 | 74585 | nFrag, data[hdr+7], iPage); |
| 74473 | 74586 | } |
| 74474 | 74587 | } |
| 74475 | 74588 | |
| 74476 | 74589 | end_of_check: |
| @@ -74494,25 +74607,44 @@ | ||
| 74494 | 74607 | ** |
| 74495 | 74608 | ** Write the number of error seen in *pnErr. Except for some memory |
| 74496 | 74609 | ** allocation errors, an error message held in memory obtained from |
| 74497 | 74610 | ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is |
| 74498 | 74611 | ** returned. If a memory allocation error occurs, NULL is returned. |
| 74612 | +** | |
| 74613 | +** If the first entry in aRoot[] is 0, that indicates that the list of | |
| 74614 | +** root pages is incomplete. This is a "partial integrity-check". This | |
| 74615 | +** happens when performing an integrity check on a single table. The | |
| 74616 | +** zero is skipped, of course. But in addition, the freelist checks | |
| 74617 | +** and the checks to make sure every page is referenced are also skipped, | |
| 74618 | +** since obviously it is not possible to know which pages are covered by | |
| 74619 | +** the unverified btrees. Except, if aRoot[1] is 1, then the freelist | |
| 74620 | +** checks are still performed. | |
| 74499 | 74621 | */ |
| 74500 | 74622 | SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( |
| 74501 | 74623 | sqlite3 *db, /* Database connection that is running the check */ |
| 74502 | 74624 | Btree *p, /* The btree to be checked */ |
| 74503 | - int *aRoot, /* An array of root pages numbers for individual trees */ | |
| 74625 | + Pgno *aRoot, /* An array of root pages numbers for individual trees */ | |
| 74504 | 74626 | int nRoot, /* Number of entries in aRoot[] */ |
| 74505 | 74627 | int mxErr, /* Stop reporting errors after this many */ |
| 74506 | 74628 | int *pnErr /* Write number of errors seen to this variable */ |
| 74507 | 74629 | ){ |
| 74508 | 74630 | Pgno i; |
| 74509 | 74631 | IntegrityCk sCheck; |
| 74510 | 74632 | BtShared *pBt = p->pBt; |
| 74511 | 74633 | u64 savedDbFlags = pBt->db->flags; |
| 74512 | 74634 | char zErr[100]; |
| 74635 | + int bPartial = 0; /* True if not checking all btrees */ | |
| 74636 | + int bCkFreelist = 1; /* True to scan the freelist */ | |
| 74513 | 74637 | VVA_ONLY( int nRef ); |
| 74638 | + assert( nRoot>0 ); | |
| 74639 | + | |
| 74640 | + /* aRoot[0]==0 means this is a partial check */ | |
| 74641 | + if( aRoot[0]==0 ){ | |
| 74642 | + assert( nRoot>1 ); | |
| 74643 | + bPartial = 1; | |
| 74644 | + if( aRoot[1]!=1 ) bCkFreelist = 0; | |
| 74645 | + } | |
| 74514 | 74646 | |
| 74515 | 74647 | sqlite3BtreeEnter(p); |
| 74516 | 74648 | assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); |
| 74517 | 74649 | VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); |
| 74518 | 74650 | assert( nRef>=0 ); |
| @@ -74548,69 +74680,75 @@ | ||
| 74548 | 74680 | i = PENDING_BYTE_PAGE(pBt); |
| 74549 | 74681 | if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); |
| 74550 | 74682 | |
| 74551 | 74683 | /* Check the integrity of the freelist |
| 74552 | 74684 | */ |
| 74553 | - sCheck.zPfx = "Main freelist: "; | |
| 74554 | - checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), | |
| 74555 | - get4byte(&pBt->pPage1->aData[36])); | |
| 74556 | - sCheck.zPfx = 0; | |
| 74685 | + if( bCkFreelist ){ | |
| 74686 | + sCheck.zPfx = "Main freelist: "; | |
| 74687 | + checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), | |
| 74688 | + get4byte(&pBt->pPage1->aData[36])); | |
| 74689 | + sCheck.zPfx = 0; | |
| 74690 | + } | |
| 74557 | 74691 | |
| 74558 | 74692 | /* Check all the tables. |
| 74559 | 74693 | */ |
| 74560 | 74694 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74561 | - if( pBt->autoVacuum ){ | |
| 74562 | - int mx = 0; | |
| 74563 | - int mxInHdr; | |
| 74564 | - for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i]; | |
| 74565 | - mxInHdr = get4byte(&pBt->pPage1->aData[52]); | |
| 74566 | - if( mx!=mxInHdr ){ | |
| 74567 | - checkAppendMsg(&sCheck, | |
| 74568 | - "max rootpage (%d) disagrees with header (%d)", | |
| 74569 | - mx, mxInHdr | |
| 74570 | - ); | |
| 74571 | - } | |
| 74572 | - }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ | |
| 74573 | - checkAppendMsg(&sCheck, | |
| 74574 | - "incremental_vacuum enabled with a max rootpage of zero" | |
| 74575 | - ); | |
| 74695 | + if( !bPartial ){ | |
| 74696 | + if( pBt->autoVacuum ){ | |
| 74697 | + Pgno mx = 0; | |
| 74698 | + Pgno mxInHdr; | |
| 74699 | + for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i]; | |
| 74700 | + mxInHdr = get4byte(&pBt->pPage1->aData[52]); | |
| 74701 | + if( mx!=mxInHdr ){ | |
| 74702 | + checkAppendMsg(&sCheck, | |
| 74703 | + "max rootpage (%d) disagrees with header (%d)", | |
| 74704 | + mx, mxInHdr | |
| 74705 | + ); | |
| 74706 | + } | |
| 74707 | + }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ | |
| 74708 | + checkAppendMsg(&sCheck, | |
| 74709 | + "incremental_vacuum enabled with a max rootpage of zero" | |
| 74710 | + ); | |
| 74711 | + } | |
| 74576 | 74712 | } |
| 74577 | 74713 | #endif |
| 74578 | 74714 | testcase( pBt->db->flags & SQLITE_CellSizeCk ); |
| 74579 | 74715 | pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; |
| 74580 | 74716 | for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ |
| 74581 | 74717 | i64 notUsed; |
| 74582 | 74718 | if( aRoot[i]==0 ) continue; |
| 74583 | 74719 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74584 | - if( pBt->autoVacuum && aRoot[i]>1 ){ | |
| 74720 | + if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ | |
| 74585 | 74721 | checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); |
| 74586 | 74722 | } |
| 74587 | 74723 | #endif |
| 74588 | 74724 | checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); |
| 74589 | 74725 | } |
| 74590 | 74726 | pBt->db->flags = savedDbFlags; |
| 74591 | 74727 | |
| 74592 | 74728 | /* Make sure every page in the file is referenced |
| 74593 | 74729 | */ |
| 74594 | - for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ | |
| 74730 | + if( !bPartial ){ | |
| 74731 | + for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ | |
| 74595 | 74732 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 74596 | - if( getPageReferenced(&sCheck, i)==0 ){ | |
| 74597 | - checkAppendMsg(&sCheck, "Page %d is never used", i); | |
| 74598 | - } | |
| 74599 | -#else | |
| 74600 | - /* If the database supports auto-vacuum, make sure no tables contain | |
| 74601 | - ** references to pointer-map pages. | |
| 74602 | - */ | |
| 74603 | - if( getPageReferenced(&sCheck, i)==0 && | |
| 74604 | - (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ | |
| 74605 | - checkAppendMsg(&sCheck, "Page %d is never used", i); | |
| 74606 | - } | |
| 74607 | - if( getPageReferenced(&sCheck, i)!=0 && | |
| 74608 | - (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ | |
| 74609 | - checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); | |
| 74610 | - } | |
| 74611 | -#endif | |
| 74733 | + if( getPageReferenced(&sCheck, i)==0 ){ | |
| 74734 | + checkAppendMsg(&sCheck, "Page %d is never used", i); | |
| 74735 | + } | |
| 74736 | +#else | |
| 74737 | + /* If the database supports auto-vacuum, make sure no tables contain | |
| 74738 | + ** references to pointer-map pages. | |
| 74739 | + */ | |
| 74740 | + if( getPageReferenced(&sCheck, i)==0 && | |
| 74741 | + (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ | |
| 74742 | + checkAppendMsg(&sCheck, "Page %d is never used", i); | |
| 74743 | + } | |
| 74744 | + if( getPageReferenced(&sCheck, i)!=0 && | |
| 74745 | + (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ | |
| 74746 | + checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); | |
| 74747 | + } | |
| 74748 | +#endif | |
| 74749 | + } | |
| 74612 | 74750 | } |
| 74613 | 74751 | |
| 74614 | 74752 | /* Clean up and report errors. |
| 74615 | 74753 | */ |
| 74616 | 74754 | integrity_ck_cleanup: |
| @@ -75794,20 +75932,29 @@ | ||
| 75794 | 75932 | ** into a buffer. |
| 75795 | 75933 | */ |
| 75796 | 75934 | static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ |
| 75797 | 75935 | StrAccum acc; |
| 75798 | 75936 | assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); |
| 75799 | - sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); | |
| 75937 | + assert( sz>22 ); | |
| 75800 | 75938 | if( p->flags & MEM_Int ){ |
| 75801 | - sqlite3_str_appendf(&acc, "%lld", p->u.i); | |
| 75802 | - }else if( p->flags & MEM_IntReal ){ | |
| 75803 | - sqlite3_str_appendf(&acc, "%!.15g", (double)p->u.i); | |
| 75939 | +#if GCC_VERSION>=7000000 | |
| 75940 | + /* Work-around for GCC bug | |
| 75941 | + ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ | |
| 75942 | + i64 x; | |
| 75943 | + assert( (p->flags&MEM_Int)*2==sizeof(x) ); | |
| 75944 | + memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); | |
| 75945 | + sqlite3Int64ToText(x, zBuf); | |
| 75946 | +#else | |
| 75947 | + sqlite3Int64ToText(p->u.i, zBuf); | |
| 75948 | +#endif | |
| 75804 | 75949 | }else{ |
| 75805 | - sqlite3_str_appendf(&acc, "%!.15g", p->u.r); | |
| 75950 | + sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); | |
| 75951 | + sqlite3_str_appendf(&acc, "%!.15g", | |
| 75952 | + (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); | |
| 75953 | + assert( acc.zText==zBuf && acc.mxAlloc<=0 ); | |
| 75954 | + zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ | |
| 75806 | 75955 | } |
| 75807 | - assert( acc.zText==zBuf && acc.mxAlloc<=0 ); | |
| 75808 | - zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ | |
| 75809 | 75956 | } |
| 75810 | 75957 | |
| 75811 | 75958 | #ifdef SQLITE_DEBUG |
| 75812 | 75959 | /* |
| 75813 | 75960 | ** Validity checks on pMem. pMem holds a string. |
| @@ -78397,11 +78544,11 @@ | ||
| 78397 | 78544 | /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing |
| 78398 | 78545 | ** cases from this switch! */ |
| 78399 | 78546 | switch( pOp->opcode ){ |
| 78400 | 78547 | case OP_Transaction: { |
| 78401 | 78548 | if( pOp->p2!=0 ) p->readOnly = 0; |
| 78402 | - /* fall thru */ | |
| 78549 | + /* no break */ deliberate_fall_through | |
| 78403 | 78550 | } |
| 78404 | 78551 | case OP_AutoCommit: |
| 78405 | 78552 | case OP_Savepoint: { |
| 78406 | 78553 | p->bIsReader = 1; |
| 78407 | 78554 | break; |
| @@ -78444,10 +78591,11 @@ | ||
| 78444 | 78591 | assert( (pOp - p->aOp) >= 3 ); |
| 78445 | 78592 | assert( pOp[-1].opcode==OP_Integer ); |
| 78446 | 78593 | n = pOp[-1].p1; |
| 78447 | 78594 | if( n>nMaxArgs ) nMaxArgs = n; |
| 78448 | 78595 | /* Fall through into the default case */ |
| 78596 | + /* no break */ deliberate_fall_through | |
| 78449 | 78597 | } |
| 78450 | 78598 | #endif |
| 78451 | 78599 | default: { |
| 78452 | 78600 | if( pOp->p2<0 ){ |
| 78453 | 78601 | /* The mkopcodeh.tcl script has so arranged things that the only |
| @@ -79301,16 +79449,16 @@ | ||
| 79301 | 79449 | sqlite3_str_appendf(&x, "vtab:%p", pVtab); |
| 79302 | 79450 | break; |
| 79303 | 79451 | } |
| 79304 | 79452 | #endif |
| 79305 | 79453 | case P4_INTARRAY: { |
| 79306 | - int i; | |
| 79307 | - int *ai = pOp->p4.ai; | |
| 79308 | - int n = ai[0]; /* The first element of an INTARRAY is always the | |
| 79454 | + u32 i; | |
| 79455 | + u32 *ai = pOp->p4.ai; | |
| 79456 | + u32 n = ai[0]; /* The first element of an INTARRAY is always the | |
| 79309 | 79457 | ** count of the number of elements to follow */ |
| 79310 | 79458 | for(i=1; i<=n; i++){ |
| 79311 | - sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]); | |
| 79459 | + sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); | |
| 79312 | 79460 | } |
| 79313 | 79461 | sqlite3_str_append(&x, "]", 1); |
| 79314 | 79462 | break; |
| 79315 | 79463 | } |
| 79316 | 79464 | case P4_SUBPROGRAM: { |
| @@ -81150,15 +81298,15 @@ | ||
| 81150 | 81298 | ** a NULL row. |
| 81151 | 81299 | ** |
| 81152 | 81300 | ** If the cursor is already pointing to the correct row and that row has |
| 81153 | 81301 | ** not been deleted out from under the cursor, then this routine is a no-op. |
| 81154 | 81302 | */ |
| 81155 | -SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ | |
| 81303 | +SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ | |
| 81156 | 81304 | VdbeCursor *p = *pp; |
| 81157 | 81305 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81158 | 81306 | if( p->deferredMoveto ){ |
| 81159 | - int iMap; | |
| 81307 | + u32 iMap; | |
| 81160 | 81308 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81161 | 81309 | *pp = p->pAltCursor; |
| 81162 | 81310 | *piCol = iMap - 1; |
| 81163 | 81311 | return SQLITE_OK; |
| 81164 | 81312 | } |
| @@ -82910,11 +83058,11 @@ | ||
| 82910 | 83058 | if( db->xProfile ){ |
| 82911 | 83059 | db->xProfile(db->pProfileArg, p->zSql, iElapse); |
| 82912 | 83060 | } |
| 82913 | 83061 | #endif |
| 82914 | 83062 | if( db->mTrace & SQLITE_TRACE_PROFILE ){ |
| 82915 | - db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); | |
| 83063 | + db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); | |
| 82916 | 83064 | } |
| 82917 | 83065 | p->startTime = 0; |
| 82918 | 83066 | } |
| 82919 | 83067 | /* |
| 82920 | 83068 | ** The checkProfileCallback(DB,P) macro checks to see if a profile callback |
| @@ -86210,10 +86358,11 @@ | ||
| 86210 | 86358 | #ifdef SQLITE_DEBUG |
| 86211 | 86359 | if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } |
| 86212 | 86360 | #endif |
| 86213 | 86361 | if( (pIn3->flags & MEM_Null)==0 ) break; |
| 86214 | 86362 | /* Fall through into OP_Halt */ |
| 86363 | + /* no break */ deliberate_fall_through | |
| 86215 | 86364 | } |
| 86216 | 86365 | |
| 86217 | 86366 | /* Opcode: Halt P1 P2 * P4 P5 |
| 86218 | 86367 | ** |
| 86219 | 86368 | ** Exit immediately. All open cursors, etc are closed |
| @@ -86380,10 +86529,11 @@ | ||
| 86380 | 86529 | goto too_big; |
| 86381 | 86530 | } |
| 86382 | 86531 | pOp->opcode = OP_String; |
| 86383 | 86532 | assert( rc==SQLITE_OK ); |
| 86384 | 86533 | /* Fall through to the next case, OP_String */ |
| 86534 | + /* no break */ deliberate_fall_through | |
| 86385 | 86535 | } |
| 86386 | 86536 | |
| 86387 | 86537 | /* Opcode: String P1 P2 P3 P4 P5 |
| 86388 | 86538 | ** Synopsis: r[P2]='P4' (len=P1) |
| 86389 | 86539 | ** |
| @@ -86691,11 +86841,11 @@ | ||
| 86691 | 86841 | #endif |
| 86692 | 86842 | } |
| 86693 | 86843 | if( db->mallocFailed ) goto no_mem; |
| 86694 | 86844 | |
| 86695 | 86845 | if( db->mTrace & SQLITE_TRACE_ROW ){ |
| 86696 | - db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); | |
| 86846 | + db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); | |
| 86697 | 86847 | } |
| 86698 | 86848 | |
| 86699 | 86849 | |
| 86700 | 86850 | /* Return SQLITE_ROW |
| 86701 | 86851 | */ |
| @@ -87427,14 +87577,14 @@ | ||
| 87427 | 87577 | int n; |
| 87428 | 87578 | int i; |
| 87429 | 87579 | int p1; |
| 87430 | 87580 | int p2; |
| 87431 | 87581 | const KeyInfo *pKeyInfo; |
| 87432 | - int idx; | |
| 87582 | + u32 idx; | |
| 87433 | 87583 | CollSeq *pColl; /* Collating sequence to use on this term */ |
| 87434 | 87584 | int bRev; /* True for DESCENDING sort order */ |
| 87435 | - int *aPermute; /* The permutation */ | |
| 87585 | + u32 *aPermute; /* The permutation */ | |
| 87436 | 87586 | |
| 87437 | 87587 | if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ |
| 87438 | 87588 | aPermute = 0; |
| 87439 | 87589 | }else{ |
| 87440 | 87590 | assert( pOp>aOp ); |
| @@ -87450,20 +87600,20 @@ | ||
| 87450 | 87600 | p1 = pOp->p1; |
| 87451 | 87601 | p2 = pOp->p2; |
| 87452 | 87602 | #ifdef SQLITE_DEBUG |
| 87453 | 87603 | if( aPermute ){ |
| 87454 | 87604 | int k, mx = 0; |
| 87455 | - for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k]; | |
| 87605 | + for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k]; | |
| 87456 | 87606 | assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87457 | 87607 | assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87458 | 87608 | }else{ |
| 87459 | 87609 | assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87460 | 87610 | assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87461 | 87611 | } |
| 87462 | 87612 | #endif /* SQLITE_DEBUG */ |
| 87463 | 87613 | for(i=0; i<n; i++){ |
| 87464 | - idx = aPermute ? aPermute[i] : i; | |
| 87614 | + idx = aPermute ? aPermute[i] : (u32)i; | |
| 87465 | 87615 | assert( memIsValid(&aMem[p1+idx]) ); |
| 87466 | 87616 | assert( memIsValid(&aMem[p2+idx]) ); |
| 87467 | 87617 | REGISTER_TRACE(p1+idx, &aMem[p1+idx]); |
| 87468 | 87618 | REGISTER_TRACE(p2+idx, &aMem[p2+idx]); |
| 87469 | 87619 | assert( i<pKeyInfo->nKeyField ); |
| @@ -87770,11 +87920,11 @@ | ||
| 87770 | 87920 | ** the result is guaranteed to only be used as the argument of a length() |
| 87771 | 87921 | ** or typeof() function, respectively. The loading of large blobs can be |
| 87772 | 87922 | ** skipped for length() and all content loading can be skipped for typeof(). |
| 87773 | 87923 | */ |
| 87774 | 87924 | case OP_Column: { |
| 87775 | - int p2; /* column number to retrieve */ | |
| 87925 | + u32 p2; /* column number to retrieve */ | |
| 87776 | 87926 | VdbeCursor *pC; /* The VDBE cursor */ |
| 87777 | 87927 | BtCursor *pCrsr; /* The BTree cursor */ |
| 87778 | 87928 | u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ |
| 87779 | 87929 | int len; /* The length of the serialized data for the column */ |
| 87780 | 87930 | int i; /* Loop counter */ |
| @@ -87788,11 +87938,11 @@ | ||
| 87788 | 87938 | Mem *pReg; /* PseudoTable input register */ |
| 87789 | 87939 | |
| 87790 | 87940 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 87791 | 87941 | pC = p->apCsr[pOp->p1]; |
| 87792 | 87942 | assert( pC!=0 ); |
| 87793 | - p2 = pOp->p2; | |
| 87943 | + p2 = (u32)pOp->p2; | |
| 87794 | 87944 | |
| 87795 | 87945 | /* If the cursor cache is stale (meaning it is not currently point at |
| 87796 | 87946 | ** the correct row) then bring it up-to-date by doing the necessary |
| 87797 | 87947 | ** B-Tree seek. */ |
| 87798 | 87948 | rc = sqlite3VdbeCursorMoveto(&pC, &p2); |
| @@ -87800,11 +87950,11 @@ | ||
| 87800 | 87950 | |
| 87801 | 87951 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 87802 | 87952 | pDest = &aMem[pOp->p3]; |
| 87803 | 87953 | memAboutToChange(p, pDest); |
| 87804 | 87954 | assert( pC!=0 ); |
| 87805 | - assert( p2<pC->nField ); | |
| 87955 | + assert( p2<(u32)pC->nField ); | |
| 87806 | 87956 | aOffset = pC->aOffset; |
| 87807 | 87957 | assert( pC->eCurType!=CURTYPE_VTAB ); |
| 87808 | 87958 | assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); |
| 87809 | 87959 | assert( pC->eCurType!=CURTYPE_SORTER ); |
| 87810 | 87960 | |
| @@ -87915,11 +88065,11 @@ | ||
| 87915 | 88065 | zHdr += sqlite3GetVarint32(zHdr, &t); |
| 87916 | 88066 | pC->aType[i] = t; |
| 87917 | 88067 | offset64 += sqlite3VdbeSerialTypeLen(t); |
| 87918 | 88068 | } |
| 87919 | 88069 | aOffset[++i] = (u32)(offset64 & 0xffffffff); |
| 87920 | - }while( i<=p2 && zHdr<zEndHdr ); | |
| 88070 | + }while( (u32)i<=p2 && zHdr<zEndHdr ); | |
| 87921 | 88071 | |
| 87922 | 88072 | /* The record is corrupt if any of the following are true: |
| 87923 | 88073 | ** (1) the bytes of the header extend past the declared header size |
| 87924 | 88074 | ** (2) the entire header was used but not all data was used |
| 87925 | 88075 | ** (3) the end of the data extends beyond the end of the record. |
| @@ -88939,11 +89089,11 @@ | ||
| 88939 | 89089 | ** See also: OP_OpenRead, OP_ReopenIdx |
| 88940 | 89090 | */ |
| 88941 | 89091 | case OP_ReopenIdx: { |
| 88942 | 89092 | int nField; |
| 88943 | 89093 | KeyInfo *pKeyInfo; |
| 88944 | - int p2; | |
| 89094 | + u32 p2; | |
| 88945 | 89095 | int iDb; |
| 88946 | 89096 | int wrFlag; |
| 88947 | 89097 | Btree *pX; |
| 88948 | 89098 | VdbeCursor *pCur; |
| 88949 | 89099 | Db *pDb; |
| @@ -88970,11 +89120,11 @@ | ||
| 88970 | 89120 | goto abort_due_to_error; |
| 88971 | 89121 | } |
| 88972 | 89122 | |
| 88973 | 89123 | nField = 0; |
| 88974 | 89124 | pKeyInfo = 0; |
| 88975 | - p2 = pOp->p2; | |
| 89125 | + p2 = (u32)pOp->p2; | |
| 88976 | 89126 | iDb = pOp->p3; |
| 88977 | 89127 | assert( iDb>=0 && iDb<db->nDb ); |
| 88978 | 89128 | assert( DbMaskTest(p->btreeMask, iDb) ); |
| 88979 | 89129 | pDb = &db->aDb[iDb]; |
| 88980 | 89130 | pX = pDb->pBt; |
| @@ -88989,11 +89139,11 @@ | ||
| 88989 | 89139 | }else{ |
| 88990 | 89140 | wrFlag = 0; |
| 88991 | 89141 | } |
| 88992 | 89142 | if( pOp->p5 & OPFLAG_P2ISREG ){ |
| 88993 | 89143 | assert( p2>0 ); |
| 88994 | - assert( p2<=(p->nMem+1 - p->nCursor) ); | |
| 89144 | + assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); | |
| 88995 | 89145 | assert( pOp->opcode==OP_OpenWrite ); |
| 88996 | 89146 | pIn2 = &aMem[p2]; |
| 88997 | 89147 | assert( memIsValid(pIn2) ); |
| 88998 | 89148 | assert( (pIn2->flags & MEM_Int)!=0 ); |
| 88999 | 89149 | sqlite3VdbeMemIntegerify(pIn2); |
| @@ -89142,11 +89292,11 @@ | ||
| 89142 | 89292 | ** opening it. If a transient table is required, just use the |
| 89143 | 89293 | ** automatically created table with root-page 1 (an BLOB_INTKEY table). |
| 89144 | 89294 | */ |
| 89145 | 89295 | if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ |
| 89146 | 89296 | assert( pOp->p4type==P4_KEYINFO ); |
| 89147 | - rc = sqlite3BtreeCreateTable(pCx->pBtx, (int*)&pCx->pgnoRoot, | |
| 89297 | + rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, | |
| 89148 | 89298 | BTREE_BLOBKEY | pOp->p5); |
| 89149 | 89299 | if( rc==SQLITE_OK ){ |
| 89150 | 89300 | assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); |
| 89151 | 89301 | assert( pKeyInfo->db==db ); |
| 89152 | 89302 | assert( pKeyInfo->enc==ENC(db) ); |
| @@ -89684,10 +89834,11 @@ | ||
| 89684 | 89834 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89685 | 89835 | pC = p->apCsr[pOp->p1]; |
| 89686 | 89836 | assert( pC!=0 ); |
| 89687 | 89837 | if( pC->seekHit ) break; |
| 89688 | 89838 | /* Fall through into OP_NotFound */ |
| 89839 | + /* no break */ deliberate_fall_through | |
| 89689 | 89840 | } |
| 89690 | 89841 | case OP_NoConflict: /* jump, in3 */ |
| 89691 | 89842 | case OP_NotFound: /* jump, in3 */ |
| 89692 | 89843 | case OP_Found: { /* jump, in3 */ |
| 89693 | 89844 | int alreadyExists; |
| @@ -89838,10 +89989,11 @@ | ||
| 89838 | 89989 | if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; |
| 89839 | 89990 | iKey = x.u.i; |
| 89840 | 89991 | goto notExistsWithKey; |
| 89841 | 89992 | } |
| 89842 | 89993 | /* Fall through into OP_NotExists */ |
| 89994 | + /* no break */ deliberate_fall_through | |
| 89843 | 89995 | case OP_NotExists: /* jump, in3 */ |
| 89844 | 89996 | pIn3 = &aMem[pOp->p3]; |
| 89845 | 89997 | assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); |
| 89846 | 89998 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89847 | 89999 | iKey = pIn3->u.i; |
| @@ -90406,14 +90558,10 @@ | ||
| 90406 | 90558 | ** generator) then the fix would be to insert a call to |
| 90407 | 90559 | ** sqlite3VdbeCursorMoveto(). |
| 90408 | 90560 | */ |
| 90409 | 90561 | assert( pC->deferredMoveto==0 ); |
| 90410 | 90562 | assert( sqlite3BtreeCursorIsValid(pCrsr) ); |
| 90411 | -#if 0 /* Not required due to the previous to assert() statements */ | |
| 90412 | - rc = sqlite3VdbeCursorMoveto(pC); | |
| 90413 | - if( rc!=SQLITE_OK ) goto abort_due_to_error; | |
| 90414 | -#endif | |
| 90415 | 90563 | |
| 90416 | 90564 | n = sqlite3BtreePayloadSize(pCrsr); |
| 90417 | 90565 | if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ |
| 90418 | 90566 | goto too_big; |
| 90419 | 90567 | } |
| @@ -90613,10 +90761,11 @@ | ||
| 90613 | 90761 | sqlite3_sort_count++; |
| 90614 | 90762 | sqlite3_search_count--; |
| 90615 | 90763 | #endif |
| 90616 | 90764 | p->aCounter[SQLITE_STMTSTATUS_SORT]++; |
| 90617 | 90765 | /* Fall through into OP_Rewind */ |
| 90766 | + /* no break */ deliberate_fall_through | |
| 90618 | 90767 | } |
| 90619 | 90768 | /* Opcode: Rewind P1 P2 * * * |
| 90620 | 90769 | ** |
| 90621 | 90770 | ** The next use of the Rowid or Column or Next instruction for P1 |
| 90622 | 90771 | ** will refer to the first entry in the database table or index. |
| @@ -91179,11 +91328,11 @@ | ||
| 91179 | 91328 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91180 | 91329 | nChange = 0; |
| 91181 | 91330 | assert( p->readOnly==0 ); |
| 91182 | 91331 | assert( DbMaskTest(p->btreeMask, pOp->p2) ); |
| 91183 | 91332 | rc = sqlite3BtreeClearTable( |
| 91184 | - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) | |
| 91333 | + db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) | |
| 91185 | 91334 | ); |
| 91186 | 91335 | if( pOp->p3 ){ |
| 91187 | 91336 | p->nChange += nChange; |
| 91188 | 91337 | if( pOp->p3>0 ){ |
| 91189 | 91338 | assert( memIsValid(&aMem[pOp->p3]) ); |
| @@ -91228,11 +91377,11 @@ | ||
| 91228 | 91377 | ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table |
| 91229 | 91378 | ** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. |
| 91230 | 91379 | ** The root page number of the new b-tree is stored in register P2. |
| 91231 | 91380 | */ |
| 91232 | 91381 | case OP_CreateBtree: { /* out2 */ |
| 91233 | - int pgno; | |
| 91382 | + Pgno pgno; | |
| 91234 | 91383 | Db *pDb; |
| 91235 | 91384 | |
| 91236 | 91385 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91237 | 91386 | pOut = out2Prerelease(p, pOp); |
| 91238 | 91387 | pgno = 0; |
| @@ -91303,10 +91452,11 @@ | ||
| 91303 | 91452 | zSchema = DFLT_SCHEMA_TABLE; |
| 91304 | 91453 | initData.db = db; |
| 91305 | 91454 | initData.iDb = iDb; |
| 91306 | 91455 | initData.pzErrMsg = &p->zErrMsg; |
| 91307 | 91456 | initData.mInitFlags = 0; |
| 91457 | + initData.mxPage = sqlite3BtreeLastPage(db->aDb[iDb].pBt); | |
| 91308 | 91458 | zSql = sqlite3MPrintf(db, |
| 91309 | 91459 | "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", |
| 91310 | 91460 | db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); |
| 91311 | 91461 | if( zSql==0 ){ |
| 91312 | 91462 | rc = SQLITE_NOMEM_BKPT; |
| @@ -91416,20 +91566,20 @@ | ||
| 91416 | 91566 | ** |
| 91417 | 91567 | ** This opcode is used to implement the integrity_check pragma. |
| 91418 | 91568 | */ |
| 91419 | 91569 | case OP_IntegrityCk: { |
| 91420 | 91570 | int nRoot; /* Number of tables to check. (Number of root pages.) */ |
| 91421 | - int *aRoot; /* Array of rootpage numbers for tables to be checked */ | |
| 91571 | + Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ | |
| 91422 | 91572 | int nErr; /* Number of errors reported */ |
| 91423 | 91573 | char *z; /* Text of the error report */ |
| 91424 | 91574 | Mem *pnErr; /* Register keeping track of errors remaining */ |
| 91425 | 91575 | |
| 91426 | 91576 | assert( p->bIsReader ); |
| 91427 | 91577 | nRoot = pOp->p2; |
| 91428 | 91578 | aRoot = pOp->p4.ai; |
| 91429 | 91579 | assert( nRoot>0 ); |
| 91430 | - assert( aRoot[0]==nRoot ); | |
| 91580 | + assert( aRoot[0]==(Pgno)nRoot ); | |
| 91431 | 91581 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 91432 | 91582 | pnErr = &aMem[pOp->p3]; |
| 91433 | 91583 | assert( (pnErr->flags & MEM_Int)!=0 ); |
| 91434 | 91584 | assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); |
| 91435 | 91585 | pIn1 = &aMem[pOp->p1]; |
| @@ -91965,10 +92115,11 @@ | ||
| 91965 | 92115 | /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ |
| 91966 | 92116 | assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); |
| 91967 | 92117 | |
| 91968 | 92118 | pOp->opcode = OP_AggStep1; |
| 91969 | 92119 | /* Fall through into OP_AggStep */ |
| 92120 | + /* no break */ deliberate_fall_through | |
| 91970 | 92121 | } |
| 91971 | 92122 | case OP_AggStep1: { |
| 91972 | 92123 | int i; |
| 91973 | 92124 | sqlite3_context *pCtx; |
| 91974 | 92125 | Mem *pMem; |
| @@ -92954,22 +93105,21 @@ | ||
| 92954 | 93105 | && !p->doingRerun |
| 92955 | 93106 | && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 |
| 92956 | 93107 | ){ |
| 92957 | 93108 | #ifndef SQLITE_OMIT_DEPRECATED |
| 92958 | 93109 | if( db->mTrace & SQLITE_TRACE_LEGACY ){ |
| 92959 | - void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace; | |
| 92960 | 93110 | char *z = sqlite3VdbeExpandSql(p, zTrace); |
| 92961 | - x(db->pTraceArg, z); | |
| 93111 | + db->trace.xLegacy(db->pTraceArg, z); | |
| 92962 | 93112 | sqlite3_free(z); |
| 92963 | 93113 | }else |
| 92964 | 93114 | #endif |
| 92965 | 93115 | if( db->nVdbeExec>1 ){ |
| 92966 | 93116 | char *z = sqlite3MPrintf(db, "-- %s", zTrace); |
| 92967 | - (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z); | |
| 93117 | + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); | |
| 92968 | 93118 | sqlite3DbFree(db, z); |
| 92969 | 93119 | }else{ |
| 92970 | - (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); | |
| 93120 | + (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); | |
| 92971 | 93121 | } |
| 92972 | 93122 | } |
| 92973 | 93123 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 92974 | 93124 | zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); |
| 92975 | 93125 | if( zTrace ){ |
| @@ -96701,11 +96851,11 @@ | ||
| 96701 | 96851 | }else{ |
| 96702 | 96852 | if( i<=2 && pCur->zType==0 ){ |
| 96703 | 96853 | Schema *pSchema; |
| 96704 | 96854 | HashElem *k; |
| 96705 | 96855 | int iDb = pOp->p3; |
| 96706 | - int iRoot = pOp->p2; | |
| 96856 | + Pgno iRoot = (Pgno)pOp->p2; | |
| 96707 | 96857 | sqlite3 *db = pVTab->db; |
| 96708 | 96858 | pSchema = db->aDb[iDb].pSchema; |
| 96709 | 96859 | pCur->zSchema = db->aDb[iDb].zDbSName; |
| 96710 | 96860 | for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| 96711 | 96861 | Table *pTab = (Table*)sqliteHashData(k); |
| @@ -97288,11 +97438,11 @@ | ||
| 97288 | 97438 | }else{ |
| 97289 | 97439 | p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); |
| 97290 | 97440 | assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); |
| 97291 | 97441 | } |
| 97292 | 97442 | |
| 97293 | - p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods; | |
| 97443 | + pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods; | |
| 97294 | 97444 | p->nSpill = nSpill; |
| 97295 | 97445 | p->flags = flags; |
| 97296 | 97446 | p->zJournal = zName; |
| 97297 | 97447 | p->pVfs = pVfs; |
| 97298 | 97448 | return SQLITE_OK; |
| @@ -97314,11 +97464,11 @@ | ||
| 97314 | 97464 | ** file has not yet been created, create it now. |
| 97315 | 97465 | */ |
| 97316 | 97466 | SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){ |
| 97317 | 97467 | int rc = SQLITE_OK; |
| 97318 | 97468 | MemJournal *p = (MemJournal*)pJfd; |
| 97319 | - if( p->pMethod==&MemJournalMethods && ( | |
| 97469 | + if( pJfd->pMethods==&MemJournalMethods && ( | |
| 97320 | 97470 | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
| 97321 | 97471 | p->nSpill>0 |
| 97322 | 97472 | #else |
| 97323 | 97473 | /* While this appears to not be possible without ATOMIC_WRITE, the |
| 97324 | 97474 | ** paths are complex, so it seems prudent to leave the test in as |
| @@ -98687,11 +98837,11 @@ | ||
| 98687 | 98837 | pExpr->op2 = pExpr->op; |
| 98688 | 98838 | pExpr->op = TK_TRUTH; |
| 98689 | 98839 | return WRC_Continue; |
| 98690 | 98840 | } |
| 98691 | 98841 | } |
| 98692 | - /* Fall thru */ | |
| 98842 | + /* no break */ deliberate_fall_through | |
| 98693 | 98843 | } |
| 98694 | 98844 | case TK_BETWEEN: |
| 98695 | 98845 | case TK_EQ: |
| 98696 | 98846 | case TK_NE: |
| 98697 | 98847 | case TK_LT: |
| @@ -101591,11 +101741,11 @@ | ||
| 101591 | 101741 | /* Convert "true" or "false" in a DEFAULT clause into the |
| 101592 | 101742 | ** appropriate TK_TRUEFALSE operator */ |
| 101593 | 101743 | if( sqlite3ExprIdToTrueFalse(pExpr) ){ |
| 101594 | 101744 | return WRC_Prune; |
| 101595 | 101745 | } |
| 101596 | - /* Fall thru */ | |
| 101746 | + /* no break */ deliberate_fall_through | |
| 101597 | 101747 | case TK_COLUMN: |
| 101598 | 101748 | case TK_AGG_FUNCTION: |
| 101599 | 101749 | case TK_AGG_COLUMN: |
| 101600 | 101750 | testcase( pExpr->op==TK_ID ); |
| 101601 | 101751 | testcase( pExpr->op==TK_COLUMN ); |
| @@ -101605,11 +101755,11 @@ | ||
| 101605 | 101755 | return WRC_Continue; |
| 101606 | 101756 | } |
| 101607 | 101757 | if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ |
| 101608 | 101758 | return WRC_Continue; |
| 101609 | 101759 | } |
| 101610 | - /* Fall through */ | |
| 101760 | + /* no break */ deliberate_fall_through | |
| 101611 | 101761 | case TK_IF_NULL_ROW: |
| 101612 | 101762 | case TK_REGISTER: |
| 101613 | 101763 | case TK_DOT: |
| 101614 | 101764 | testcase( pExpr->op==TK_REGISTER ); |
| 101615 | 101765 | testcase( pExpr->op==TK_IF_NULL_ROW ); |
| @@ -101626,11 +101776,11 @@ | ||
| 101626 | 101776 | /* A bound parameter in a CREATE statement that originates from |
| 101627 | 101777 | ** sqlite3_prepare() causes an error */ |
| 101628 | 101778 | pWalker->eCode = 0; |
| 101629 | 101779 | return WRC_Abort; |
| 101630 | 101780 | } |
| 101631 | - /* Fall through */ | |
| 101781 | + /* no break */ deliberate_fall_through | |
| 101632 | 101782 | default: |
| 101633 | 101783 | testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ |
| 101634 | 101784 | testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ |
| 101635 | 101785 | return WRC_Continue; |
| 101636 | 101786 | } |
| @@ -103398,10 +103548,11 @@ | ||
| 103398 | 103548 | } |
| 103399 | 103549 | } |
| 103400 | 103550 | return target; |
| 103401 | 103551 | } |
| 103402 | 103552 | /* Otherwise, fall thru into the TK_COLUMN case */ |
| 103553 | + /* no break */ deliberate_fall_through | |
| 103403 | 103554 | } |
| 103404 | 103555 | case TK_COLUMN: { |
| 103405 | 103556 | int iTab = pExpr->iTable; |
| 103406 | 103557 | int iReg; |
| 103407 | 103558 | if( ExprHasProperty(pExpr, EP_FixedCol) ){ |
| @@ -104463,11 +104614,11 @@ | ||
| 104463 | 104614 | case TK_ISNOT: |
| 104464 | 104615 | testcase( op==TK_IS ); |
| 104465 | 104616 | testcase( op==TK_ISNOT ); |
| 104466 | 104617 | op = (op==TK_IS) ? TK_EQ : TK_NE; |
| 104467 | 104618 | jumpIfNull = SQLITE_NULLEQ; |
| 104468 | - /* Fall thru */ | |
| 104619 | + /* no break */ deliberate_fall_through | |
| 104469 | 104620 | case TK_LT: |
| 104470 | 104621 | case TK_LE: |
| 104471 | 104622 | case TK_GT: |
| 104472 | 104623 | case TK_GE: |
| 104473 | 104624 | case TK_NE: |
| @@ -104639,11 +104790,11 @@ | ||
| 104639 | 104790 | case TK_ISNOT: |
| 104640 | 104791 | testcase( pExpr->op==TK_IS ); |
| 104641 | 104792 | testcase( pExpr->op==TK_ISNOT ); |
| 104642 | 104793 | op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; |
| 104643 | 104794 | jumpIfNull = SQLITE_NULLEQ; |
| 104644 | - /* Fall thru */ | |
| 104795 | + /* no break */ deliberate_fall_through | |
| 104645 | 104796 | case TK_LT: |
| 104646 | 104797 | case TK_LE: |
| 104647 | 104798 | case TK_GT: |
| 104648 | 104799 | case TK_GE: |
| 104649 | 104800 | case TK_NE: |
| @@ -104951,17 +105102,17 @@ | ||
| 104951 | 105102 | case TK_BITOR: |
| 104952 | 105103 | case TK_LSHIFT: |
| 104953 | 105104 | case TK_RSHIFT: |
| 104954 | 105105 | case TK_CONCAT: |
| 104955 | 105106 | seenNot = 1; |
| 104956 | - /* Fall thru */ | |
| 105107 | + /* no break */ deliberate_fall_through | |
| 104957 | 105108 | case TK_STAR: |
| 104958 | 105109 | case TK_REM: |
| 104959 | 105110 | case TK_BITAND: |
| 104960 | 105111 | case TK_SLASH: { |
| 104961 | 105112 | if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1; |
| 104962 | - /* Fall thru into the next case */ | |
| 105113 | + /* no break */ deliberate_fall_through | |
| 104963 | 105114 | } |
| 104964 | 105115 | case TK_SPAN: |
| 104965 | 105116 | case TK_COLLATE: |
| 104966 | 105117 | case TK_UPLUS: |
| 104967 | 105118 | case TK_UMINUS: { |
| @@ -105106,10 +105257,11 @@ | ||
| 105106 | 105257 | || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) |
| 105107 | 105258 | && IsVirtual(pRight->y.pTab)) |
| 105108 | 105259 | ){ |
| 105109 | 105260 | return WRC_Prune; |
| 105110 | 105261 | } |
| 105262 | + /* no break */ deliberate_fall_through | |
| 105111 | 105263 | } |
| 105112 | 105264 | default: |
| 105113 | 105265 | return WRC_Continue; |
| 105114 | 105266 | } |
| 105115 | 105267 | } |
| @@ -106839,11 +106991,11 @@ | ||
| 106839 | 106991 | } |
| 106840 | 106992 | if( rc==SQLITE_OK && pStep->zTarget ){ |
| 106841 | 106993 | SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); |
| 106842 | 106994 | if( pSrc ){ |
| 106843 | 106995 | int i; |
| 106844 | - for(i=0; i<pSrc->nSrc; i++){ | |
| 106996 | + for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){ | |
| 106845 | 106997 | struct SrcList_item *p = &pSrc->a[i]; |
| 106846 | 106998 | p->pTab = sqlite3LocateTableItem(pParse, 0, p); |
| 106847 | 106999 | p->iCursor = pParse->nTab++; |
| 106848 | 107000 | if( p->pTab==0 ){ |
| 106849 | 107001 | rc = SQLITE_ERROR; |
| @@ -107581,11 +107733,11 @@ | ||
| 107581 | 107733 | }; |
| 107582 | 107734 | int i; |
| 107583 | 107735 | sqlite3 *db = pParse->db; |
| 107584 | 107736 | Db *pDb; |
| 107585 | 107737 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 107586 | - int aRoot[ArraySize(aTable)]; | |
| 107738 | + u32 aRoot[ArraySize(aTable)]; | |
| 107587 | 107739 | u8 aCreateTbl[ArraySize(aTable)]; |
| 107588 | 107740 | #ifdef SQLITE_ENABLE_STAT4 |
| 107589 | 107741 | const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; |
| 107590 | 107742 | #else |
| 107591 | 107743 | const int nToOpen = 1; |
| @@ -107610,11 +107762,11 @@ | ||
| 107610 | 107762 | ** of the new table in register pParse->regRoot. This is important |
| 107611 | 107763 | ** because the OpenWrite opcode below will be needing it. */ |
| 107612 | 107764 | sqlite3NestedParse(pParse, |
| 107613 | 107765 | "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols |
| 107614 | 107766 | ); |
| 107615 | - aRoot[i] = pParse->regRoot; | |
| 107767 | + aRoot[i] = (u32)pParse->regRoot; | |
| 107616 | 107768 | aCreateTbl[i] = OPFLAG_P2ISREG; |
| 107617 | 107769 | } |
| 107618 | 107770 | }else{ |
| 107619 | 107771 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 107620 | 107772 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| @@ -107630,19 +107782,19 @@ | ||
| 107630 | 107782 | }else if( db->xPreUpdateCallback ){ |
| 107631 | 107783 | sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab); |
| 107632 | 107784 | #endif |
| 107633 | 107785 | }else{ |
| 107634 | 107786 | /* The sqlite_stat[134] table already exists. Delete all rows. */ |
| 107635 | - sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); | |
| 107787 | + sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb); | |
| 107636 | 107788 | } |
| 107637 | 107789 | } |
| 107638 | 107790 | } |
| 107639 | 107791 | |
| 107640 | 107792 | /* Open the sqlite_stat[134] tables for writing. */ |
| 107641 | 107793 | for(i=0; i<nToOpen; i++){ |
| 107642 | 107794 | assert( i<ArraySize(aTable) ); |
| 107643 | - sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3); | |
| 107795 | + sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3); | |
| 107644 | 107796 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 107645 | 107797 | VdbeComment((v, aTable[i].zName)); |
| 107646 | 107798 | } |
| 107647 | 107799 | } |
| 107648 | 107800 | |
| @@ -110271,11 +110423,11 @@ | ||
| 110271 | 110423 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 110272 | 110424 | ** codeTableLocks() functions. |
| 110273 | 110425 | */ |
| 110274 | 110426 | struct TableLock { |
| 110275 | 110427 | int iDb; /* The database containing the table to be locked */ |
| 110276 | - int iTab; /* The root page of the table to be locked */ | |
| 110428 | + Pgno iTab; /* The root page of the table to be locked */ | |
| 110277 | 110429 | u8 isWriteLock; /* True for write lock. False for a read lock */ |
| 110278 | 110430 | const char *zLockName; /* Name of the table */ |
| 110279 | 110431 | }; |
| 110280 | 110432 | |
| 110281 | 110433 | /* |
| @@ -110289,11 +110441,11 @@ | ||
| 110289 | 110441 | ** codeTableLocks() which occurs during sqlite3FinishCoding(). |
| 110290 | 110442 | */ |
| 110291 | 110443 | SQLITE_PRIVATE void sqlite3TableLock( |
| 110292 | 110444 | Parse *pParse, /* Parsing context */ |
| 110293 | 110445 | int iDb, /* Index of the database containing the table to lock */ |
| 110294 | - int iTab, /* Root page number of the table to be locked */ | |
| 110446 | + Pgno iTab, /* Root page number of the table to be locked */ | |
| 110295 | 110447 | u8 isWriteLock, /* True for a write lock */ |
| 110296 | 110448 | const char *zName /* Name of the table to be locked */ |
| 110297 | 110449 | ){ |
| 110298 | 110450 | Parse *pToplevel = sqlite3ParseToplevel(pParse); |
| 110299 | 110451 | int i; |
| @@ -111128,23 +111280,24 @@ | ||
| 111128 | 111280 | const char *zName, /* Name of the object to check */ |
| 111129 | 111281 | const char *zType, /* Type of this object */ |
| 111130 | 111282 | const char *zTblName /* Parent table name for triggers and indexes */ |
| 111131 | 111283 | ){ |
| 111132 | 111284 | sqlite3 *db = pParse->db; |
| 111133 | - if( sqlite3WritableSchema(db) || db->init.imposterTable ){ | |
| 111285 | + if( sqlite3WritableSchema(db) | |
| 111286 | + || db->init.imposterTable | |
| 111287 | + || !sqlite3Config.bExtraSchemaChecks | |
| 111288 | + ){ | |
| 111134 | 111289 | /* Skip these error checks for writable_schema=ON */ |
| 111135 | 111290 | return SQLITE_OK; |
| 111136 | 111291 | } |
| 111137 | 111292 | if( db->init.busy ){ |
| 111138 | 111293 | if( sqlite3_stricmp(zType, db->init.azInit[0]) |
| 111139 | 111294 | || sqlite3_stricmp(zName, db->init.azInit[1]) |
| 111140 | 111295 | || sqlite3_stricmp(zTblName, db->init.azInit[2]) |
| 111141 | 111296 | ){ |
| 111142 | - if( sqlite3Config.bExtraSchemaChecks ){ | |
| 111143 | - sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ | |
| 111144 | - return SQLITE_ERROR; | |
| 111145 | - } | |
| 111297 | + sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ | |
| 111298 | + return SQLITE_ERROR; | |
| 111146 | 111299 | } |
| 111147 | 111300 | }else{ |
| 111148 | 111301 | if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7)) |
| 111149 | 111302 | || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName)) |
| 111150 | 111303 | ){ |
| @@ -112354,11 +112507,11 @@ | ||
| 112354 | 112507 | ** table entry. This is only required if currently generating VDBE |
| 112355 | 112508 | ** code for a CREATE TABLE (not when parsing one as part of reading |
| 112356 | 112509 | ** a database schema). */ |
| 112357 | 112510 | if( v && pPk->tnum>0 ){ |
| 112358 | 112511 | assert( db->init.busy==0 ); |
| 112359 | - sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto); | |
| 112512 | + sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto); | |
| 112360 | 112513 | } |
| 112361 | 112514 | |
| 112362 | 112515 | /* The root page of the PRIMARY KEY is the table root page */ |
| 112363 | 112516 | pPk->tnum = pTab->tnum; |
| 112364 | 112517 | |
| @@ -112942,14 +113095,12 @@ | ||
| 112942 | 113095 | ** statement that defines the view. |
| 112943 | 113096 | */ |
| 112944 | 113097 | assert( pTable->pSelect ); |
| 112945 | 113098 | pSel = sqlite3SelectDup(db, pTable->pSelect, 0); |
| 112946 | 113099 | if( pSel ){ |
| 112947 | -#ifndef SQLITE_OMIT_ALTERTABLE | |
| 112948 | 113100 | u8 eParseMode = pParse->eParseMode; |
| 112949 | 113101 | pParse->eParseMode = PARSE_MODE_NORMAL; |
| 112950 | -#endif | |
| 112951 | 113102 | n = pParse->nTab; |
| 112952 | 113103 | sqlite3SrcListAssignCursors(pParse, pSel->pSrc); |
| 112953 | 113104 | pTable->nCol = -1; |
| 112954 | 113105 | DisableLookaside; |
| 112955 | 113106 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| @@ -112993,13 +113144,11 @@ | ||
| 112993 | 113144 | } |
| 112994 | 113145 | pTable->nNVCol = pTable->nCol; |
| 112995 | 113146 | sqlite3DeleteTable(db, pSelTab); |
| 112996 | 113147 | sqlite3SelectDelete(db, pSel); |
| 112997 | 113148 | EnableLookaside; |
| 112998 | -#ifndef SQLITE_OMIT_ALTERTABLE | |
| 112999 | 113149 | pParse->eParseMode = eParseMode; |
| 113000 | -#endif | |
| 113001 | 113150 | } else { |
| 113002 | 113151 | nErr++; |
| 113003 | 113152 | } |
| 113004 | 113153 | pTable->pSchema->schemaFlags |= DB_UnresetViews; |
| 113005 | 113154 | if( db->mallocFailed ){ |
| @@ -113050,11 +113199,11 @@ | ||
| 113050 | 113199 | ** We must continue looping until all tables and indices with |
| 113051 | 113200 | ** rootpage==iFrom have been converted to have a rootpage of iTo |
| 113052 | 113201 | ** in order to be certain that we got the right one. |
| 113053 | 113202 | */ |
| 113054 | 113203 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 113055 | -SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ | |
| 113204 | +SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){ | |
| 113056 | 113205 | HashElem *pElem; |
| 113057 | 113206 | Hash *pHash; |
| 113058 | 113207 | Db *pDb; |
| 113059 | 113208 | |
| 113060 | 113209 | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| @@ -113127,22 +113276,22 @@ | ||
| 113127 | 113276 | ** and root page 5 happened to be the largest root-page number in the |
| 113128 | 113277 | ** database, then root page 5 would be moved to page 4 by the |
| 113129 | 113278 | ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit |
| 113130 | 113279 | ** a free-list page. |
| 113131 | 113280 | */ |
| 113132 | - int iTab = pTab->tnum; | |
| 113133 | - int iDestroyed = 0; | |
| 113281 | + Pgno iTab = pTab->tnum; | |
| 113282 | + Pgno iDestroyed = 0; | |
| 113134 | 113283 | |
| 113135 | 113284 | while( 1 ){ |
| 113136 | 113285 | Index *pIdx; |
| 113137 | - int iLargest = 0; | |
| 113286 | + Pgno iLargest = 0; | |
| 113138 | 113287 | |
| 113139 | 113288 | if( iDestroyed==0 || iTab<iDestroyed ){ |
| 113140 | 113289 | iLargest = iTab; |
| 113141 | 113290 | } |
| 113142 | 113291 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 113143 | - int iIdx = pIdx->tnum; | |
| 113292 | + Pgno iIdx = pIdx->tnum; | |
| 113144 | 113293 | assert( pIdx->pSchema==pTab->pSchema ); |
| 113145 | 113294 | if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){ |
| 113146 | 113295 | iLargest = iIdx; |
| 113147 | 113296 | } |
| 113148 | 113297 | } |
| @@ -113561,11 +113710,11 @@ | ||
| 113561 | 113710 | int iTab = pParse->nTab++; /* Btree cursor used for pTab */ |
| 113562 | 113711 | int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ |
| 113563 | 113712 | int iSorter; /* Cursor opened by OpenSorter (if in use) */ |
| 113564 | 113713 | int addr1; /* Address of top of loop */ |
| 113565 | 113714 | int addr2; /* Address to jump to for next iteration */ |
| 113566 | - int tnum; /* Root page of index */ | |
| 113715 | + Pgno tnum; /* Root page of index */ | |
| 113567 | 113716 | int iPartIdxLabel; /* Jump to this label to skip a row */ |
| 113568 | 113717 | Vdbe *v; /* Generate code into this virtual machine */ |
| 113569 | 113718 | KeyInfo *pKey; /* KeyInfo for index */ |
| 113570 | 113719 | int regRecord; /* Register holding assembled index record */ |
| 113571 | 113720 | sqlite3 *db = pParse->db; /* The database connection */ |
| @@ -113582,11 +113731,11 @@ | ||
| 113582 | 113731 | sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); |
| 113583 | 113732 | |
| 113584 | 113733 | v = sqlite3GetVdbe(pParse); |
| 113585 | 113734 | if( v==0 ) return; |
| 113586 | 113735 | if( memRootPage>=0 ){ |
| 113587 | - tnum = memRootPage; | |
| 113736 | + tnum = (Pgno)memRootPage; | |
| 113588 | 113737 | }else{ |
| 113589 | 113738 | tnum = pIndex->tnum; |
| 113590 | 113739 | } |
| 113591 | 113740 | pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); |
| 113592 | 113741 | assert( pKey!=0 || db->mallocFailed || pParse->nErr ); |
| @@ -113607,11 +113756,11 @@ | ||
| 113607 | 113756 | sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); |
| 113608 | 113757 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 113609 | 113758 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); |
| 113610 | 113759 | sqlite3VdbeJumpHere(v, addr1); |
| 113611 | 113760 | if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); |
| 113612 | - sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, | |
| 113761 | + sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb, | |
| 113613 | 113762 | (char *)pKey, P4_KEYINFO); |
| 113614 | 113763 | sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); |
| 113615 | 113764 | |
| 113616 | 113765 | addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v); |
| 113617 | 113766 | if( IsUniqueIndex(pIndex) ){ |
| @@ -114216,11 +114365,11 @@ | ||
| 114216 | 114365 | ** doing so, code a Noop instruction and store its address in |
| 114217 | 114366 | ** Index.tnum. This is required in case this index is actually a |
| 114218 | 114367 | ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In |
| 114219 | 114368 | ** that case the convertToWithoutRowidTable() routine will replace |
| 114220 | 114369 | ** the Noop with a Goto to jump over the VDBE code generated below. */ |
| 114221 | - pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); | |
| 114370 | + pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop); | |
| 114222 | 114371 | sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); |
| 114223 | 114372 | |
| 114224 | 114373 | /* Gather the complete text of the CREATE INDEX statement into |
| 114225 | 114374 | ** the zStmt variable |
| 114226 | 114375 | */ |
| @@ -114258,11 +114407,11 @@ | ||
| 114258 | 114407 | sqlite3VdbeAddParseSchemaOp(v, iDb, |
| 114259 | 114408 | sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); |
| 114260 | 114409 | sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); |
| 114261 | 114410 | } |
| 114262 | 114411 | |
| 114263 | - sqlite3VdbeJumpHere(v, pIndex->tnum); | |
| 114412 | + sqlite3VdbeJumpHere(v, (int)pIndex->tnum); | |
| 114264 | 114413 | } |
| 114265 | 114414 | } |
| 114266 | 114415 | if( db->init.busy || pTblName==0 ){ |
| 114267 | 114416 | pIndex->pNext = pTab->pIndex; |
| 114268 | 114417 | pTab->pIndex = pIndex; |
| @@ -114333,11 +114482,11 @@ | ||
| 114333 | 114482 | ** |
| 114334 | 114483 | ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1 |
| 114335 | 114484 | ** table but other parts we are having to guess at, then do not let the |
| 114336 | 114485 | ** estimated number of rows in the table be less than 1000 (LogEst 99). |
| 114337 | 114486 | ** Failure to do this can cause the indexes for which we do not have |
| 114338 | - ** stat1 data to be ignored by the query planner. tag-20200527-1 | |
| 114487 | + ** stat1 data to be ignored by the query planner. | |
| 114339 | 114488 | */ |
| 114340 | 114489 | x = pIdx->pTable->nRowLogEst; |
| 114341 | 114490 | assert( 99==sqlite3LogEst(1000) ); |
| 114342 | 114491 | if( x<99 ){ |
| 114343 | 114492 | pIdx->pTable->nRowLogEst = x = 99; |
| @@ -120309,10 +120458,11 @@ | ||
| 120309 | 120458 | case OE_Cascade: |
| 120310 | 120459 | if( !pChanges ){ |
| 120311 | 120460 | pStep->op = TK_DELETE; |
| 120312 | 120461 | break; |
| 120313 | 120462 | } |
| 120463 | + /* no break */ deliberate_fall_through | |
| 120314 | 120464 | default: |
| 120315 | 120465 | pStep->op = TK_UPDATE; |
| 120316 | 120466 | } |
| 120317 | 120467 | pStep->pTrig = pTrigger; |
| 120318 | 120468 | pTrigger->pSchema = pTab->pSchema; |
| @@ -120581,11 +120731,11 @@ | ||
| 120581 | 120731 | for(i=1; i<iEnd; i++){ |
| 120582 | 120732 | VdbeOp *pOp = sqlite3VdbeGetOp(v, i); |
| 120583 | 120733 | assert( pOp!=0 ); |
| 120584 | 120734 | if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ |
| 120585 | 120735 | Index *pIndex; |
| 120586 | - int tnum = pOp->p2; | |
| 120736 | + Pgno tnum = pOp->p2; | |
| 120587 | 120737 | if( tnum==pTab->tnum ){ |
| 120588 | 120738 | return 1; |
| 120589 | 120739 | } |
| 120590 | 120740 | for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ |
| 120591 | 120741 | if( tnum==pIndex->tnum ){ |
| @@ -122013,11 +122163,11 @@ | ||
| 122013 | 122163 | sqlite3VdbeJumpHere(v, addr1); |
| 122014 | 122164 | break; |
| 122015 | 122165 | } |
| 122016 | 122166 | case OE_Abort: |
| 122017 | 122167 | sqlite3MayAbort(pParse); |
| 122018 | - /* Fall through */ | |
| 122168 | + /* no break */ deliberate_fall_through | |
| 122019 | 122169 | case OE_Rollback: |
| 122020 | 122170 | case OE_Fail: { |
| 122021 | 122171 | char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, |
| 122022 | 122172 | pCol->zName); |
| 122023 | 122173 | sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, |
| @@ -122241,11 +122391,11 @@ | ||
| 122241 | 122391 | VdbeCoverage(v); |
| 122242 | 122392 | |
| 122243 | 122393 | switch( onError ){ |
| 122244 | 122394 | default: { |
| 122245 | 122395 | onError = OE_Abort; |
| 122246 | - /* Fall thru into the next case */ | |
| 122396 | + /* no break */ deliberate_fall_through | |
| 122247 | 122397 | } |
| 122248 | 122398 | case OE_Rollback: |
| 122249 | 122399 | case OE_Abort: |
| 122250 | 122400 | case OE_Fail: { |
| 122251 | 122401 | testcase( onError==OE_Rollback ); |
| @@ -122302,11 +122452,11 @@ | ||
| 122302 | 122452 | break; |
| 122303 | 122453 | } |
| 122304 | 122454 | #ifndef SQLITE_OMIT_UPSERT |
| 122305 | 122455 | case OE_Update: { |
| 122306 | 122456 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); |
| 122307 | - /* Fall through */ | |
| 122457 | + /* no break */ deliberate_fall_through | |
| 122308 | 122458 | } |
| 122309 | 122459 | #endif |
| 122310 | 122460 | case OE_Ignore: { |
| 122311 | 122461 | testcase( onError==OE_Ignore ); |
| 122312 | 122462 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -122523,11 +122673,11 @@ | ||
| 122523 | 122673 | break; |
| 122524 | 122674 | } |
| 122525 | 122675 | #ifndef SQLITE_OMIT_UPSERT |
| 122526 | 122676 | case OE_Update: { |
| 122527 | 122677 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); |
| 122528 | - /* Fall through */ | |
| 122678 | + /* no break */ deliberate_fall_through | |
| 122529 | 122679 | } |
| 122530 | 122680 | #endif |
| 122531 | 122681 | case OE_Ignore: { |
| 122532 | 122682 | testcase( onError==OE_Ignore ); |
| 122533 | 122683 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -126204,17 +126354,23 @@ | ||
| 126204 | 126354 | ** |
| 126205 | 126355 | ** Return the number of pages in the specified database. |
| 126206 | 126356 | */ |
| 126207 | 126357 | case PragTyp_PAGE_COUNT: { |
| 126208 | 126358 | int iReg; |
| 126359 | + i64 x = 0; | |
| 126209 | 126360 | sqlite3CodeVerifySchema(pParse, iDb); |
| 126210 | 126361 | iReg = ++pParse->nMem; |
| 126211 | 126362 | if( sqlite3Tolower(zLeft[0])=='p' ){ |
| 126212 | 126363 | sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); |
| 126213 | 126364 | }else{ |
| 126214 | - sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, | |
| 126215 | - sqlite3AbsInt32(sqlite3Atoi(zRight))); | |
| 126365 | + if( zRight && sqlite3DecOrHexToI64(zRight,&x)==0 ){ | |
| 126366 | + if( x<0 ) x = 0; | |
| 126367 | + else if( x>0xfffffffe ) x = 0xfffffffe; | |
| 126368 | + }else{ | |
| 126369 | + x = 0; | |
| 126370 | + } | |
| 126371 | + sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, (int)x); | |
| 126216 | 126372 | } |
| 126217 | 126373 | sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); |
| 126218 | 126374 | break; |
| 126219 | 126375 | } |
| 126220 | 126376 | |
| @@ -127113,13 +127269,26 @@ | ||
| 127113 | 127269 | ** |
| 127114 | 127270 | ** The "quick_check" is reduced version of |
| 127115 | 127271 | ** integrity_check designed to detect most database corruption |
| 127116 | 127272 | ** without the overhead of cross-checking indexes. Quick_check |
| 127117 | 127273 | ** is linear time wherease integrity_check is O(NlogN). |
| 127274 | + ** | |
| 127275 | + ** The maximum nubmer of errors is 100 by default. A different default | |
| 127276 | + ** can be specified using a numeric parameter N. | |
| 127277 | + ** | |
| 127278 | + ** Or, the parameter N can be the name of a table. In that case, only | |
| 127279 | + ** the one table named is verified. The freelist is only verified if | |
| 127280 | + ** the named table is "sqlite_schema" (or one of its aliases). | |
| 127281 | + ** | |
| 127282 | + ** All schemas are checked by default. To check just a single | |
| 127283 | + ** schema, use the form: | |
| 127284 | + ** | |
| 127285 | + ** PRAGMA schema.integrity_check; | |
| 127118 | 127286 | */ |
| 127119 | 127287 | case PragTyp_INTEGRITY_CHECK: { |
| 127120 | 127288 | int i, j, addr, mxErr; |
| 127289 | + Table *pObjTab = 0; /* Check only this one table, if not NULL */ | |
| 127121 | 127290 | |
| 127122 | 127291 | int isQuick = (sqlite3Tolower(zLeft[0])=='q'); |
| 127123 | 127292 | |
| 127124 | 127293 | /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", |
| 127125 | 127294 | ** then iDb is set to the index of the database identified by <db>. |
| @@ -127138,13 +127307,17 @@ | ||
| 127138 | 127307 | pParse->nMem = 6; |
| 127139 | 127308 | |
| 127140 | 127309 | /* Set the maximum error count */ |
| 127141 | 127310 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 127142 | 127311 | if( zRight ){ |
| 127143 | - sqlite3GetInt32(zRight, &mxErr); | |
| 127144 | - if( mxErr<=0 ){ | |
| 127145 | - mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; | |
| 127312 | + if( sqlite3GetInt32(zRight, &mxErr) ){ | |
| 127313 | + if( mxErr<=0 ){ | |
| 127314 | + mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; | |
| 127315 | + } | |
| 127316 | + }else{ | |
| 127317 | + pObjTab = sqlite3LocateTable(pParse, 0, zRight, | |
| 127318 | + iDb>=0 ? db->aDb[iDb].zDbSName : 0); | |
| 127146 | 127319 | } |
| 127147 | 127320 | } |
| 127148 | 127321 | sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ |
| 127149 | 127322 | |
| 127150 | 127323 | /* Do an integrity check on each database file */ |
| @@ -127169,19 +127342,25 @@ | ||
| 127169 | 127342 | pTbls = &db->aDb[i].pSchema->tblHash; |
| 127170 | 127343 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ |
| 127171 | 127344 | Table *pTab = sqliteHashData(x); /* Current table */ |
| 127172 | 127345 | Index *pIdx; /* An index on pTab */ |
| 127173 | 127346 | int nIdx; /* Number of indexes on pTab */ |
| 127347 | + if( pObjTab && pObjTab!=pTab ) continue; | |
| 127174 | 127348 | if( HasRowid(pTab) ) cnt++; |
| 127175 | 127349 | for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } |
| 127176 | 127350 | if( nIdx>mxIdx ) mxIdx = nIdx; |
| 127177 | 127351 | } |
| 127352 | + if( cnt==0 ) continue; | |
| 127353 | + if( pObjTab ) cnt++; | |
| 127178 | 127354 | aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); |
| 127179 | 127355 | if( aRoot==0 ) break; |
| 127180 | - for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ | |
| 127356 | + cnt = 0; | |
| 127357 | + if( pObjTab ) aRoot[++cnt] = 0; | |
| 127358 | + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ | |
| 127181 | 127359 | Table *pTab = sqliteHashData(x); |
| 127182 | 127360 | Index *pIdx; |
| 127361 | + if( pObjTab && pObjTab!=pTab ) continue; | |
| 127183 | 127362 | if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; |
| 127184 | 127363 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 127185 | 127364 | aRoot[++cnt] = pIdx->tnum; |
| 127186 | 127365 | } |
| 127187 | 127366 | } |
| @@ -127211,10 +127390,11 @@ | ||
| 127211 | 127390 | int loopTop; |
| 127212 | 127391 | int iDataCur, iIdxCur; |
| 127213 | 127392 | int r1 = -1; |
| 127214 | 127393 | |
| 127215 | 127394 | if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ |
| 127395 | + if( pObjTab && pObjTab!=pTab ) continue; | |
| 127216 | 127396 | pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); |
| 127217 | 127397 | sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, |
| 127218 | 127398 | 1, 0, &iDataCur, &iIdxCur); |
| 127219 | 127399 | /* reg[7] counts the number of entries in the table. |
| 127220 | 127400 | ** reg[8+i] counts the number of entries in the i-th index |
| @@ -128272,11 +128452,17 @@ | ||
| 128272 | 128452 | sqlite3_stmt *pStmt; |
| 128273 | 128453 | TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ |
| 128274 | 128454 | |
| 128275 | 128455 | assert( db->init.busy ); |
| 128276 | 128456 | db->init.iDb = iDb; |
| 128277 | - db->init.newTnum = sqlite3Atoi(argv[3]); | |
| 128457 | + if( sqlite3GetUInt32(argv[3], &db->init.newTnum)==0 | |
| 128458 | + || (db->init.newTnum>pData->mxPage && pData->mxPage>0) | |
| 128459 | + ){ | |
| 128460 | + if( sqlite3Config.bExtraSchemaChecks ){ | |
| 128461 | + corruptSchema(pData, argv[1], "invalid rootpage"); | |
| 128462 | + } | |
| 128463 | + } | |
| 128278 | 128464 | db->init.orphanTrigger = 0; |
| 128279 | 128465 | db->init.azInit = argv; |
| 128280 | 128466 | pStmt = 0; |
| 128281 | 128467 | TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); |
| 128282 | 128468 | rc = db->errCode; |
| @@ -128305,16 +128491,21 @@ | ||
| 128305 | 128491 | ** been created when we processed the CREATE TABLE. All we have |
| 128306 | 128492 | ** to do here is record the root page number for that index. |
| 128307 | 128493 | */ |
| 128308 | 128494 | Index *pIndex; |
| 128309 | 128495 | pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); |
| 128310 | - if( pIndex==0 | |
| 128311 | - || sqlite3GetInt32(argv[3],&pIndex->tnum)==0 | |
| 128496 | + if( pIndex==0 ){ | |
| 128497 | + corruptSchema(pData, argv[1], "orphan index"); | |
| 128498 | + }else | |
| 128499 | + if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 | |
| 128312 | 128500 | || pIndex->tnum<2 |
| 128501 | + || pIndex->tnum>pData->mxPage | |
| 128313 | 128502 | || sqlite3IndexHasDuplicateRootPage(pIndex) |
| 128314 | 128503 | ){ |
| 128315 | - corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index"); | |
| 128504 | + if( sqlite3Config.bExtraSchemaChecks ){ | |
| 128505 | + corruptSchema(pData, argv[1], "invalid rootpage"); | |
| 128506 | + } | |
| 128316 | 128507 | } |
| 128317 | 128508 | } |
| 128318 | 128509 | return 0; |
| 128319 | 128510 | } |
| 128320 | 128511 | |
| @@ -128364,10 +128555,11 @@ | ||
| 128364 | 128555 | initData.iDb = iDb; |
| 128365 | 128556 | initData.rc = SQLITE_OK; |
| 128366 | 128557 | initData.pzErrMsg = pzErrMsg; |
| 128367 | 128558 | initData.mInitFlags = mFlags; |
| 128368 | 128559 | initData.nInitRow = 0; |
| 128560 | + initData.mxPage = 0; | |
| 128369 | 128561 | sqlite3InitCallback(&initData, 5, (char **)azArg, 0); |
| 128370 | 128562 | db->mDbFlags &= mask; |
| 128371 | 128563 | if( initData.rc ){ |
| 128372 | 128564 | rc = initData.rc; |
| 128373 | 128565 | goto error_out; |
| @@ -128486,10 +128678,11 @@ | ||
| 128486 | 128678 | } |
| 128487 | 128679 | |
| 128488 | 128680 | /* Read the schema information out of the schema tables |
| 128489 | 128681 | */ |
| 128490 | 128682 | assert( db->init.busy ); |
| 128683 | + initData.mxPage = sqlite3BtreeLastPage(pDb->pBt); | |
| 128491 | 128684 | { |
| 128492 | 128685 | char *zSql; |
| 128493 | 128686 | zSql = sqlite3MPrintf(db, |
| 128494 | 128687 | "SELECT*FROM\"%w\".%s ORDER BY rowid", |
| 128495 | 128688 | db->aDb[iDb].zDbSName, zSchemaTabName); |
| @@ -129368,12 +129561,14 @@ | ||
| 129368 | 129561 | ** Return the index of a column in a table. Return -1 if the column |
| 129369 | 129562 | ** is not contained in the table. |
| 129370 | 129563 | */ |
| 129371 | 129564 | static int columnIndex(Table *pTab, const char *zCol){ |
| 129372 | 129565 | int i; |
| 129373 | - for(i=0; i<pTab->nCol; i++){ | |
| 129374 | - if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; | |
| 129566 | + u8 h = sqlite3StrIHash(zCol); | |
| 129567 | + Column *pCol; | |
| 129568 | + for(pCol=pTab->aCol, i=0; i<pTab->nCol; pCol++, i++){ | |
| 129569 | + if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; | |
| 129375 | 129570 | } |
| 129376 | 129571 | return -1; |
| 129377 | 129572 | } |
| 129378 | 129573 | |
| 129379 | 129574 | /* |
| @@ -130231,20 +130426,24 @@ | ||
| 130231 | 130426 | sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1); |
| 130232 | 130427 | break; |
| 130233 | 130428 | } |
| 130234 | 130429 | |
| 130235 | 130430 | case SRT_Upfrom: { |
| 130236 | -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT | |
| 130237 | 130431 | if( pSort ){ |
| 130238 | 130432 | pushOntoSorter( |
| 130239 | 130433 | pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); |
| 130240 | - }else | |
| 130241 | -#endif | |
| 130242 | - { | |
| 130434 | + }else{ | |
| 130243 | 130435 | int i2 = pDest->iSDParm2; |
| 130244 | 130436 | int r1 = sqlite3GetTempReg(pParse); |
| 130245 | - sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1); | |
| 130437 | + | |
| 130438 | + /* If the UPDATE FROM join is an aggregate that matches no rows, it | |
| 130439 | + ** might still be trying to return one row, because that is what | |
| 130440 | + ** aggregates do. Don't record that empty row in the output table. */ | |
| 130441 | + sqlite3VdbeAddOp2(v, OP_IsNull, regResult, iBreak); VdbeCoverage(v); | |
| 130442 | + | |
| 130443 | + sqlite3VdbeAddOp3(v, OP_MakeRecord, | |
| 130444 | + regResult+(i2<0), nResultCol-(i2<0), r1); | |
| 130246 | 130445 | if( i2<0 ){ |
| 130247 | 130446 | sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult); |
| 130248 | 130447 | }else{ |
| 130249 | 130448 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2); |
| 130250 | 130449 | } |
| @@ -130682,11 +130881,10 @@ | ||
| 130682 | 130881 | case SRT_Mem: { |
| 130683 | 130882 | /* The LIMIT clause will terminate the loop for us */ |
| 130684 | 130883 | break; |
| 130685 | 130884 | } |
| 130686 | 130885 | #endif |
| 130687 | -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT | |
| 130688 | 130886 | case SRT_Upfrom: { |
| 130689 | 130887 | int i2 = pDest->iSDParm2; |
| 130690 | 130888 | int r1 = sqlite3GetTempReg(pParse); |
| 130691 | 130889 | sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1); |
| 130692 | 130890 | if( i2<0 ){ |
| @@ -130694,11 +130892,10 @@ | ||
| 130694 | 130892 | }else{ |
| 130695 | 130893 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2); |
| 130696 | 130894 | } |
| 130697 | 130895 | break; |
| 130698 | 130896 | } |
| 130699 | -#endif | |
| 130700 | 130897 | default: { |
| 130701 | 130898 | assert( eDest==SRT_Output || eDest==SRT_Coroutine ); |
| 130702 | 130899 | testcase( eDest==SRT_Output ); |
| 130703 | 130900 | testcase( eDest==SRT_Coroutine ); |
| 130704 | 130901 | if( eDest==SRT_Output ){ |
| @@ -132281,11 +132478,11 @@ | ||
| 132281 | 132478 | KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ |
| 132282 | 132479 | KeyInfo *pKeyMerge; /* Comparison information for merging rows */ |
| 132283 | 132480 | sqlite3 *db; /* Database connection */ |
| 132284 | 132481 | ExprList *pOrderBy; /* The ORDER BY clause */ |
| 132285 | 132482 | int nOrderBy; /* Number of terms in the ORDER BY clause */ |
| 132286 | - int *aPermute; /* Mapping from ORDER BY terms to result set columns */ | |
| 132483 | + u32 *aPermute; /* Mapping from ORDER BY terms to result set columns */ | |
| 132287 | 132484 | |
| 132288 | 132485 | assert( p->pOrderBy!=0 ); |
| 132289 | 132486 | assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ |
| 132290 | 132487 | db = pParse->db; |
| 132291 | 132488 | v = pParse->pVdbe; |
| @@ -132330,11 +132527,11 @@ | ||
| 132330 | 132527 | ** row of results comes from selectA or selectB. Also add explicit |
| 132331 | 132528 | ** collations to the ORDER BY clause terms so that when the subqueries |
| 132332 | 132529 | ** to the right and the left are evaluated, they use the correct |
| 132333 | 132530 | ** collation. |
| 132334 | 132531 | */ |
| 132335 | - aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1)); | |
| 132532 | + aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); | |
| 132336 | 132533 | if( aPermute ){ |
| 132337 | 132534 | struct ExprList_item *pItem; |
| 132338 | 132535 | aPermute[0] = nOrderBy; |
| 132339 | 132536 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 132340 | 132537 | assert( pItem->u.x.iOrderByCol>0 ); |
| @@ -133286,11 +133483,11 @@ | ||
| 133286 | 133483 | sqlite3AggInfoPersistWalkerInit(&w, pParse); |
| 133287 | 133484 | sqlite3WalkSelect(&w,pSub1); |
| 133288 | 133485 | sqlite3SelectDelete(db, pSub1); |
| 133289 | 133486 | |
| 133290 | 133487 | #if SELECTTRACE_ENABLED |
| 133291 | - if( sqlite3SelectTrace & 0x100 ){ | |
| 133488 | + if( sqlite3_unsupported_selecttrace & 0x100 ){ | |
| 133292 | 133489 | SELECTTRACE(0x100,pParse,p,("After flattening:\n")); |
| 133293 | 133490 | sqlite3TreeViewSelect(0, p, 0); |
| 133294 | 133491 | } |
| 133295 | 133492 | #endif |
| 133296 | 133493 | |
| @@ -134724,11 +134921,11 @@ | ||
| 134724 | 134921 | sWalker.pParse = pParse; |
| 134725 | 134922 | sWalker.xExprCallback = havingToWhereExprCb; |
| 134726 | 134923 | sWalker.u.pSelect = p; |
| 134727 | 134924 | sqlite3WalkExpr(&sWalker, p->pHaving); |
| 134728 | 134925 | #if SELECTTRACE_ENABLED |
| 134729 | - if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ | |
| 134926 | + if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ | |
| 134730 | 134927 | SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); |
| 134731 | 134928 | sqlite3TreeViewSelect(0, p, 0); |
| 134732 | 134929 | } |
| 134733 | 134930 | #endif |
| 134734 | 134931 | } |
| @@ -134846,11 +135043,11 @@ | ||
| 134846 | 135043 | } |
| 134847 | 135044 | p->pEList->a[0].pExpr = pExpr; |
| 134848 | 135045 | p->selFlags &= ~SF_Aggregate; |
| 134849 | 135046 | |
| 134850 | 135047 | #if SELECTTRACE_ENABLED |
| 134851 | - if( sqlite3SelectTrace & 0x400 ){ | |
| 135048 | + if( sqlite3_unsupported_selecttrace & 0x400 ){ | |
| 134852 | 135049 | SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); |
| 134853 | 135050 | sqlite3TreeViewSelect(0, p, 0); |
| 134854 | 135051 | } |
| 134855 | 135052 | #endif |
| 134856 | 135053 | return 1; |
| @@ -134899,11 +135096,11 @@ | ||
| 134899 | 135096 | return 1; |
| 134900 | 135097 | } |
| 134901 | 135098 | if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; |
| 134902 | 135099 | #if SELECTTRACE_ENABLED |
| 134903 | 135100 | SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); |
| 134904 | - if( sqlite3SelectTrace & 0x100 ){ | |
| 135101 | + if( sqlite3_unsupported_selecttrace & 0x100 ){ | |
| 134905 | 135102 | sqlite3TreeViewSelect(0, p, 0); |
| 134906 | 135103 | } |
| 134907 | 135104 | #endif |
| 134908 | 135105 | |
| 134909 | 135106 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| @@ -134926,11 +135123,11 @@ | ||
| 134926 | 135123 | if( pParse->nErr || db->mallocFailed ){ |
| 134927 | 135124 | goto select_end; |
| 134928 | 135125 | } |
| 134929 | 135126 | assert( p->pEList!=0 ); |
| 134930 | 135127 | #if SELECTTRACE_ENABLED |
| 134931 | - if( sqlite3SelectTrace & 0x104 ){ | |
| 135128 | + if( sqlite3_unsupported_selecttrace & 0x104 ){ | |
| 134932 | 135129 | SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); |
| 134933 | 135130 | sqlite3TreeViewSelect(0, p, 0); |
| 134934 | 135131 | } |
| 134935 | 135132 | #endif |
| 134936 | 135133 | |
| @@ -134961,11 +135158,11 @@ | ||
| 134961 | 135158 | if( rc ){ |
| 134962 | 135159 | assert( db->mallocFailed || pParse->nErr>0 ); |
| 134963 | 135160 | goto select_end; |
| 134964 | 135161 | } |
| 134965 | 135162 | #if SELECTTRACE_ENABLED |
| 134966 | - if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){ | |
| 135163 | + if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ | |
| 134967 | 135164 | SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); |
| 134968 | 135165 | sqlite3TreeViewSelect(0, p, 0); |
| 134969 | 135166 | } |
| 134970 | 135167 | #endif |
| 134971 | 135168 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| @@ -135068,11 +135265,11 @@ | ||
| 135068 | 135265 | */ |
| 135069 | 135266 | if( p->pPrior ){ |
| 135070 | 135267 | rc = multiSelect(pParse, p, pDest); |
| 135071 | 135268 | #if SELECTTRACE_ENABLED |
| 135072 | 135269 | SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); |
| 135073 | - if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ | |
| 135270 | + if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ | |
| 135074 | 135271 | sqlite3TreeViewSelect(0, p, 0); |
| 135075 | 135272 | } |
| 135076 | 135273 | #endif |
| 135077 | 135274 | if( p->pNext==0 ) ExplainQueryPlanPop(pParse); |
| 135078 | 135275 | return rc; |
| @@ -135087,11 +135284,11 @@ | ||
| 135087 | 135284 | if( pTabList->nSrc>1 |
| 135088 | 135285 | && OptimizationEnabled(db, SQLITE_PropagateConst) |
| 135089 | 135286 | && propagateConstants(pParse, p) |
| 135090 | 135287 | ){ |
| 135091 | 135288 | #if SELECTTRACE_ENABLED |
| 135092 | - if( sqlite3SelectTrace & 0x100 ){ | |
| 135289 | + if( sqlite3_unsupported_selecttrace & 0x100 ){ | |
| 135093 | 135290 | SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); |
| 135094 | 135291 | sqlite3TreeViewSelect(0, p, 0); |
| 135095 | 135292 | } |
| 135096 | 135293 | #endif |
| 135097 | 135294 | }else{ |
| @@ -135175,11 +135372,11 @@ | ||
| 135175 | 135372 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 135176 | 135373 | && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, |
| 135177 | 135374 | (pItem->fg.jointype & JT_OUTER)!=0) |
| 135178 | 135375 | ){ |
| 135179 | 135376 | #if SELECTTRACE_ENABLED |
| 135180 | - if( sqlite3SelectTrace & 0x100 ){ | |
| 135377 | + if( sqlite3_unsupported_selecttrace & 0x100 ){ | |
| 135181 | 135378 | SELECTTRACE(0x100,pParse,p, |
| 135182 | 135379 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| 135183 | 135380 | sqlite3TreeViewSelect(0, p, 0); |
| 135184 | 135381 | } |
| 135185 | 135382 | #endif |
| @@ -135275,11 +135472,11 @@ | ||
| 135275 | 135472 | pGroupBy = p->pGroupBy; |
| 135276 | 135473 | pHaving = p->pHaving; |
| 135277 | 135474 | sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; |
| 135278 | 135475 | |
| 135279 | 135476 | #if SELECTTRACE_ENABLED |
| 135280 | - if( sqlite3SelectTrace & 0x400 ){ | |
| 135477 | + if( sqlite3_unsupported_selecttrace & 0x400 ){ | |
| 135281 | 135478 | SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); |
| 135282 | 135479 | sqlite3TreeViewSelect(0, p, 0); |
| 135283 | 135480 | } |
| 135284 | 135481 | #endif |
| 135285 | 135482 | |
| @@ -135311,11 +135508,11 @@ | ||
| 135311 | 135508 | ** the sDistinct.isTnct is still set. Hence, isTnct represents the |
| 135312 | 135509 | ** original setting of the SF_Distinct flag, not the current setting */ |
| 135313 | 135510 | assert( sDistinct.isTnct ); |
| 135314 | 135511 | |
| 135315 | 135512 | #if SELECTTRACE_ENABLED |
| 135316 | - if( sqlite3SelectTrace & 0x400 ){ | |
| 135513 | + if( sqlite3_unsupported_selecttrace & 0x400 ){ | |
| 135317 | 135514 | SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); |
| 135318 | 135515 | sqlite3TreeViewSelect(0, p, 0); |
| 135319 | 135516 | } |
| 135320 | 135517 | #endif |
| 135321 | 135518 | } |
| @@ -135559,11 +135756,11 @@ | ||
| 135559 | 135756 | sNC.ncFlags &= ~NC_InAggFunc; |
| 135560 | 135757 | } |
| 135561 | 135758 | pAggInfo->mxReg = pParse->nMem; |
| 135562 | 135759 | if( db->mallocFailed ) goto select_end; |
| 135563 | 135760 | #if SELECTTRACE_ENABLED |
| 135564 | - if( sqlite3SelectTrace & 0x400 ){ | |
| 135761 | + if( sqlite3_unsupported_selecttrace & 0x400 ){ | |
| 135565 | 135762 | int ii; |
| 135566 | 135763 | SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); |
| 135567 | 135764 | sqlite3TreeViewSelect(0, p, 0); |
| 135568 | 135765 | for(ii=0; ii<pAggInfo->nColumn; ii++){ |
| 135569 | 135766 | sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", |
| @@ -135823,11 +136020,11 @@ | ||
| 135823 | 136020 | const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
| 135824 | 136021 | const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ |
| 135825 | 136022 | Index *pIdx; /* Iterator variable */ |
| 135826 | 136023 | KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ |
| 135827 | 136024 | Index *pBest = 0; /* Best index found so far */ |
| 135828 | - int iRoot = pTab->tnum; /* Root page of scanned b-tree */ | |
| 136025 | + Pgno iRoot = pTab->tnum; /* Root page of scanned b-tree */ | |
| 135829 | 136026 | |
| 135830 | 136027 | sqlite3CodeVerifySchema(pParse, iDb); |
| 135831 | 136028 | sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
| 135832 | 136029 | |
| 135833 | 136030 | /* Search for the index that has the lowest scan cost. |
| @@ -135855,11 +136052,11 @@ | ||
| 135855 | 136052 | iRoot = pBest->tnum; |
| 135856 | 136053 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| 135857 | 136054 | } |
| 135858 | 136055 | |
| 135859 | 136056 | /* Open a read-only cursor, execute the OP_Count, close the cursor. */ |
| 135860 | - sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1); | |
| 136057 | + sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, (int)iRoot, iDb, 1); | |
| 135861 | 136058 | if( pKeyInfo ){ |
| 135862 | 136059 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 135863 | 136060 | } |
| 135864 | 136061 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); |
| 135865 | 136062 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| @@ -135978,11 +136175,11 @@ | ||
| 135978 | 136175 | } |
| 135979 | 136176 | #endif |
| 135980 | 136177 | |
| 135981 | 136178 | #if SELECTTRACE_ENABLED |
| 135982 | 136179 | SELECTTRACE(0x1,pParse,p,("end processing\n")); |
| 135983 | - if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ | |
| 136180 | + if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ | |
| 135984 | 136181 | sqlite3TreeViewSelect(0, p, 0); |
| 135985 | 136182 | } |
| 135986 | 136183 | #endif |
| 135987 | 136184 | ExplainQueryPlanPop(pParse); |
| 135988 | 136185 | return rc; |
| @@ -142375,11 +142572,11 @@ | ||
| 142375 | 142572 | if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) |
| 142376 | 142573 | && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) |
| 142377 | 142574 | ){ |
| 142378 | 142575 | int i; |
| 142379 | 142576 | Table *pTab = pIdx->pTable; |
| 142380 | - int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1)); | |
| 142577 | + u32 *ai = (u32*)sqlite3DbMallocZero(pParse->db, sizeof(u32)*(pTab->nCol+1)); | |
| 142381 | 142578 | if( ai ){ |
| 142382 | 142579 | ai[0] = pTab->nCol; |
| 142383 | 142580 | for(i=0; i<pIdx->nColumn-1; i++){ |
| 142384 | 142581 | int x1, x2; |
| 142385 | 142582 | assert( pIdx->aiColumn[i]<pTab->nCol ); |
| @@ -151749,11 +151946,11 @@ | ||
| 151749 | 151946 | assert( pWin->pOwner==pExpr ); |
| 151750 | 151947 | return WRC_Prune; |
| 151751 | 151948 | } |
| 151752 | 151949 | } |
| 151753 | 151950 | } |
| 151754 | - /* Fall through. */ | |
| 151951 | + /* no break */ deliberate_fall_through | |
| 151755 | 151952 | |
| 151756 | 151953 | case TK_AGG_FUNCTION: |
| 151757 | 151954 | case TK_COLUMN: { |
| 151758 | 151955 | int iCol = -1; |
| 151759 | 151956 | if( p->pSub ){ |
| @@ -159966,10 +160163,11 @@ | ||
| 159966 | 160163 | *tokenType = TK_DOT; |
| 159967 | 160164 | return 1; |
| 159968 | 160165 | } |
| 159969 | 160166 | /* If the next character is a digit, this is a floating point |
| 159970 | 160167 | ** number that begins with ".". Fall thru into the next case */ |
| 160168 | + /* no break */ deliberate_fall_through | |
| 159971 | 160169 | } |
| 159972 | 160170 | case CC_DIGIT: { |
| 159973 | 160171 | testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); |
| 159974 | 160172 | testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); |
| 159975 | 160173 | testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); |
| @@ -160070,10 +160268,11 @@ | ||
| 160070 | 160268 | return i; |
| 160071 | 160269 | } |
| 160072 | 160270 | #endif |
| 160073 | 160271 | /* If it is not a BLOB literal, then it must be an ID, since no |
| 160074 | 160272 | ** SQL keywords start with the letter 'x'. Fall through */ |
| 160273 | + /* no break */ deliberate_fall_through | |
| 160075 | 160274 | } |
| 160076 | 160275 | case CC_ID: { |
| 160077 | 160276 | i = 1; |
| 160078 | 160277 | break; |
| 160079 | 160278 | } |
| @@ -162000,11 +162199,11 @@ | ||
| 162000 | 162199 | if( !sqlite3SafetyCheckSickOrOk(db) ){ |
| 162001 | 162200 | return SQLITE_MISUSE_BKPT; |
| 162002 | 162201 | } |
| 162003 | 162202 | sqlite3_mutex_enter(db->mutex); |
| 162004 | 162203 | if( db->mTrace & SQLITE_TRACE_CLOSE ){ |
| 162005 | - db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); | |
| 162204 | + db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); | |
| 162006 | 162205 | } |
| 162007 | 162206 | |
| 162008 | 162207 | /* Force xDisconnect calls on all virtual tables */ |
| 162009 | 162208 | disconnectAllVtab(db); |
| 162010 | 162209 | |
| @@ -162889,11 +163088,11 @@ | ||
| 162889 | 163088 | } |
| 162890 | 163089 | #endif |
| 162891 | 163090 | sqlite3_mutex_enter(db->mutex); |
| 162892 | 163091 | pOld = db->pTraceArg; |
| 162893 | 163092 | db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; |
| 162894 | - db->xTrace = (int(*)(u32,void*,void*,void*))xTrace; | |
| 163093 | + db->trace.xLegacy = xTrace; | |
| 162895 | 163094 | db->pTraceArg = pArg; |
| 162896 | 163095 | sqlite3_mutex_leave(db->mutex); |
| 162897 | 163096 | return pOld; |
| 162898 | 163097 | } |
| 162899 | 163098 | #endif /* SQLITE_OMIT_DEPRECATED */ |
| @@ -162913,11 +163112,11 @@ | ||
| 162913 | 163112 | #endif |
| 162914 | 163113 | sqlite3_mutex_enter(db->mutex); |
| 162915 | 163114 | if( mTrace==0 ) xTrace = 0; |
| 162916 | 163115 | if( xTrace==0 ) mTrace = 0; |
| 162917 | 163116 | db->mTrace = mTrace; |
| 162918 | - db->xTrace = xTrace; | |
| 163117 | + db->trace.xV2 = xTrace; | |
| 162919 | 163118 | db->pTraceArg = pArg; |
| 162920 | 163119 | sqlite3_mutex_leave(db->mutex); |
| 162921 | 163120 | return SQLITE_OK; |
| 162922 | 163121 | } |
| 162923 | 163122 | |
| @@ -164889,10 +165088,16 @@ | ||
| 164889 | 165088 | /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int); |
| 164890 | 165089 | ** |
| 164891 | 165090 | ** Set or clear a flag that causes SQLite to verify that type, name, |
| 164892 | 165091 | ** and tbl_name fields of the sqlite_schema table. This is normally |
| 164893 | 165092 | ** on, but it is sometimes useful to turn it off for testing. |
| 165093 | + ** | |
| 165094 | + ** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the | |
| 165095 | + ** verification of rootpage numbers when parsing the schema. This | |
| 165096 | + ** is useful to make it easier to reach strange internal error states | |
| 165097 | + ** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled | |
| 165098 | + ** in production. | |
| 164894 | 165099 | */ |
| 164895 | 165100 | case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: { |
| 164896 | 165101 | sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int); |
| 164897 | 165102 | break; |
| 164898 | 165103 | } |
| @@ -166503,10 +166708,12 @@ | ||
| 166503 | 166708 | # define TESTONLY(X) |
| 166504 | 166709 | #endif |
| 166505 | 166710 | |
| 166506 | 166711 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 166507 | 166712 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 166713 | + | |
| 166714 | +#define deliberate_fall_through | |
| 166508 | 166715 | |
| 166509 | 166716 | #endif /* SQLITE_AMALGAMATION */ |
| 166510 | 166717 | |
| 166511 | 166718 | #ifdef SQLITE_DEBUG |
| 166512 | 166719 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -170146,11 +170353,11 @@ | ||
| 170146 | 170353 | }else if( p->zLanguageid==0 ){ |
| 170147 | 170354 | sqlite3_result_int(pCtx, 0); |
| 170148 | 170355 | break; |
| 170149 | 170356 | }else{ |
| 170150 | 170357 | iCol = p->nColumn; |
| 170151 | - /* fall-through */ | |
| 170358 | + /* no break */ deliberate_fall_through | |
| 170152 | 170359 | } |
| 170153 | 170360 | |
| 170154 | 170361 | default: |
| 170155 | 170362 | /* A user column. Or, if this is a full-table scan, possibly the |
| 170156 | 170363 | ** language-id column. Seek the cursor. */ |
| @@ -170389,13 +170596,17 @@ | ||
| 170389 | 170596 | } |
| 170390 | 170597 | if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; |
| 170391 | 170598 | |
| 170392 | 170599 | switch( nVal ){ |
| 170393 | 170600 | case 6: nToken = sqlite3_value_int(apVal[5]); |
| 170601 | + /* no break */ deliberate_fall_through | |
| 170394 | 170602 | case 5: iCol = sqlite3_value_int(apVal[4]); |
| 170603 | + /* no break */ deliberate_fall_through | |
| 170395 | 170604 | case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); |
| 170605 | + /* no break */ deliberate_fall_through | |
| 170396 | 170606 | case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); |
| 170607 | + /* no break */ deliberate_fall_through | |
| 170397 | 170608 | case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); |
| 170398 | 170609 | } |
| 170399 | 170610 | if( !zEllipsis || !zEnd || !zStart ){ |
| 170400 | 170611 | sqlite3_result_error_nomem(pContext); |
| 170401 | 170612 | }else if( nToken==0 ){ |
| @@ -172498,11 +172709,12 @@ | ||
| 172498 | 172709 | ** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK ); |
| 172499 | 172710 | */ |
| 172500 | 172711 | fts3EvalRestart(pCsr, pRoot, &rc); |
| 172501 | 172712 | do { |
| 172502 | 172713 | fts3EvalNextRow(pCsr, pRoot, &rc); |
| 172503 | - assert( pRoot->bEof==0 ); | |
| 172714 | + assert_fts3_nc( pRoot->bEof==0 ); | |
| 172715 | + if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB; | |
| 172504 | 172716 | }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); |
| 172505 | 172717 | } |
| 172506 | 172718 | } |
| 172507 | 172719 | return rc; |
| 172508 | 172720 | } |
| @@ -176607,11 +176819,12 @@ | ||
| 176607 | 176819 | rc = fts3tokQueryTokenizer((Fts3Hash*)pHash, zModule, &pMod, pzErr); |
| 176608 | 176820 | } |
| 176609 | 176821 | |
| 176610 | 176822 | assert( (rc==SQLITE_OK)==(pMod!=0) ); |
| 176611 | 176823 | if( rc==SQLITE_OK ){ |
| 176612 | - const char * const *azArg = (const char * const *)&azDequote[1]; | |
| 176824 | + const char * const *azArg = 0; | |
| 176825 | + if( nDequote>1 ) azArg = (const char * const *)&azDequote[1]; | |
| 176613 | 176826 | rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok); |
| 176614 | 176827 | } |
| 176615 | 176828 | |
| 176616 | 176829 | if( rc==SQLITE_OK ){ |
| 176617 | 176830 | pTab = (Fts3tokTable *)sqlite3_malloc(sizeof(Fts3tokTable)); |
| @@ -185252,10 +185465,14 @@ | ||
| 185252 | 185465 | #ifndef LARGEST_INT64 |
| 185253 | 185466 | # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) |
| 185254 | 185467 | # define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) |
| 185255 | 185468 | #endif |
| 185256 | 185469 | |
| 185470 | +#ifndef deliberate_fall_through | |
| 185471 | +# define deliberate_fall_through | |
| 185472 | +#endif | |
| 185473 | + | |
| 185257 | 185474 | /* |
| 185258 | 185475 | ** Versions of isspace(), isalnum() and isdigit() to which it is safe |
| 185259 | 185476 | ** to pass signed char values. |
| 185260 | 185477 | */ |
| 185261 | 185478 | #ifdef sqlite3Isdigit |
| @@ -185670,11 +185887,11 @@ | ||
| 185670 | 185887 | case JSON_STRING: { |
| 185671 | 185888 | if( pNode->jnFlags & JNODE_RAW ){ |
| 185672 | 185889 | jsonAppendString(pOut, pNode->u.zJContent, pNode->n); |
| 185673 | 185890 | break; |
| 185674 | 185891 | } |
| 185675 | - /* Fall through into the next case */ | |
| 185892 | + /* no break */ deliberate_fall_through | |
| 185676 | 185893 | } |
| 185677 | 185894 | case JSON_REAL: |
| 185678 | 185895 | case JSON_INT: { |
| 185679 | 185896 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 185680 | 185897 | break; |
| @@ -185811,11 +186028,11 @@ | ||
| 185811 | 186028 | } |
| 185812 | 186029 | if( pNode->u.zJContent[0]=='-' ){ i = -i; } |
| 185813 | 186030 | sqlite3_result_int64(pCtx, i); |
| 185814 | 186031 | int_done: |
| 185815 | 186032 | break; |
| 185816 | - int_as_real: /* fall through to real */; | |
| 186033 | + int_as_real: i=0; /* no break */ deliberate_fall_through | |
| 185817 | 186034 | } |
| 185818 | 186035 | case JSON_REAL: { |
| 185819 | 186036 | double r; |
| 185820 | 186037 | #ifdef SQLITE_AMALGAMATION |
| 185821 | 186038 | const char *z = pNode->u.zJContent; |
| @@ -187514,10 +187731,11 @@ | ||
| 187514 | 187731 | jsonResult(&x); |
| 187515 | 187732 | break; |
| 187516 | 187733 | } |
| 187517 | 187734 | /* For json_each() path and root are the same so fall through |
| 187518 | 187735 | ** into the root case */ |
| 187736 | + /* no break */ deliberate_fall_through | |
| 187519 | 187737 | } |
| 187520 | 187738 | default: { |
| 187521 | 187739 | const char *zRoot = p->zRoot; |
| 187522 | 187740 | if( zRoot==0 ) zRoot = "$"; |
| 187523 | 187741 | sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); |
| @@ -187921,10 +188139,11 @@ | ||
| 187921 | 188139 | #endif |
| 187922 | 188140 | |
| 187923 | 188141 | /* #include <string.h> */ |
| 187924 | 188142 | /* #include <stdio.h> */ |
| 187925 | 188143 | /* #include <assert.h> */ |
| 188144 | +/* #include <stdlib.h> */ | |
| 187926 | 188145 | |
| 187927 | 188146 | /* The following macro is used to suppress compiler warnings. |
| 187928 | 188147 | */ |
| 187929 | 188148 | #ifndef UNUSED_PARAMETER |
| 187930 | 188149 | # define UNUSED_PARAMETER(x) (void)(x) |
| @@ -188258,10 +188477,27 @@ | ||
| 188258 | 188477 | */ |
| 188259 | 188478 | #ifndef SQLITE_AMALGAMATION |
| 188260 | 188479 | # define testcase(X) |
| 188261 | 188480 | #endif |
| 188262 | 188481 | |
| 188482 | +/* | |
| 188483 | +** Make sure that the compiler intrinsics we desire are enabled when | |
| 188484 | +** compiling with an appropriate version of MSVC unless prevented by | |
| 188485 | +** the SQLITE_DISABLE_INTRINSIC define. | |
| 188486 | +*/ | |
| 188487 | +#if !defined(SQLITE_DISABLE_INTRINSIC) | |
| 188488 | +# if defined(_MSC_VER) && _MSC_VER>=1400 | |
| 188489 | +# if !defined(_WIN32_WCE) | |
| 188490 | +/* # include <intrin.h> */ | |
| 188491 | +# pragma intrinsic(_byteswap_ulong) | |
| 188492 | +# pragma intrinsic(_byteswap_uint64) | |
| 188493 | +# else | |
| 188494 | +/* # include <cmnintrin.h> */ | |
| 188495 | +# endif | |
| 188496 | +# endif | |
| 188497 | +#endif | |
| 188498 | + | |
| 188263 | 188499 | /* |
| 188264 | 188500 | ** Macros to determine whether the machine is big or little endian, |
| 188265 | 188501 | ** and whether or not that determination is run-time or compile-time. |
| 188266 | 188502 | ** |
| 188267 | 188503 | ** For best performance, an attempt is made to guess at the byte-order |
| @@ -205152,12 +205388,17 @@ | ||
| 205152 | 205388 | memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); |
| 205153 | 205389 | p->in.iNext += nCopy; |
| 205154 | 205390 | } |
| 205155 | 205391 | |
| 205156 | 205392 | p->apValue = (sqlite3_value**)p->tblhdr.aBuf; |
| 205157 | - p->abPK = (u8*)&p->apValue[p->nCol*2]; | |
| 205158 | - p->zTab = (char*)&p->abPK[p->nCol]; | |
| 205393 | + if( p->apValue==0 ){ | |
| 205394 | + p->abPK = 0; | |
| 205395 | + p->zTab = 0; | |
| 205396 | + }else{ | |
| 205397 | + p->abPK = (u8*)&p->apValue[p->nCol*2]; | |
| 205398 | + p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0; | |
| 205399 | + } | |
| 205159 | 205400 | return (p->rc = rc); |
| 205160 | 205401 | } |
| 205161 | 205402 | |
| 205162 | 205403 | /* |
| 205163 | 205404 | ** Advance the changeset iterator to the next change. |
| @@ -225482,11 +225723,11 @@ | ||
| 225482 | 225723 | int nArg, /* Number of args */ |
| 225483 | 225724 | sqlite3_value **apUnused /* Function arguments */ |
| 225484 | 225725 | ){ |
| 225485 | 225726 | assert( nArg==0 ); |
| 225486 | 225727 | UNUSED_PARAM2(nArg, apUnused); |
| 225487 | - sqlite3_result_text(pCtx, "fts5: 2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3", -1, SQLITE_TRANSIENT); | |
| 225728 | + sqlite3_result_text(pCtx, "fts5: 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f", -1, SQLITE_TRANSIENT); | |
| 225488 | 225729 | } |
| 225489 | 225730 | |
| 225490 | 225731 | /* |
| 225491 | 225732 | ** Return true if zName is the extension on one of the shadow tables used |
| 225492 | 225733 | ** by this module. |
| @@ -230265,12 +230506,12 @@ | ||
| 230265 | 230506 | } |
| 230266 | 230507 | #endif /* SQLITE_CORE */ |
| 230267 | 230508 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 230268 | 230509 | |
| 230269 | 230510 | /************** End of stmt.c ************************************************/ |
| 230270 | -#if __LINE__!=230270 | |
| 230511 | +#if __LINE__!=230511 | |
| 230271 | 230512 | #undef SQLITE_SOURCE_ID |
| 230272 | -#define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829alt2" | |
| 230513 | +#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0alt2" | |
| 230273 | 230514 | #endif |
| 230274 | 230515 | /* Return the source-id for this library */ |
| 230275 | 230516 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 230276 | 230517 | /************************** End of sqlite3.c ******************************/ |
| 230277 | 230518 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -997,10 +997,19 @@ | |
| 997 | |
| 998 | #if defined(__OpenBSD__) && !defined(_BSD_SOURCE) |
| 999 | # define _BSD_SOURCE |
| 1000 | #endif |
| 1001 | |
| 1002 | /* |
| 1003 | ** For MinGW, check to see if we can include the header file containing its |
| 1004 | ** version information, among other things. Normally, this internal MinGW |
| 1005 | ** header file would [only] be included automatically by other MinGW header |
| 1006 | ** files; however, the contained version information is now required by this |
| @@ -1162,11 +1171,11 @@ | |
| 1162 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1163 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1164 | */ |
| 1165 | #define SQLITE_VERSION "3.33.0" |
| 1166 | #define SQLITE_VERSION_NUMBER 3033000 |
| 1167 | #define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3" |
| 1168 | |
| 1169 | /* |
| 1170 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1171 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1172 | ** |
| @@ -14532,11 +14541,11 @@ | |
| 14532 | # define SELECTTRACE_ENABLED 0 |
| 14533 | #endif |
| 14534 | #if defined(SQLITE_ENABLE_SELECTTRACE) |
| 14535 | # define SELECTTRACE_ENABLED 1 |
| 14536 | # define SELECTTRACE(K,P,S,X) \ |
| 14537 | if(sqlite3SelectTrace&(K)) \ |
| 14538 | sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ |
| 14539 | sqlite3DebugPrintf X |
| 14540 | #else |
| 14541 | # define SELECTTRACE(K,P,S,X) |
| 14542 | # define SELECTTRACE_ENABLED 0 |
| @@ -14595,11 +14604,11 @@ | |
| 14595 | ** one parameter that destructors normally want. So we have to introduce |
| 14596 | ** this magic value that the code knows to handle differently. Any |
| 14597 | ** pointer will work here as long as it is distinct from SQLITE_STATIC |
| 14598 | ** and SQLITE_TRANSIENT. |
| 14599 | */ |
| 14600 | #define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) |
| 14601 | |
| 14602 | /* |
| 14603 | ** When SQLITE_OMIT_WSD is defined, it means that the target platform does |
| 14604 | ** not support Writable Static Data (WSD) such as global and static variables. |
| 14605 | ** All variables must either be on the stack or dynamically allocated from |
| @@ -14735,10 +14744,257 @@ | |
| 14735 | /* |
| 14736 | ** Defer sourcing vdbe.h and btree.h until after the "u8" and |
| 14737 | ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque |
| 14738 | ** pointer types (i.e. FuncDef) defined above. |
| 14739 | */ |
| 14740 | /************** Include btree.h in the middle of sqliteInt.h *****************/ |
| 14741 | /************** Begin file btree.h *******************************************/ |
| 14742 | /* |
| 14743 | ** 2001 September 15 |
| 14744 | ** |
| @@ -14810,12 +15066,12 @@ | |
| 14810 | SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); |
| 14811 | #endif |
| 14812 | SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned); |
| 14813 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 14814 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 14815 | SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); |
| 14816 | SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); |
| 14817 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 14818 | SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); |
| 14819 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 14820 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 14821 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| @@ -14823,11 +15079,11 @@ | |
| 14823 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char*); |
| 14824 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); |
| 14825 | SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); |
| 14826 | SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); |
| 14827 | SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); |
| 14828 | SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); |
| 14829 | SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); |
| 14830 | SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); |
| 14831 | SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); |
| 14832 | SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); |
| 14833 | SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); |
| @@ -14964,11 +15220,11 @@ | |
| 14964 | #define BTREE_WRCSR 0x00000004 /* read-write cursor */ |
| 14965 | #define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */ |
| 14966 | |
| 14967 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 14968 | Btree*, /* BTree containing table to open */ |
| 14969 | int iTable, /* Index of root page */ |
| 14970 | int wrFlag, /* 1 for writing. 0 for read-only */ |
| 14971 | struct KeyInfo*, /* First argument to compare function */ |
| 14972 | BtCursor *pCursor /* Space to write cursor structure */ |
| 14973 | ); |
| 14974 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| @@ -15055,11 +15311,11 @@ | |
| 15055 | SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); |
| 15056 | SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); |
| 15057 | SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); |
| 15058 | SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); |
| 15059 | |
| 15060 | SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,int*aRoot,int nRoot,int,int*); |
| 15061 | SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); |
| 15062 | SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); |
| 15063 | |
| 15064 | #ifndef SQLITE_OMIT_INCRBLOB |
| 15065 | SQLITE_PRIVATE int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*); |
| @@ -15192,11 +15448,11 @@ | |
| 15192 | sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */ |
| 15193 | CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ |
| 15194 | Mem *pMem; /* Used when p4type is P4_MEM */ |
| 15195 | VTable *pVtab; /* Used when p4type is P4_VTAB */ |
| 15196 | KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ |
| 15197 | int *ai; /* Used when p4type is P4_INTARRAY */ |
| 15198 | SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ |
| 15199 | Table *pTab; /* Used when p4type is P4_TABLE */ |
| 15200 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 15201 | Expr *pExpr; /* Used when p4type is P4_EXPR */ |
| 15202 | #endif |
| @@ -15756,257 +16012,10 @@ | |
| 15756 | #endif |
| 15757 | |
| 15758 | #endif /* SQLITE_VDBE_H */ |
| 15759 | |
| 15760 | /************** End of vdbe.h ************************************************/ |
| 15761 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 15762 | /************** Include pager.h in the middle of sqliteInt.h *****************/ |
| 15763 | /************** Begin file pager.h *******************************************/ |
| 15764 | /* |
| 15765 | ** 2001 September 15 |
| 15766 | ** |
| 15767 | ** The author disclaims copyright to this source code. In place of |
| 15768 | ** a legal notice, here is a blessing: |
| 15769 | ** |
| 15770 | ** May you do good and not evil. |
| 15771 | ** May you find forgiveness for yourself and forgive others. |
| 15772 | ** May you share freely, never taking more than you give. |
| 15773 | ** |
| 15774 | ************************************************************************* |
| 15775 | ** This header file defines the interface that the sqlite page cache |
| 15776 | ** subsystem. The page cache subsystem reads and writes a file a page |
| 15777 | ** at a time and provides a journal for rollback. |
| 15778 | */ |
| 15779 | |
| 15780 | #ifndef SQLITE_PAGER_H |
| 15781 | #define SQLITE_PAGER_H |
| 15782 | |
| 15783 | /* |
| 15784 | ** Default maximum size for persistent journal files. A negative |
| 15785 | ** value means no limit. This value may be overridden using the |
| 15786 | ** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". |
| 15787 | */ |
| 15788 | #ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT |
| 15789 | #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 |
| 15790 | #endif |
| 15791 | |
| 15792 | /* |
| 15793 | ** The type used to represent a page number. The first page in a file |
| 15794 | ** is called page 1. 0 is used to represent "not a page". |
| 15795 | */ |
| 15796 | typedef u32 Pgno; |
| 15797 | |
| 15798 | /* |
| 15799 | ** Each open file is managed by a separate instance of the "Pager" structure. |
| 15800 | */ |
| 15801 | typedef struct Pager Pager; |
| 15802 | |
| 15803 | /* |
| 15804 | ** Handle type for pages. |
| 15805 | */ |
| 15806 | typedef struct PgHdr DbPage; |
| 15807 | |
| 15808 | /* |
| 15809 | ** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is |
| 15810 | ** reserved for working around a windows/posix incompatibility). It is |
| 15811 | ** used in the journal to signify that the remainder of the journal file |
| 15812 | ** is devoted to storing a super-journal name - there are no more pages to |
| 15813 | ** roll back. See comments for function writeSuperJournal() in pager.c |
| 15814 | ** for details. |
| 15815 | */ |
| 15816 | #define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) |
| 15817 | |
| 15818 | /* |
| 15819 | ** Allowed values for the flags parameter to sqlite3PagerOpen(). |
| 15820 | ** |
| 15821 | ** NOTE: These values must match the corresponding BTREE_ values in btree.h. |
| 15822 | */ |
| 15823 | #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ |
| 15824 | #define PAGER_MEMORY 0x0002 /* In-memory database */ |
| 15825 | |
| 15826 | /* |
| 15827 | ** Valid values for the second argument to sqlite3PagerLockingMode(). |
| 15828 | */ |
| 15829 | #define PAGER_LOCKINGMODE_QUERY -1 |
| 15830 | #define PAGER_LOCKINGMODE_NORMAL 0 |
| 15831 | #define PAGER_LOCKINGMODE_EXCLUSIVE 1 |
| 15832 | |
| 15833 | /* |
| 15834 | ** Numeric constants that encode the journalmode. |
| 15835 | ** |
| 15836 | ** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY) |
| 15837 | ** are exposed in the API via the "PRAGMA journal_mode" command and |
| 15838 | ** therefore cannot be changed without a compatibility break. |
| 15839 | */ |
| 15840 | #define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ |
| 15841 | #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ |
| 15842 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 15843 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 15844 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 15845 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 15846 | #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ |
| 15847 | |
| 15848 | /* |
| 15849 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 15850 | */ |
| 15851 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| 15852 | #define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ |
| 15853 | |
| 15854 | /* |
| 15855 | ** Flags for sqlite3PagerSetFlags() |
| 15856 | ** |
| 15857 | ** Value constraints (enforced via assert()): |
| 15858 | ** PAGER_FULLFSYNC == SQLITE_FullFSync |
| 15859 | ** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync |
| 15860 | ** PAGER_CACHE_SPILL == SQLITE_CacheSpill |
| 15861 | */ |
| 15862 | #define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */ |
| 15863 | #define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */ |
| 15864 | #define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */ |
| 15865 | #define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */ |
| 15866 | #define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */ |
| 15867 | #define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */ |
| 15868 | #define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */ |
| 15869 | #define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */ |
| 15870 | #define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */ |
| 15871 | |
| 15872 | /* |
| 15873 | ** The remainder of this file contains the declarations of the functions |
| 15874 | ** that make up the Pager sub-system API. See source code comments for |
| 15875 | ** a detailed description of each routine. |
| 15876 | */ |
| 15877 | |
| 15878 | /* Open and close a Pager connection. */ |
| 15879 | SQLITE_PRIVATE int sqlite3PagerOpen( |
| 15880 | sqlite3_vfs*, |
| 15881 | Pager **ppPager, |
| 15882 | const char*, |
| 15883 | int, |
| 15884 | int, |
| 15885 | int, |
| 15886 | void(*)(DbPage*) |
| 15887 | ); |
| 15888 | SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*); |
| 15889 | SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); |
| 15890 | |
| 15891 | /* Functions used to configure a Pager object. */ |
| 15892 | SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); |
| 15893 | SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); |
| 15894 | SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); |
| 15895 | SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); |
| 15896 | SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); |
| 15897 | SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); |
| 15898 | SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); |
| 15899 | SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned); |
| 15900 | SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); |
| 15901 | SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); |
| 15902 | SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); |
| 15903 | SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); |
| 15904 | SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); |
| 15905 | SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); |
| 15906 | SQLITE_PRIVATE int sqlite3PagerFlush(Pager*); |
| 15907 | |
| 15908 | /* Functions used to obtain and release page references. */ |
| 15909 | SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); |
| 15910 | SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); |
| 15911 | SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); |
| 15912 | SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); |
| 15913 | SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); |
| 15914 | SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*); |
| 15915 | |
| 15916 | /* Operations on page references. */ |
| 15917 | SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); |
| 15918 | SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); |
| 15919 | SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); |
| 15920 | SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); |
| 15921 | SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); |
| 15922 | SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); |
| 15923 | |
| 15924 | /* Functions used to manage pager transactions and savepoints. */ |
| 15925 | SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); |
| 15926 | SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); |
| 15927 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); |
| 15928 | SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); |
| 15929 | SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper); |
| 15930 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); |
| 15931 | SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); |
| 15932 | SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); |
| 15933 | SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); |
| 15934 | SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); |
| 15935 | |
| 15936 | #ifndef SQLITE_OMIT_WAL |
| 15937 | SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); |
| 15938 | SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); |
| 15939 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); |
| 15940 | SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); |
| 15941 | SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
| 15942 | # ifdef SQLITE_ENABLE_SNAPSHOT |
| 15943 | SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); |
| 15944 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); |
| 15945 | SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); |
| 15946 | SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 15947 | SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); |
| 15948 | # endif |
| 15949 | #endif |
| 15950 | |
| 15951 | #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) |
| 15952 | SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); |
| 15953 | SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); |
| 15954 | #else |
| 15955 | # define sqlite3PagerWalWriteLock(y,z) SQLITE_OK |
| 15956 | # define sqlite3PagerWalDb(x,y) |
| 15957 | #endif |
| 15958 | |
| 15959 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 15960 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); |
| 15961 | #endif |
| 15962 | |
| 15963 | #ifdef SQLITE_ENABLE_ZIPVFS |
| 15964 | SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); |
| 15965 | #endif |
| 15966 | |
| 15967 | /* Functions used to query pager state and configuration. */ |
| 15968 | SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); |
| 15969 | SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); |
| 15970 | #ifdef SQLITE_DEBUG |
| 15971 | SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); |
| 15972 | #endif |
| 15973 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 15974 | SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); |
| 15975 | SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 15976 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| 15977 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); |
| 15978 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 15979 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 15980 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 15981 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 15982 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); |
| 15983 | SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); |
| 15984 | |
| 15985 | /* Functions used to truncate the database file. */ |
| 15986 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 15987 | |
| 15988 | SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| 15989 | |
| 15990 | /* Functions to support testing and debugging. */ |
| 15991 | #if !defined(NDEBUG) || defined(SQLITE_TEST) |
| 15992 | SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); |
| 15993 | SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); |
| 15994 | #endif |
| 15995 | #ifdef SQLITE_TEST |
| 15996 | SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); |
| 15997 | SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); |
| 15998 | void disable_simulated_io_errors(void); |
| 15999 | void enable_simulated_io_errors(void); |
| 16000 | #else |
| 16001 | # define disable_simulated_io_errors() |
| 16002 | # define enable_simulated_io_errors() |
| 16003 | #endif |
| 16004 | |
| 16005 | #endif /* SQLITE_PAGER_H */ |
| 16006 | |
| 16007 | /************** End of pager.h ***********************************************/ |
| 16008 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 16009 | /************** Include pcache.h in the middle of sqliteInt.h ****************/ |
| 16010 | /************** Begin file pcache.h ******************************************/ |
| 16011 | /* |
| 16012 | ** 2008 August 05 |
| @@ -16843,11 +16852,11 @@ | |
| 16843 | int nChange; /* Value returned by sqlite3_changes() */ |
| 16844 | int nTotalChange; /* Value returned by sqlite3_total_changes() */ |
| 16845 | int aLimit[SQLITE_N_LIMIT]; /* Limits */ |
| 16846 | int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ |
| 16847 | struct sqlite3InitInfo { /* Information used during initialization */ |
| 16848 | int newTnum; /* Rootpage of table being initialized */ |
| 16849 | u8 iDb; /* Which db file is being initialized */ |
| 16850 | u8 busy; /* TRUE if currently initializing */ |
| 16851 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 16852 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 16853 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| @@ -16858,11 +16867,14 @@ | |
| 16858 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 16859 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| 16860 | int nVDestroy; /* Number of active OP_VDestroy operations */ |
| 16861 | int nExtension; /* Number of loaded extensions */ |
| 16862 | void **aExtension; /* Array of shared library handles */ |
| 16863 | int (*xTrace)(u32,void*,void*,void*); /* Trace function */ |
| 16864 | void *pTraceArg; /* Argument to the trace function */ |
| 16865 | #ifndef SQLITE_OMIT_DEPRECATED |
| 16866 | void (*xProfile)(void*,const char*,u64); /* Profiling function */ |
| 16867 | void *pProfileArg; /* Argument to profile function */ |
| 16868 | #endif |
| @@ -17482,11 +17494,11 @@ | |
| 17482 | Select *pSelect; /* NULL for tables. Points to definition if a view. */ |
| 17483 | FKey *pFKey; /* Linked list of all foreign keys in this table */ |
| 17484 | char *zColAff; /* String defining the affinity of each column */ |
| 17485 | ExprList *pCheck; /* All CHECK constraints */ |
| 17486 | /* ... also used as column name list in a VIEW */ |
| 17487 | int tnum; /* Root BTree page for this table */ |
| 17488 | u32 nTabRef; /* Number of pointers to this Table */ |
| 17489 | u32 tabFlags; /* Mask of TF_* values */ |
| 17490 | i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ |
| 17491 | i16 nCol; /* Number of columns in this table */ |
| 17492 | i16 nNVCol; /* Number of columns that are not VIRTUAL */ |
| @@ -17775,11 +17787,11 @@ | |
| 17775 | Schema *pSchema; /* Schema containing this index */ |
| 17776 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 17777 | const char **azColl; /* Array of collation sequence names for index */ |
| 17778 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 17779 | ExprList *aColExpr; /* Column expressions */ |
| 17780 | int tnum; /* DB Page containing root of this index */ |
| 17781 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 17782 | u16 nKeyCol; /* Number of columns forming the key */ |
| 17783 | u16 nColumn; /* Number of columns stored in the index */ |
| 17784 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 17785 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| @@ -17901,15 +17913,10 @@ | |
| 17901 | int nFunc; /* Number of entries in aFunc[] */ |
| 17902 | u32 selId; /* Select to which this AggInfo belongs */ |
| 17903 | AggInfo *pNext; /* Next in list of them all */ |
| 17904 | }; |
| 17905 | |
| 17906 | /* |
| 17907 | ** Value for AggInfo.iAggMagic when the structure is valid |
| 17908 | */ |
| 17909 | #define AggInfoMagic 0x2059e99e |
| 17910 | |
| 17911 | /* |
| 17912 | ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. |
| 17913 | ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater |
| 17914 | ** than 32767 we have to make it 32-bit. 16-bit is preferred because |
| 17915 | ** it uses less memory in the Expr object, which is a big memory user |
| @@ -18744,13 +18751,11 @@ | |
| 18744 | |
| 18745 | Token sLastToken; /* The last token parsed */ |
| 18746 | ynVar nVar; /* Number of '?' variables seen in the SQL so far */ |
| 18747 | u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ |
| 18748 | u8 explain; /* True if the EXPLAIN flag is found on the query */ |
| 18749 | #if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)) |
| 18750 | u8 eParseMode; /* PARSE_MODE_XXX constant */ |
| 18751 | #endif |
| 18752 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 18753 | int nVtabLock; /* Number of virtual tables to lock */ |
| 18754 | #endif |
| 18755 | int nHeight; /* Expression tree height of current sub-select */ |
| 18756 | #ifndef SQLITE_OMIT_EXPLAIN |
| @@ -18990,10 +18995,11 @@ | |
| 18990 | char **pzErrMsg; /* Error message stored here */ |
| 18991 | int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ |
| 18992 | int rc; /* Result code stored here */ |
| 18993 | u32 mInitFlags; /* Flags controlling error messages */ |
| 18994 | u32 nInitRow; /* Number of rows processed */ |
| 18995 | } InitData; |
| 18996 | |
| 18997 | /* |
| 18998 | ** Allowed values for mInitFlags |
| 18999 | */ |
| @@ -19823,12 +19829,14 @@ | |
| 19823 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 19824 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 19825 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 19826 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 19827 | SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); |
| 19828 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 19829 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 19830 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 19831 | #ifndef SQLITE_OMIT_UTF16 |
| 19832 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 19833 | #endif |
| 19834 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| @@ -19944,19 +19952,19 @@ | |
| 19944 | SQLITE_PRIVATE const char sqlite3StrBINARY[]; |
| 19945 | SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; |
| 19946 | SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; |
| 19947 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; |
| 19948 | SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; |
| 19949 | SQLITE_PRIVATE u32 sqlite3SelectTrace; |
| 19950 | #ifndef SQLITE_OMIT_WSD |
| 19951 | SQLITE_PRIVATE int sqlite3PendingByte; |
| 19952 | #endif |
| 19953 | #endif /* SQLITE_AMALGAMATION */ |
| 19954 | #ifdef VDBE_PROFILE |
| 19955 | SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt; |
| 19956 | #endif |
| 19957 | SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int); |
| 19958 | SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); |
| 19959 | SQLITE_PRIVATE void sqlite3AlterFunctions(void); |
| 19960 | SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); |
| 19961 | SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); |
| 19962 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| @@ -20066,11 +20074,11 @@ | |
| 20066 | #else |
| 20067 | # define sqlite3CloseExtensions(X) |
| 20068 | #endif |
| 20069 | |
| 20070 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 20071 | SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, int, u8, const char *); |
| 20072 | #else |
| 20073 | #define sqlite3TableLock(v,w,x,y,z) |
| 20074 | #endif |
| 20075 | |
| 20076 | #ifdef SQLITE_TEST |
| @@ -20661,11 +20669,11 @@ | |
| 20661 | #endif |
| 20662 | |
| 20663 | /* |
| 20664 | ** Flags for select tracing and the ".selecttrace" macro of the CLI |
| 20665 | */ |
| 20666 | /**/ u32 sqlite3SelectTrace = 0; |
| 20667 | |
| 20668 | /* #include "opcodes.h" */ |
| 20669 | /* |
| 20670 | ** Properties of opcodes. The OPFLG_INITIALIZER macro is |
| 20671 | ** created by mkopcodeh.awk during compilation. Data is obtained |
| @@ -20788,11 +20796,11 @@ | |
| 20788 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 20789 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 20790 | Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 20791 | Btree *pBtx; /* Separate file holding temporary table */ |
| 20792 | i64 seqCount; /* Sequence counter */ |
| 20793 | int *aAltMap; /* Mapping from table to index column numbers */ |
| 20794 | |
| 20795 | /* Cached OP_Column parse information is only valid if cacheStatus matches |
| 20796 | ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of |
| 20797 | ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that |
| 20798 | ** the cache is out of date. */ |
| @@ -21184,11 +21192,11 @@ | |
| 21184 | */ |
| 21185 | SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); |
| 21186 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); |
| 21187 | void sqliteVdbePopStack(Vdbe*,int); |
| 21188 | SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); |
| 21189 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); |
| 21190 | SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); |
| 21191 | SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); |
| 21192 | SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); |
| 21193 | SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); |
| 21194 | SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); |
| @@ -21660,11 +21668,11 @@ | |
| 21660 | ** pagers the database handle is connected to. *pHighwater is always set |
| 21661 | ** to zero. |
| 21662 | */ |
| 21663 | case SQLITE_DBSTATUS_CACHE_SPILL: |
| 21664 | op = SQLITE_DBSTATUS_CACHE_WRITE+1; |
| 21665 | /* Fall through into the next case */ |
| 21666 | case SQLITE_DBSTATUS_CACHE_HIT: |
| 21667 | case SQLITE_DBSTATUS_CACHE_MISS: |
| 21668 | case SQLITE_DBSTATUS_CACHE_WRITE:{ |
| 21669 | int i; |
| 21670 | int nRet = 0; |
| @@ -22816,12 +22824,12 @@ | |
| 22816 | break; |
| 22817 | } |
| 22818 | case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; |
| 22819 | case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; |
| 22820 | case 's': { |
| 22821 | sqlite3_snprintf(30,&z[j],"%lld", |
| 22822 | (i64)(x.iJD/1000 - 21086676*(i64)10000)); |
| 22823 | j += sqlite3Strlen30(&z[j]); |
| 22824 | break; |
| 22825 | } |
| 22826 | case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; |
| 22827 | case 'w': { |
| @@ -28537,15 +28545,15 @@ | |
| 28537 | assert( precision>=(-1) ); |
| 28538 | switch( xtype ){ |
| 28539 | case etPOINTER: |
| 28540 | flag_long = sizeof(char*)==sizeof(i64) ? 2 : |
| 28541 | sizeof(char*)==sizeof(long int) ? 1 : 0; |
| 28542 | /* Fall through into the next case */ |
| 28543 | case etORDINAL: |
| 28544 | case etRADIX: |
| 28545 | cThousand = 0; |
| 28546 | /* Fall through into the next case */ |
| 28547 | case etDECIMAL: |
| 28548 | if( infop->flags & FLAG_SIGNED ){ |
| 28549 | i64 v; |
| 28550 | if( bArgList ){ |
| 28551 | v = getIntArg(pArgList); |
| @@ -31770,10 +31778,34 @@ | |
| 31770 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 31771 | } |
| 31772 | #if defined(_MSC_VER) |
| 31773 | #pragma warning(default : 4756) |
| 31774 | #endif |
| 31775 | |
| 31776 | /* |
| 31777 | ** Compare the 19-character string zNum against the text representation |
| 31778 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 31779 | ** if zNum is less than, equal to, or greater than the string. |
| @@ -32011,13 +32043,31 @@ | |
| 32011 | ** Return a 32-bit integer value extracted from a string. If the |
| 32012 | ** string is not an integer, just return 0. |
| 32013 | */ |
| 32014 | SQLITE_PRIVATE int sqlite3Atoi(const char *z){ |
| 32015 | int x = 0; |
| 32016 | if( z ) sqlite3GetInt32(z, &x); |
| 32017 | return x; |
| 32018 | } |
| 32019 | |
| 32020 | /* |
| 32021 | ** The variable-length integer encoding is as follows: |
| 32022 | ** |
| 32023 | ** KEY: |
| @@ -39188,11 +39238,11 @@ | |
| 39188 | } |
| 39189 | #endif |
| 39190 | if( rc!=SQLITE_OK ){ |
| 39191 | if( h>=0 ) robust_close(pNew, h, __LINE__); |
| 39192 | }else{ |
| 39193 | pNew->pMethod = pLockingStyle; |
| 39194 | OpenCounter(+1); |
| 39195 | verifyDbFile(pNew); |
| 39196 | } |
| 39197 | return rc; |
| 39198 | } |
| @@ -46899,11 +46949,11 @@ | |
| 46899 | { |
| 46900 | sqlite3_free(zConverted); |
| 46901 | } |
| 46902 | |
| 46903 | sqlite3_free(zTmpname); |
| 46904 | pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod; |
| 46905 | pFile->pVfs = pVfs; |
| 46906 | pFile->h = h; |
| 46907 | if( isReadonly ){ |
| 46908 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46909 | } |
| @@ -48125,11 +48175,11 @@ | |
| 48125 | } |
| 48126 | memset(p, 0, sizeof(*p)); |
| 48127 | p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; |
| 48128 | assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ |
| 48129 | *pOutFlags = flags | SQLITE_OPEN_MEMORY; |
| 48130 | p->base.pMethods = &memdb_io_methods; |
| 48131 | p->szMax = sqlite3GlobalConfig.mxMemdbSize; |
| 48132 | return SQLITE_OK; |
| 48133 | } |
| 48134 | |
| 48135 | #if 0 /* Only used to delete rollback journals, super-journals, and WAL |
| @@ -52442,15 +52492,10 @@ | |
| 52442 | # define USEFETCH(x) ((x)->bUseFetch) |
| 52443 | #else |
| 52444 | # define USEFETCH(x) 0 |
| 52445 | #endif |
| 52446 | |
| 52447 | /* |
| 52448 | ** The maximum legal page number is (2^31 - 1). |
| 52449 | */ |
| 52450 | #define PAGER_MAX_PGNO 2147483647 |
| 52451 | |
| 52452 | /* |
| 52453 | ** The argument to this macro is a file descriptor (type sqlite3_file*). |
| 52454 | ** Return 0 if it is not open, or non-zero (but not 1) if it is. |
| 52455 | ** |
| 52456 | ** This is so that expressions can be written as: |
| @@ -54153,16 +54198,17 @@ | |
| 54153 | |
| 54154 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54155 | ** If successful, open the super-journal file for reading. |
| 54156 | */ |
| 54157 | pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); |
| 54158 | pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); |
| 54159 | if( !pSuper ){ |
| 54160 | rc = SQLITE_NOMEM_BKPT; |
| 54161 | }else{ |
| 54162 | const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); |
| 54163 | rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); |
| 54164 | } |
| 54165 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54166 | |
| 54167 | /* Load the entire super-journal file into space obtained from |
| 54168 | ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain |
| @@ -55419,11 +55465,11 @@ | |
| 55419 | ** Make no changes if mxPage is zero or negative. And never reduce the |
| 55420 | ** maximum page count below the current size of the database. |
| 55421 | ** |
| 55422 | ** Regardless of mxPage, return the current maximum page count. |
| 55423 | */ |
| 55424 | SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ |
| 55425 | if( mxPage>0 ){ |
| 55426 | pPager->mxPgno = mxPage; |
| 55427 | } |
| 55428 | assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ |
| 55429 | /* assert( pPager->mxPgno>=pPager->dbSize ); */ |
| @@ -57146,22 +57192,22 @@ | |
| 57146 | |
| 57147 | noContent = (flags & PAGER_GET_NOCONTENT)!=0; |
| 57148 | if( pPg->pPager && !noContent ){ |
| 57149 | /* In this case the pcache already contains an initialized copy of |
| 57150 | ** the page. Return without further ado. */ |
| 57151 | assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); |
| 57152 | pPager->aStat[PAGER_STAT_HIT]++; |
| 57153 | return SQLITE_OK; |
| 57154 | |
| 57155 | }else{ |
| 57156 | /* The pager cache has created a new page. Its content needs to |
| 57157 | ** be initialized. But first some error checks: |
| 57158 | ** |
| 57159 | ** (1) The maximum page number is 2^31 |
| 57160 | ** (2) Never try to fetch the locking page |
| 57161 | */ |
| 57162 | if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ |
| 57163 | rc = SQLITE_CORRUPT_BKPT; |
| 57164 | goto pager_acquire_err; |
| 57165 | } |
| 57166 | |
| 57167 | pPg->pPager = pPager; |
| @@ -59863,11 +59909,11 @@ | |
| 59863 | ** walIteratorFree() - Free an iterator. |
| 59864 | ** |
| 59865 | ** This functionality is used by the checkpoint code (see walCheckpoint()). |
| 59866 | */ |
| 59867 | struct WalIterator { |
| 59868 | int iPrior; /* Last result returned from the iterator */ |
| 59869 | int nSegment; /* Number of entries in aSegment[] */ |
| 59870 | struct WalSegment { |
| 59871 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 59872 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 59873 | u32 *aPgno; /* Array of page numbers. */ |
| @@ -59945,11 +59991,13 @@ | |
| 59945 | rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, |
| 59946 | pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] |
| 59947 | ); |
| 59948 | assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); |
| 59949 | testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); |
| 59950 | if( (rc&0xff)==SQLITE_READONLY ){ |
| 59951 | pWal->readOnly |= WAL_SHM_RDONLY; |
| 59952 | if( rc==SQLITE_READONLY ){ |
| 59953 | rc = SQLITE_OK; |
| 59954 | } |
| 59955 | } |
| @@ -60320,10 +60368,11 @@ | |
| 60320 | && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) |
| 60321 | && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) |
| 60322 | && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) |
| 60323 | && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) |
| 60324 | ); |
| 60325 | return iHash; |
| 60326 | } |
| 60327 | |
| 60328 | /* |
| 60329 | ** Return the page number associated with frame iFrame in this WAL. |
| @@ -60516,16 +60565,10 @@ | |
| 60516 | assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); |
| 60517 | assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); |
| 60518 | assert( pWal->writeLock ); |
| 60519 | iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; |
| 60520 | rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60521 | if( rc==SQLITE_OK ){ |
| 60522 | rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 60523 | if( rc!=SQLITE_OK ){ |
| 60524 | walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60525 | } |
| 60526 | } |
| 60527 | if( rc ){ |
| 60528 | return rc; |
| 60529 | } |
| 60530 | |
| 60531 | WALTRACE(("WAL%p: recovery begin...\n", pWal)); |
| @@ -60537,19 +60580,20 @@ | |
| 60537 | goto recovery_error; |
| 60538 | } |
| 60539 | |
| 60540 | if( nSize>WAL_HDRSIZE ){ |
| 60541 | u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ |
| 60542 | u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ |
| 60543 | int szFrame; /* Number of bytes in buffer aFrame[] */ |
| 60544 | u8 *aData; /* Pointer to data part of aFrame buffer */ |
| 60545 | int iFrame; /* Index of last frame read */ |
| 60546 | i64 iOffset; /* Next offset to read from log file */ |
| 60547 | int szPage; /* Page size according to the log */ |
| 60548 | u32 magic; /* Magic value read from WAL header */ |
| 60549 | u32 version; /* Magic value read from WAL header */ |
| 60550 | int isValid; /* True if this frame is valid */ |
| 60551 | |
| 60552 | /* Read in the WAL header. */ |
| 60553 | rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); |
| 60554 | if( rc!=SQLITE_OK ){ |
| 60555 | goto recovery_error; |
| @@ -60592,42 +60636,86 @@ | |
| 60592 | goto finished; |
| 60593 | } |
| 60594 | |
| 60595 | /* Malloc a buffer to read frames into. */ |
| 60596 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 60597 | aFrame = (u8 *)sqlite3_malloc64(szFrame); |
| 60598 | if( !aFrame ){ |
| 60599 | rc = SQLITE_NOMEM_BKPT; |
| 60600 | goto recovery_error; |
| 60601 | } |
| 60602 | aData = &aFrame[WAL_FRAME_HDRSIZE]; |
| 60603 | |
| 60604 | /* Read all frames from the log file. */ |
| 60605 | iFrame = 0; |
| 60606 | for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){ |
| 60607 | u32 pgno; /* Database page number for frame */ |
| 60608 | u32 nTruncate; /* dbsize field from frame header */ |
| 60609 | |
| 60610 | /* Read and decode the next log frame. */ |
| 60611 | iFrame++; |
| 60612 | rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); |
| 60613 | if( rc!=SQLITE_OK ) break; |
| 60614 | isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); |
| 60615 | if( !isValid ) break; |
| 60616 | rc = walIndexAppend(pWal, iFrame, pgno); |
| 60617 | if( rc!=SQLITE_OK ) break; |
| 60618 | |
| 60619 | /* If nTruncate is non-zero, this is a commit record. */ |
| 60620 | if( nTruncate ){ |
| 60621 | pWal->hdr.mxFrame = iFrame; |
| 60622 | pWal->hdr.nPage = nTruncate; |
| 60623 | pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); |
| 60624 | testcase( szPage<=32768 ); |
| 60625 | testcase( szPage>=65536 ); |
| 60626 | aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; |
| 60627 | aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; |
| 60628 | } |
| 60629 | } |
| 60630 | |
| 60631 | sqlite3_free(aFrame); |
| 60632 | } |
| 60633 | |
| @@ -60638,19 +60726,30 @@ | |
| 60638 | pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; |
| 60639 | pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; |
| 60640 | walIndexWriteHdr(pWal); |
| 60641 | |
| 60642 | /* Reset the checkpoint-header. This is safe because this thread is |
| 60643 | ** currently holding locks that exclude all other readers, writers and |
| 60644 | ** checkpointers. |
| 60645 | */ |
| 60646 | pInfo = walCkptInfo(pWal); |
| 60647 | pInfo->nBackfill = 0; |
| 60648 | pInfo->nBackfillAttempted = pWal->hdr.mxFrame; |
| 60649 | pInfo->aReadMark[0] = 0; |
| 60650 | for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 60651 | if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame; |
| 60652 | |
| 60653 | /* If more than one frame was recovered from the log file, report an |
| 60654 | ** event via sqlite3_log(). This is to help with identifying performance |
| 60655 | ** problems caused by applications routinely shutting down without |
| 60656 | ** checkpointing the log file. |
| @@ -60664,11 +60763,10 @@ | |
| 60664 | } |
| 60665 | |
| 60666 | recovery_error: |
| 60667 | WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); |
| 60668 | walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60669 | walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 60670 | return rc; |
| 60671 | } |
| 60672 | |
| 60673 | /* |
| 60674 | ** Close an open wal-index. |
| @@ -61312,14 +61410,22 @@ | |
| 61312 | i64 nReq = ((i64)mxPage * szPage); |
| 61313 | i64 nSize; /* Current size of database file */ |
| 61314 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); |
| 61315 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 61316 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 61317 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 61318 | } |
| 61319 | } |
| 61320 | |
| 61321 | |
| 61322 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 61323 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 61324 | i64 iOffset; |
| 61325 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| @@ -64048,11 +64154,12 @@ | |
| 64048 | Pgno nPage; /* Number of pages in the database */ |
| 64049 | int mxErr; /* Stop accumulating errors when this reaches zero */ |
| 64050 | int nErr; /* Number of messages written to zErrMsg so far */ |
| 64051 | int bOomFault; /* A memory allocation error has occurred */ |
| 64052 | const char *zPfx; /* Error message prefix */ |
| 64053 | int v1, v2; /* Values for up to two %d fields in zPfx */ |
| 64054 | StrAccum errMsg; /* Accumulate the error message text here */ |
| 64055 | u32 *heap; /* Min-heap used for analyzing cell coverage */ |
| 64056 | sqlite3 *db; /* Database connection running the check */ |
| 64057 | }; |
| 64058 | |
| @@ -66513,16 +66620,15 @@ | |
| 66513 | /* |
| 66514 | ** Return the size of the database file in pages. If there is any kind of |
| 66515 | ** error, return ((unsigned int)-1). |
| 66516 | */ |
| 66517 | static Pgno btreePagecount(BtShared *pBt){ |
| 66518 | assert( (pBt->nPage & 0x80000000)==0 || CORRUPT_DB ); |
| 66519 | return pBt->nPage; |
| 66520 | } |
| 66521 | SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){ |
| 66522 | assert( sqlite3BtreeHoldsMutex(p) ); |
| 66523 | return btreePagecount(p->pBt) & 0x7fffffff; |
| 66524 | } |
| 66525 | |
| 66526 | /* |
| 66527 | ** Get a page from the pager and initialize it. |
| 66528 | ** |
| @@ -67306,12 +67412,12 @@ | |
| 67306 | /* |
| 67307 | ** Set the maximum page count for a database if mxPage is positive. |
| 67308 | ** No changes are made if mxPage is 0 or negative. |
| 67309 | ** Regardless of the value of mxPage, return the maximum page count. |
| 67310 | */ |
| 67311 | SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){ |
| 67312 | int n; |
| 67313 | sqlite3BtreeEnter(p); |
| 67314 | n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); |
| 67315 | sqlite3BtreeLeave(p); |
| 67316 | return n; |
| 67317 | } |
| @@ -68746,11 +68852,11 @@ | |
| 68746 | ** It is assumed that the sqlite3BtreeCursorZero() has been called |
| 68747 | ** on pCur to initialize the memory space prior to invoking this routine. |
| 68748 | */ |
| 68749 | static int btreeCursor( |
| 68750 | Btree *p, /* The btree */ |
| 68751 | int iTable, /* Root page of table to open */ |
| 68752 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68753 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68754 | BtCursor *pCur /* Space for new cursor */ |
| 68755 | ){ |
| 68756 | BtShared *pBt = p->pBt; /* Shared b-tree handle */ |
| @@ -68789,21 +68895,21 @@ | |
| 68789 | } |
| 68790 | } |
| 68791 | |
| 68792 | /* Now that no other errors can occur, finish filling in the BtCursor |
| 68793 | ** variables and link the cursor into the BtShared list. */ |
| 68794 | pCur->pgnoRoot = (Pgno)iTable; |
| 68795 | pCur->iPage = -1; |
| 68796 | pCur->pKeyInfo = pKeyInfo; |
| 68797 | pCur->pBtree = p; |
| 68798 | pCur->pBt = pBt; |
| 68799 | pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; |
| 68800 | pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; |
| 68801 | /* If there are two or more cursors on the same btree, then all such |
| 68802 | ** cursors *must* have the BTCF_Multiple flag set. */ |
| 68803 | for(pX=pBt->pCursor; pX; pX=pX->pNext){ |
| 68804 | if( pX->pgnoRoot==(Pgno)iTable ){ |
| 68805 | pX->curFlags |= BTCF_Multiple; |
| 68806 | pCur->curFlags |= BTCF_Multiple; |
| 68807 | } |
| 68808 | } |
| 68809 | pCur->pNext = pBt->pCursor; |
| @@ -68811,11 +68917,11 @@ | |
| 68811 | pCur->eState = CURSOR_INVALID; |
| 68812 | return SQLITE_OK; |
| 68813 | } |
| 68814 | static int btreeCursorWithLock( |
| 68815 | Btree *p, /* The btree */ |
| 68816 | int iTable, /* Root page of table to open */ |
| 68817 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68818 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68819 | BtCursor *pCur /* Space for new cursor */ |
| 68820 | ){ |
| 68821 | int rc; |
| @@ -68824,11 +68930,11 @@ | |
| 68824 | sqlite3BtreeLeave(p); |
| 68825 | return rc; |
| 68826 | } |
| 68827 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 68828 | Btree *p, /* The btree */ |
| 68829 | int iTable, /* Root page of table to open */ |
| 68830 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68831 | struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ |
| 68832 | BtCursor *pCur /* Write new cursor here */ |
| 68833 | ){ |
| 68834 | if( p->sharable ){ |
| @@ -69249,10 +69355,11 @@ | |
| 69249 | } |
| 69250 | |
| 69251 | assert( rc==SQLITE_OK && amt>0 ); |
| 69252 | while( nextPage ){ |
| 69253 | /* If required, populate the overflow page-list cache. */ |
| 69254 | assert( pCur->aOverflow[iIdx]==0 |
| 69255 | || pCur->aOverflow[iIdx]==nextPage |
| 69256 | || CORRUPT_DB ); |
| 69257 | pCur->aOverflow[iIdx] = nextPage; |
| 69258 | |
| @@ -70664,10 +70771,14 @@ | |
| 70664 | */ |
| 70665 | if( nFree!=0 ){ |
| 70666 | u32 nLeaf; /* Initial number of leaf cells on trunk page */ |
| 70667 | |
| 70668 | iTrunk = get4byte(&pPage1->aData[32]); |
| 70669 | rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); |
| 70670 | if( rc!=SQLITE_OK ){ |
| 70671 | goto freepage_out; |
| 70672 | } |
| 70673 | |
| @@ -73468,11 +73579,11 @@ | |
| 73468 | ** flags might not work: |
| 73469 | ** |
| 73470 | ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys |
| 73471 | ** BTREE_ZERODATA Used for SQL indices |
| 73472 | */ |
| 73473 | static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ |
| 73474 | BtShared *pBt = p->pBt; |
| 73475 | MemPage *pRoot; |
| 73476 | Pgno pgnoRoot; |
| 73477 | int rc; |
| 73478 | int ptfFlags; /* Page-type flage for the root page of new table */ |
| @@ -73501,21 +73612,23 @@ | |
| 73501 | /* Read the value of meta[3] from the database to determine where the |
| 73502 | ** root page of the new table should go. meta[3] is the largest root-page |
| 73503 | ** created so far, so the new root-page is (meta[3]+1). |
| 73504 | */ |
| 73505 | sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); |
| 73506 | pgnoRoot++; |
| 73507 | |
| 73508 | /* The new root-page may not be allocated on a pointer-map page, or the |
| 73509 | ** PENDING_BYTE page. |
| 73510 | */ |
| 73511 | while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || |
| 73512 | pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ |
| 73513 | pgnoRoot++; |
| 73514 | } |
| 73515 | assert( pgnoRoot>=3 || CORRUPT_DB ); |
| 73516 | testcase( pgnoRoot<3 ); |
| 73517 | |
| 73518 | /* Allocate a page. The page that currently resides at pgnoRoot will |
| 73519 | ** be moved to the allocated page (unless the allocated page happens |
| 73520 | ** to reside at pgnoRoot). |
| 73521 | */ |
| @@ -73608,14 +73721,14 @@ | |
| 73608 | ptfFlags = PTF_ZERODATA | PTF_LEAF; |
| 73609 | } |
| 73610 | zeroPage(pRoot, ptfFlags); |
| 73611 | sqlite3PagerUnref(pRoot->pDbPage); |
| 73612 | assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); |
| 73613 | *piTable = (int)pgnoRoot; |
| 73614 | return SQLITE_OK; |
| 73615 | } |
| 73616 | SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ |
| 73617 | int rc; |
| 73618 | sqlite3BtreeEnter(p); |
| 73619 | rc = btreeCreateTable(p, piTable, flags); |
| 73620 | sqlite3BtreeLeave(p); |
| 73621 | return rc; |
| @@ -74095,11 +74208,11 @@ | |
| 74095 | ** Verify that the number of pages on the list is N. |
| 74096 | */ |
| 74097 | static void checkList( |
| 74098 | IntegrityCk *pCheck, /* Integrity checking context */ |
| 74099 | int isFreeList, /* True for a freelist. False for overflow page list */ |
| 74100 | int iPage, /* Page number for first page in the list */ |
| 74101 | u32 N /* Expected number of pages in the list */ |
| 74102 | ){ |
| 74103 | int i; |
| 74104 | u32 expected = N; |
| 74105 | int nErrAtStart = pCheck->nErr; |
| @@ -74227,11 +74340,11 @@ | |
| 74227 | ** 4. Recursively call checkTreePage on all children. |
| 74228 | ** 5. Verify that the depth of all children is the same. |
| 74229 | */ |
| 74230 | static int checkTreePage( |
| 74231 | IntegrityCk *pCheck, /* Context for the sanity check */ |
| 74232 | int iPage, /* Page number of the page to check */ |
| 74233 | i64 *piMinKey, /* Write minimum integer primary key here */ |
| 74234 | i64 maxKey /* Error if integer primary key greater than this */ |
| 74235 | ){ |
| 74236 | MemPage *pPage = 0; /* The page being analyzed */ |
| 74237 | int i; /* Loop counter */ |
| @@ -74263,13 +74376,13 @@ | |
| 74263 | */ |
| 74264 | pBt = pCheck->pBt; |
| 74265 | usableSize = pBt->usableSize; |
| 74266 | if( iPage==0 ) return 0; |
| 74267 | if( checkRef(pCheck, iPage) ) return 0; |
| 74268 | pCheck->zPfx = "Page %d: "; |
| 74269 | pCheck->v1 = iPage; |
| 74270 | if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ |
| 74271 | checkAppendMsg(pCheck, |
| 74272 | "unable to get the page. error code=%d", rc); |
| 74273 | goto end_of_check; |
| 74274 | } |
| 74275 | |
| @@ -74290,11 +74403,11 @@ | |
| 74290 | } |
| 74291 | data = pPage->aData; |
| 74292 | hdr = pPage->hdrOffset; |
| 74293 | |
| 74294 | /* Set up for cell analysis */ |
| 74295 | pCheck->zPfx = "On tree page %d cell %d: "; |
| 74296 | contentOffset = get2byteNotZero(&data[hdr+5]); |
| 74297 | assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ |
| 74298 | |
| 74299 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 74300 | ** number of cells on the page. */ |
| @@ -74310,11 +74423,11 @@ | |
| 74310 | if( !pPage->leaf ){ |
| 74311 | /* Analyze the right-child page of internal pages */ |
| 74312 | pgno = get4byte(&data[hdr+8]); |
| 74313 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74314 | if( pBt->autoVacuum ){ |
| 74315 | pCheck->zPfx = "On page %d at right child: "; |
| 74316 | checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); |
| 74317 | } |
| 74318 | #endif |
| 74319 | depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); |
| 74320 | keyCanBeEqual = 0; |
| @@ -74451,11 +74564,11 @@ | |
| 74451 | nFrag = 0; |
| 74452 | prev = contentOffset - 1; /* Implied first min-heap entry */ |
| 74453 | while( btreeHeapPull(heap,&x) ){ |
| 74454 | if( (prev&0xffff)>=(x>>16) ){ |
| 74455 | checkAppendMsg(pCheck, |
| 74456 | "Multiple uses for byte %u of page %d", x>>16, iPage); |
| 74457 | break; |
| 74458 | }else{ |
| 74459 | nFrag += (x>>16) - (prev&0xffff) - 1; |
| 74460 | prev = x; |
| 74461 | } |
| @@ -74466,11 +74579,11 @@ | |
| 74466 | ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the |
| 74467 | ** number of fragmented free bytes within the cell content area. |
| 74468 | */ |
| 74469 | if( heap[0]==0 && nFrag!=data[hdr+7] ){ |
| 74470 | checkAppendMsg(pCheck, |
| 74471 | "Fragmentation of %d bytes reported as %d on page %d", |
| 74472 | nFrag, data[hdr+7], iPage); |
| 74473 | } |
| 74474 | } |
| 74475 | |
| 74476 | end_of_check: |
| @@ -74494,25 +74607,44 @@ | |
| 74494 | ** |
| 74495 | ** Write the number of error seen in *pnErr. Except for some memory |
| 74496 | ** allocation errors, an error message held in memory obtained from |
| 74497 | ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is |
| 74498 | ** returned. If a memory allocation error occurs, NULL is returned. |
| 74499 | */ |
| 74500 | SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( |
| 74501 | sqlite3 *db, /* Database connection that is running the check */ |
| 74502 | Btree *p, /* The btree to be checked */ |
| 74503 | int *aRoot, /* An array of root pages numbers for individual trees */ |
| 74504 | int nRoot, /* Number of entries in aRoot[] */ |
| 74505 | int mxErr, /* Stop reporting errors after this many */ |
| 74506 | int *pnErr /* Write number of errors seen to this variable */ |
| 74507 | ){ |
| 74508 | Pgno i; |
| 74509 | IntegrityCk sCheck; |
| 74510 | BtShared *pBt = p->pBt; |
| 74511 | u64 savedDbFlags = pBt->db->flags; |
| 74512 | char zErr[100]; |
| 74513 | VVA_ONLY( int nRef ); |
| 74514 | |
| 74515 | sqlite3BtreeEnter(p); |
| 74516 | assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); |
| 74517 | VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); |
| 74518 | assert( nRef>=0 ); |
| @@ -74548,69 +74680,75 @@ | |
| 74548 | i = PENDING_BYTE_PAGE(pBt); |
| 74549 | if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); |
| 74550 | |
| 74551 | /* Check the integrity of the freelist |
| 74552 | */ |
| 74553 | sCheck.zPfx = "Main freelist: "; |
| 74554 | checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), |
| 74555 | get4byte(&pBt->pPage1->aData[36])); |
| 74556 | sCheck.zPfx = 0; |
| 74557 | |
| 74558 | /* Check all the tables. |
| 74559 | */ |
| 74560 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74561 | if( pBt->autoVacuum ){ |
| 74562 | int mx = 0; |
| 74563 | int mxInHdr; |
| 74564 | for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i]; |
| 74565 | mxInHdr = get4byte(&pBt->pPage1->aData[52]); |
| 74566 | if( mx!=mxInHdr ){ |
| 74567 | checkAppendMsg(&sCheck, |
| 74568 | "max rootpage (%d) disagrees with header (%d)", |
| 74569 | mx, mxInHdr |
| 74570 | ); |
| 74571 | } |
| 74572 | }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ |
| 74573 | checkAppendMsg(&sCheck, |
| 74574 | "incremental_vacuum enabled with a max rootpage of zero" |
| 74575 | ); |
| 74576 | } |
| 74577 | #endif |
| 74578 | testcase( pBt->db->flags & SQLITE_CellSizeCk ); |
| 74579 | pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; |
| 74580 | for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ |
| 74581 | i64 notUsed; |
| 74582 | if( aRoot[i]==0 ) continue; |
| 74583 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74584 | if( pBt->autoVacuum && aRoot[i]>1 ){ |
| 74585 | checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); |
| 74586 | } |
| 74587 | #endif |
| 74588 | checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); |
| 74589 | } |
| 74590 | pBt->db->flags = savedDbFlags; |
| 74591 | |
| 74592 | /* Make sure every page in the file is referenced |
| 74593 | */ |
| 74594 | for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ |
| 74595 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 74596 | if( getPageReferenced(&sCheck, i)==0 ){ |
| 74597 | checkAppendMsg(&sCheck, "Page %d is never used", i); |
| 74598 | } |
| 74599 | #else |
| 74600 | /* If the database supports auto-vacuum, make sure no tables contain |
| 74601 | ** references to pointer-map pages. |
| 74602 | */ |
| 74603 | if( getPageReferenced(&sCheck, i)==0 && |
| 74604 | (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ |
| 74605 | checkAppendMsg(&sCheck, "Page %d is never used", i); |
| 74606 | } |
| 74607 | if( getPageReferenced(&sCheck, i)!=0 && |
| 74608 | (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ |
| 74609 | checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); |
| 74610 | } |
| 74611 | #endif |
| 74612 | } |
| 74613 | |
| 74614 | /* Clean up and report errors. |
| 74615 | */ |
| 74616 | integrity_ck_cleanup: |
| @@ -75794,20 +75932,29 @@ | |
| 75794 | ** into a buffer. |
| 75795 | */ |
| 75796 | static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ |
| 75797 | StrAccum acc; |
| 75798 | assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); |
| 75799 | sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); |
| 75800 | if( p->flags & MEM_Int ){ |
| 75801 | sqlite3_str_appendf(&acc, "%lld", p->u.i); |
| 75802 | }else if( p->flags & MEM_IntReal ){ |
| 75803 | sqlite3_str_appendf(&acc, "%!.15g", (double)p->u.i); |
| 75804 | }else{ |
| 75805 | sqlite3_str_appendf(&acc, "%!.15g", p->u.r); |
| 75806 | } |
| 75807 | assert( acc.zText==zBuf && acc.mxAlloc<=0 ); |
| 75808 | zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ |
| 75809 | } |
| 75810 | |
| 75811 | #ifdef SQLITE_DEBUG |
| 75812 | /* |
| 75813 | ** Validity checks on pMem. pMem holds a string. |
| @@ -78397,11 +78544,11 @@ | |
| 78397 | /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing |
| 78398 | ** cases from this switch! */ |
| 78399 | switch( pOp->opcode ){ |
| 78400 | case OP_Transaction: { |
| 78401 | if( pOp->p2!=0 ) p->readOnly = 0; |
| 78402 | /* fall thru */ |
| 78403 | } |
| 78404 | case OP_AutoCommit: |
| 78405 | case OP_Savepoint: { |
| 78406 | p->bIsReader = 1; |
| 78407 | break; |
| @@ -78444,10 +78591,11 @@ | |
| 78444 | assert( (pOp - p->aOp) >= 3 ); |
| 78445 | assert( pOp[-1].opcode==OP_Integer ); |
| 78446 | n = pOp[-1].p1; |
| 78447 | if( n>nMaxArgs ) nMaxArgs = n; |
| 78448 | /* Fall through into the default case */ |
| 78449 | } |
| 78450 | #endif |
| 78451 | default: { |
| 78452 | if( pOp->p2<0 ){ |
| 78453 | /* The mkopcodeh.tcl script has so arranged things that the only |
| @@ -79301,16 +79449,16 @@ | |
| 79301 | sqlite3_str_appendf(&x, "vtab:%p", pVtab); |
| 79302 | break; |
| 79303 | } |
| 79304 | #endif |
| 79305 | case P4_INTARRAY: { |
| 79306 | int i; |
| 79307 | int *ai = pOp->p4.ai; |
| 79308 | int n = ai[0]; /* The first element of an INTARRAY is always the |
| 79309 | ** count of the number of elements to follow */ |
| 79310 | for(i=1; i<=n; i++){ |
| 79311 | sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]); |
| 79312 | } |
| 79313 | sqlite3_str_append(&x, "]", 1); |
| 79314 | break; |
| 79315 | } |
| 79316 | case P4_SUBPROGRAM: { |
| @@ -81150,15 +81298,15 @@ | |
| 81150 | ** a NULL row. |
| 81151 | ** |
| 81152 | ** If the cursor is already pointing to the correct row and that row has |
| 81153 | ** not been deleted out from under the cursor, then this routine is a no-op. |
| 81154 | */ |
| 81155 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ |
| 81156 | VdbeCursor *p = *pp; |
| 81157 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81158 | if( p->deferredMoveto ){ |
| 81159 | int iMap; |
| 81160 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81161 | *pp = p->pAltCursor; |
| 81162 | *piCol = iMap - 1; |
| 81163 | return SQLITE_OK; |
| 81164 | } |
| @@ -82910,11 +83058,11 @@ | |
| 82910 | if( db->xProfile ){ |
| 82911 | db->xProfile(db->pProfileArg, p->zSql, iElapse); |
| 82912 | } |
| 82913 | #endif |
| 82914 | if( db->mTrace & SQLITE_TRACE_PROFILE ){ |
| 82915 | db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); |
| 82916 | } |
| 82917 | p->startTime = 0; |
| 82918 | } |
| 82919 | /* |
| 82920 | ** The checkProfileCallback(DB,P) macro checks to see if a profile callback |
| @@ -86210,10 +86358,11 @@ | |
| 86210 | #ifdef SQLITE_DEBUG |
| 86211 | if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } |
| 86212 | #endif |
| 86213 | if( (pIn3->flags & MEM_Null)==0 ) break; |
| 86214 | /* Fall through into OP_Halt */ |
| 86215 | } |
| 86216 | |
| 86217 | /* Opcode: Halt P1 P2 * P4 P5 |
| 86218 | ** |
| 86219 | ** Exit immediately. All open cursors, etc are closed |
| @@ -86380,10 +86529,11 @@ | |
| 86380 | goto too_big; |
| 86381 | } |
| 86382 | pOp->opcode = OP_String; |
| 86383 | assert( rc==SQLITE_OK ); |
| 86384 | /* Fall through to the next case, OP_String */ |
| 86385 | } |
| 86386 | |
| 86387 | /* Opcode: String P1 P2 P3 P4 P5 |
| 86388 | ** Synopsis: r[P2]='P4' (len=P1) |
| 86389 | ** |
| @@ -86691,11 +86841,11 @@ | |
| 86691 | #endif |
| 86692 | } |
| 86693 | if( db->mallocFailed ) goto no_mem; |
| 86694 | |
| 86695 | if( db->mTrace & SQLITE_TRACE_ROW ){ |
| 86696 | db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); |
| 86697 | } |
| 86698 | |
| 86699 | |
| 86700 | /* Return SQLITE_ROW |
| 86701 | */ |
| @@ -87427,14 +87577,14 @@ | |
| 87427 | int n; |
| 87428 | int i; |
| 87429 | int p1; |
| 87430 | int p2; |
| 87431 | const KeyInfo *pKeyInfo; |
| 87432 | int idx; |
| 87433 | CollSeq *pColl; /* Collating sequence to use on this term */ |
| 87434 | int bRev; /* True for DESCENDING sort order */ |
| 87435 | int *aPermute; /* The permutation */ |
| 87436 | |
| 87437 | if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ |
| 87438 | aPermute = 0; |
| 87439 | }else{ |
| 87440 | assert( pOp>aOp ); |
| @@ -87450,20 +87600,20 @@ | |
| 87450 | p1 = pOp->p1; |
| 87451 | p2 = pOp->p2; |
| 87452 | #ifdef SQLITE_DEBUG |
| 87453 | if( aPermute ){ |
| 87454 | int k, mx = 0; |
| 87455 | for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k]; |
| 87456 | assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87457 | assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87458 | }else{ |
| 87459 | assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87460 | assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87461 | } |
| 87462 | #endif /* SQLITE_DEBUG */ |
| 87463 | for(i=0; i<n; i++){ |
| 87464 | idx = aPermute ? aPermute[i] : i; |
| 87465 | assert( memIsValid(&aMem[p1+idx]) ); |
| 87466 | assert( memIsValid(&aMem[p2+idx]) ); |
| 87467 | REGISTER_TRACE(p1+idx, &aMem[p1+idx]); |
| 87468 | REGISTER_TRACE(p2+idx, &aMem[p2+idx]); |
| 87469 | assert( i<pKeyInfo->nKeyField ); |
| @@ -87770,11 +87920,11 @@ | |
| 87770 | ** the result is guaranteed to only be used as the argument of a length() |
| 87771 | ** or typeof() function, respectively. The loading of large blobs can be |
| 87772 | ** skipped for length() and all content loading can be skipped for typeof(). |
| 87773 | */ |
| 87774 | case OP_Column: { |
| 87775 | int p2; /* column number to retrieve */ |
| 87776 | VdbeCursor *pC; /* The VDBE cursor */ |
| 87777 | BtCursor *pCrsr; /* The BTree cursor */ |
| 87778 | u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ |
| 87779 | int len; /* The length of the serialized data for the column */ |
| 87780 | int i; /* Loop counter */ |
| @@ -87788,11 +87938,11 @@ | |
| 87788 | Mem *pReg; /* PseudoTable input register */ |
| 87789 | |
| 87790 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 87791 | pC = p->apCsr[pOp->p1]; |
| 87792 | assert( pC!=0 ); |
| 87793 | p2 = pOp->p2; |
| 87794 | |
| 87795 | /* If the cursor cache is stale (meaning it is not currently point at |
| 87796 | ** the correct row) then bring it up-to-date by doing the necessary |
| 87797 | ** B-Tree seek. */ |
| 87798 | rc = sqlite3VdbeCursorMoveto(&pC, &p2); |
| @@ -87800,11 +87950,11 @@ | |
| 87800 | |
| 87801 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 87802 | pDest = &aMem[pOp->p3]; |
| 87803 | memAboutToChange(p, pDest); |
| 87804 | assert( pC!=0 ); |
| 87805 | assert( p2<pC->nField ); |
| 87806 | aOffset = pC->aOffset; |
| 87807 | assert( pC->eCurType!=CURTYPE_VTAB ); |
| 87808 | assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); |
| 87809 | assert( pC->eCurType!=CURTYPE_SORTER ); |
| 87810 | |
| @@ -87915,11 +88065,11 @@ | |
| 87915 | zHdr += sqlite3GetVarint32(zHdr, &t); |
| 87916 | pC->aType[i] = t; |
| 87917 | offset64 += sqlite3VdbeSerialTypeLen(t); |
| 87918 | } |
| 87919 | aOffset[++i] = (u32)(offset64 & 0xffffffff); |
| 87920 | }while( i<=p2 && zHdr<zEndHdr ); |
| 87921 | |
| 87922 | /* The record is corrupt if any of the following are true: |
| 87923 | ** (1) the bytes of the header extend past the declared header size |
| 87924 | ** (2) the entire header was used but not all data was used |
| 87925 | ** (3) the end of the data extends beyond the end of the record. |
| @@ -88939,11 +89089,11 @@ | |
| 88939 | ** See also: OP_OpenRead, OP_ReopenIdx |
| 88940 | */ |
| 88941 | case OP_ReopenIdx: { |
| 88942 | int nField; |
| 88943 | KeyInfo *pKeyInfo; |
| 88944 | int p2; |
| 88945 | int iDb; |
| 88946 | int wrFlag; |
| 88947 | Btree *pX; |
| 88948 | VdbeCursor *pCur; |
| 88949 | Db *pDb; |
| @@ -88970,11 +89120,11 @@ | |
| 88970 | goto abort_due_to_error; |
| 88971 | } |
| 88972 | |
| 88973 | nField = 0; |
| 88974 | pKeyInfo = 0; |
| 88975 | p2 = pOp->p2; |
| 88976 | iDb = pOp->p3; |
| 88977 | assert( iDb>=0 && iDb<db->nDb ); |
| 88978 | assert( DbMaskTest(p->btreeMask, iDb) ); |
| 88979 | pDb = &db->aDb[iDb]; |
| 88980 | pX = pDb->pBt; |
| @@ -88989,11 +89139,11 @@ | |
| 88989 | }else{ |
| 88990 | wrFlag = 0; |
| 88991 | } |
| 88992 | if( pOp->p5 & OPFLAG_P2ISREG ){ |
| 88993 | assert( p2>0 ); |
| 88994 | assert( p2<=(p->nMem+1 - p->nCursor) ); |
| 88995 | assert( pOp->opcode==OP_OpenWrite ); |
| 88996 | pIn2 = &aMem[p2]; |
| 88997 | assert( memIsValid(pIn2) ); |
| 88998 | assert( (pIn2->flags & MEM_Int)!=0 ); |
| 88999 | sqlite3VdbeMemIntegerify(pIn2); |
| @@ -89142,11 +89292,11 @@ | |
| 89142 | ** opening it. If a transient table is required, just use the |
| 89143 | ** automatically created table with root-page 1 (an BLOB_INTKEY table). |
| 89144 | */ |
| 89145 | if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ |
| 89146 | assert( pOp->p4type==P4_KEYINFO ); |
| 89147 | rc = sqlite3BtreeCreateTable(pCx->pBtx, (int*)&pCx->pgnoRoot, |
| 89148 | BTREE_BLOBKEY | pOp->p5); |
| 89149 | if( rc==SQLITE_OK ){ |
| 89150 | assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); |
| 89151 | assert( pKeyInfo->db==db ); |
| 89152 | assert( pKeyInfo->enc==ENC(db) ); |
| @@ -89684,10 +89834,11 @@ | |
| 89684 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89685 | pC = p->apCsr[pOp->p1]; |
| 89686 | assert( pC!=0 ); |
| 89687 | if( pC->seekHit ) break; |
| 89688 | /* Fall through into OP_NotFound */ |
| 89689 | } |
| 89690 | case OP_NoConflict: /* jump, in3 */ |
| 89691 | case OP_NotFound: /* jump, in3 */ |
| 89692 | case OP_Found: { /* jump, in3 */ |
| 89693 | int alreadyExists; |
| @@ -89838,10 +89989,11 @@ | |
| 89838 | if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; |
| 89839 | iKey = x.u.i; |
| 89840 | goto notExistsWithKey; |
| 89841 | } |
| 89842 | /* Fall through into OP_NotExists */ |
| 89843 | case OP_NotExists: /* jump, in3 */ |
| 89844 | pIn3 = &aMem[pOp->p3]; |
| 89845 | assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); |
| 89846 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89847 | iKey = pIn3->u.i; |
| @@ -90406,14 +90558,10 @@ | |
| 90406 | ** generator) then the fix would be to insert a call to |
| 90407 | ** sqlite3VdbeCursorMoveto(). |
| 90408 | */ |
| 90409 | assert( pC->deferredMoveto==0 ); |
| 90410 | assert( sqlite3BtreeCursorIsValid(pCrsr) ); |
| 90411 | #if 0 /* Not required due to the previous to assert() statements */ |
| 90412 | rc = sqlite3VdbeCursorMoveto(pC); |
| 90413 | if( rc!=SQLITE_OK ) goto abort_due_to_error; |
| 90414 | #endif |
| 90415 | |
| 90416 | n = sqlite3BtreePayloadSize(pCrsr); |
| 90417 | if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ |
| 90418 | goto too_big; |
| 90419 | } |
| @@ -90613,10 +90761,11 @@ | |
| 90613 | sqlite3_sort_count++; |
| 90614 | sqlite3_search_count--; |
| 90615 | #endif |
| 90616 | p->aCounter[SQLITE_STMTSTATUS_SORT]++; |
| 90617 | /* Fall through into OP_Rewind */ |
| 90618 | } |
| 90619 | /* Opcode: Rewind P1 P2 * * * |
| 90620 | ** |
| 90621 | ** The next use of the Rowid or Column or Next instruction for P1 |
| 90622 | ** will refer to the first entry in the database table or index. |
| @@ -91179,11 +91328,11 @@ | |
| 91179 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91180 | nChange = 0; |
| 91181 | assert( p->readOnly==0 ); |
| 91182 | assert( DbMaskTest(p->btreeMask, pOp->p2) ); |
| 91183 | rc = sqlite3BtreeClearTable( |
| 91184 | db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) |
| 91185 | ); |
| 91186 | if( pOp->p3 ){ |
| 91187 | p->nChange += nChange; |
| 91188 | if( pOp->p3>0 ){ |
| 91189 | assert( memIsValid(&aMem[pOp->p3]) ); |
| @@ -91228,11 +91377,11 @@ | |
| 91228 | ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table |
| 91229 | ** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. |
| 91230 | ** The root page number of the new b-tree is stored in register P2. |
| 91231 | */ |
| 91232 | case OP_CreateBtree: { /* out2 */ |
| 91233 | int pgno; |
| 91234 | Db *pDb; |
| 91235 | |
| 91236 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91237 | pOut = out2Prerelease(p, pOp); |
| 91238 | pgno = 0; |
| @@ -91303,10 +91452,11 @@ | |
| 91303 | zSchema = DFLT_SCHEMA_TABLE; |
| 91304 | initData.db = db; |
| 91305 | initData.iDb = iDb; |
| 91306 | initData.pzErrMsg = &p->zErrMsg; |
| 91307 | initData.mInitFlags = 0; |
| 91308 | zSql = sqlite3MPrintf(db, |
| 91309 | "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", |
| 91310 | db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); |
| 91311 | if( zSql==0 ){ |
| 91312 | rc = SQLITE_NOMEM_BKPT; |
| @@ -91416,20 +91566,20 @@ | |
| 91416 | ** |
| 91417 | ** This opcode is used to implement the integrity_check pragma. |
| 91418 | */ |
| 91419 | case OP_IntegrityCk: { |
| 91420 | int nRoot; /* Number of tables to check. (Number of root pages.) */ |
| 91421 | int *aRoot; /* Array of rootpage numbers for tables to be checked */ |
| 91422 | int nErr; /* Number of errors reported */ |
| 91423 | char *z; /* Text of the error report */ |
| 91424 | Mem *pnErr; /* Register keeping track of errors remaining */ |
| 91425 | |
| 91426 | assert( p->bIsReader ); |
| 91427 | nRoot = pOp->p2; |
| 91428 | aRoot = pOp->p4.ai; |
| 91429 | assert( nRoot>0 ); |
| 91430 | assert( aRoot[0]==nRoot ); |
| 91431 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 91432 | pnErr = &aMem[pOp->p3]; |
| 91433 | assert( (pnErr->flags & MEM_Int)!=0 ); |
| 91434 | assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); |
| 91435 | pIn1 = &aMem[pOp->p1]; |
| @@ -91965,10 +92115,11 @@ | |
| 91965 | /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ |
| 91966 | assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); |
| 91967 | |
| 91968 | pOp->opcode = OP_AggStep1; |
| 91969 | /* Fall through into OP_AggStep */ |
| 91970 | } |
| 91971 | case OP_AggStep1: { |
| 91972 | int i; |
| 91973 | sqlite3_context *pCtx; |
| 91974 | Mem *pMem; |
| @@ -92954,22 +93105,21 @@ | |
| 92954 | && !p->doingRerun |
| 92955 | && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 |
| 92956 | ){ |
| 92957 | #ifndef SQLITE_OMIT_DEPRECATED |
| 92958 | if( db->mTrace & SQLITE_TRACE_LEGACY ){ |
| 92959 | void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace; |
| 92960 | char *z = sqlite3VdbeExpandSql(p, zTrace); |
| 92961 | x(db->pTraceArg, z); |
| 92962 | sqlite3_free(z); |
| 92963 | }else |
| 92964 | #endif |
| 92965 | if( db->nVdbeExec>1 ){ |
| 92966 | char *z = sqlite3MPrintf(db, "-- %s", zTrace); |
| 92967 | (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z); |
| 92968 | sqlite3DbFree(db, z); |
| 92969 | }else{ |
| 92970 | (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); |
| 92971 | } |
| 92972 | } |
| 92973 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 92974 | zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); |
| 92975 | if( zTrace ){ |
| @@ -96701,11 +96851,11 @@ | |
| 96701 | }else{ |
| 96702 | if( i<=2 && pCur->zType==0 ){ |
| 96703 | Schema *pSchema; |
| 96704 | HashElem *k; |
| 96705 | int iDb = pOp->p3; |
| 96706 | int iRoot = pOp->p2; |
| 96707 | sqlite3 *db = pVTab->db; |
| 96708 | pSchema = db->aDb[iDb].pSchema; |
| 96709 | pCur->zSchema = db->aDb[iDb].zDbSName; |
| 96710 | for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| 96711 | Table *pTab = (Table*)sqliteHashData(k); |
| @@ -97288,11 +97438,11 @@ | |
| 97288 | }else{ |
| 97289 | p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); |
| 97290 | assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); |
| 97291 | } |
| 97292 | |
| 97293 | p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods; |
| 97294 | p->nSpill = nSpill; |
| 97295 | p->flags = flags; |
| 97296 | p->zJournal = zName; |
| 97297 | p->pVfs = pVfs; |
| 97298 | return SQLITE_OK; |
| @@ -97314,11 +97464,11 @@ | |
| 97314 | ** file has not yet been created, create it now. |
| 97315 | */ |
| 97316 | SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){ |
| 97317 | int rc = SQLITE_OK; |
| 97318 | MemJournal *p = (MemJournal*)pJfd; |
| 97319 | if( p->pMethod==&MemJournalMethods && ( |
| 97320 | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
| 97321 | p->nSpill>0 |
| 97322 | #else |
| 97323 | /* While this appears to not be possible without ATOMIC_WRITE, the |
| 97324 | ** paths are complex, so it seems prudent to leave the test in as |
| @@ -98687,11 +98837,11 @@ | |
| 98687 | pExpr->op2 = pExpr->op; |
| 98688 | pExpr->op = TK_TRUTH; |
| 98689 | return WRC_Continue; |
| 98690 | } |
| 98691 | } |
| 98692 | /* Fall thru */ |
| 98693 | } |
| 98694 | case TK_BETWEEN: |
| 98695 | case TK_EQ: |
| 98696 | case TK_NE: |
| 98697 | case TK_LT: |
| @@ -101591,11 +101741,11 @@ | |
| 101591 | /* Convert "true" or "false" in a DEFAULT clause into the |
| 101592 | ** appropriate TK_TRUEFALSE operator */ |
| 101593 | if( sqlite3ExprIdToTrueFalse(pExpr) ){ |
| 101594 | return WRC_Prune; |
| 101595 | } |
| 101596 | /* Fall thru */ |
| 101597 | case TK_COLUMN: |
| 101598 | case TK_AGG_FUNCTION: |
| 101599 | case TK_AGG_COLUMN: |
| 101600 | testcase( pExpr->op==TK_ID ); |
| 101601 | testcase( pExpr->op==TK_COLUMN ); |
| @@ -101605,11 +101755,11 @@ | |
| 101605 | return WRC_Continue; |
| 101606 | } |
| 101607 | if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ |
| 101608 | return WRC_Continue; |
| 101609 | } |
| 101610 | /* Fall through */ |
| 101611 | case TK_IF_NULL_ROW: |
| 101612 | case TK_REGISTER: |
| 101613 | case TK_DOT: |
| 101614 | testcase( pExpr->op==TK_REGISTER ); |
| 101615 | testcase( pExpr->op==TK_IF_NULL_ROW ); |
| @@ -101626,11 +101776,11 @@ | |
| 101626 | /* A bound parameter in a CREATE statement that originates from |
| 101627 | ** sqlite3_prepare() causes an error */ |
| 101628 | pWalker->eCode = 0; |
| 101629 | return WRC_Abort; |
| 101630 | } |
| 101631 | /* Fall through */ |
| 101632 | default: |
| 101633 | testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ |
| 101634 | testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ |
| 101635 | return WRC_Continue; |
| 101636 | } |
| @@ -103398,10 +103548,11 @@ | |
| 103398 | } |
| 103399 | } |
| 103400 | return target; |
| 103401 | } |
| 103402 | /* Otherwise, fall thru into the TK_COLUMN case */ |
| 103403 | } |
| 103404 | case TK_COLUMN: { |
| 103405 | int iTab = pExpr->iTable; |
| 103406 | int iReg; |
| 103407 | if( ExprHasProperty(pExpr, EP_FixedCol) ){ |
| @@ -104463,11 +104614,11 @@ | |
| 104463 | case TK_ISNOT: |
| 104464 | testcase( op==TK_IS ); |
| 104465 | testcase( op==TK_ISNOT ); |
| 104466 | op = (op==TK_IS) ? TK_EQ : TK_NE; |
| 104467 | jumpIfNull = SQLITE_NULLEQ; |
| 104468 | /* Fall thru */ |
| 104469 | case TK_LT: |
| 104470 | case TK_LE: |
| 104471 | case TK_GT: |
| 104472 | case TK_GE: |
| 104473 | case TK_NE: |
| @@ -104639,11 +104790,11 @@ | |
| 104639 | case TK_ISNOT: |
| 104640 | testcase( pExpr->op==TK_IS ); |
| 104641 | testcase( pExpr->op==TK_ISNOT ); |
| 104642 | op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; |
| 104643 | jumpIfNull = SQLITE_NULLEQ; |
| 104644 | /* Fall thru */ |
| 104645 | case TK_LT: |
| 104646 | case TK_LE: |
| 104647 | case TK_GT: |
| 104648 | case TK_GE: |
| 104649 | case TK_NE: |
| @@ -104951,17 +105102,17 @@ | |
| 104951 | case TK_BITOR: |
| 104952 | case TK_LSHIFT: |
| 104953 | case TK_RSHIFT: |
| 104954 | case TK_CONCAT: |
| 104955 | seenNot = 1; |
| 104956 | /* Fall thru */ |
| 104957 | case TK_STAR: |
| 104958 | case TK_REM: |
| 104959 | case TK_BITAND: |
| 104960 | case TK_SLASH: { |
| 104961 | if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1; |
| 104962 | /* Fall thru into the next case */ |
| 104963 | } |
| 104964 | case TK_SPAN: |
| 104965 | case TK_COLLATE: |
| 104966 | case TK_UPLUS: |
| 104967 | case TK_UMINUS: { |
| @@ -105106,10 +105257,11 @@ | |
| 105106 | || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) |
| 105107 | && IsVirtual(pRight->y.pTab)) |
| 105108 | ){ |
| 105109 | return WRC_Prune; |
| 105110 | } |
| 105111 | } |
| 105112 | default: |
| 105113 | return WRC_Continue; |
| 105114 | } |
| 105115 | } |
| @@ -106839,11 +106991,11 @@ | |
| 106839 | } |
| 106840 | if( rc==SQLITE_OK && pStep->zTarget ){ |
| 106841 | SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); |
| 106842 | if( pSrc ){ |
| 106843 | int i; |
| 106844 | for(i=0; i<pSrc->nSrc; i++){ |
| 106845 | struct SrcList_item *p = &pSrc->a[i]; |
| 106846 | p->pTab = sqlite3LocateTableItem(pParse, 0, p); |
| 106847 | p->iCursor = pParse->nTab++; |
| 106848 | if( p->pTab==0 ){ |
| 106849 | rc = SQLITE_ERROR; |
| @@ -107581,11 +107733,11 @@ | |
| 107581 | }; |
| 107582 | int i; |
| 107583 | sqlite3 *db = pParse->db; |
| 107584 | Db *pDb; |
| 107585 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 107586 | int aRoot[ArraySize(aTable)]; |
| 107587 | u8 aCreateTbl[ArraySize(aTable)]; |
| 107588 | #ifdef SQLITE_ENABLE_STAT4 |
| 107589 | const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; |
| 107590 | #else |
| 107591 | const int nToOpen = 1; |
| @@ -107610,11 +107762,11 @@ | |
| 107610 | ** of the new table in register pParse->regRoot. This is important |
| 107611 | ** because the OpenWrite opcode below will be needing it. */ |
| 107612 | sqlite3NestedParse(pParse, |
| 107613 | "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols |
| 107614 | ); |
| 107615 | aRoot[i] = pParse->regRoot; |
| 107616 | aCreateTbl[i] = OPFLAG_P2ISREG; |
| 107617 | } |
| 107618 | }else{ |
| 107619 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 107620 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| @@ -107630,19 +107782,19 @@ | |
| 107630 | }else if( db->xPreUpdateCallback ){ |
| 107631 | sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab); |
| 107632 | #endif |
| 107633 | }else{ |
| 107634 | /* The sqlite_stat[134] table already exists. Delete all rows. */ |
| 107635 | sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); |
| 107636 | } |
| 107637 | } |
| 107638 | } |
| 107639 | |
| 107640 | /* Open the sqlite_stat[134] tables for writing. */ |
| 107641 | for(i=0; i<nToOpen; i++){ |
| 107642 | assert( i<ArraySize(aTable) ); |
| 107643 | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3); |
| 107644 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 107645 | VdbeComment((v, aTable[i].zName)); |
| 107646 | } |
| 107647 | } |
| 107648 | |
| @@ -110271,11 +110423,11 @@ | |
| 110271 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 110272 | ** codeTableLocks() functions. |
| 110273 | */ |
| 110274 | struct TableLock { |
| 110275 | int iDb; /* The database containing the table to be locked */ |
| 110276 | int iTab; /* The root page of the table to be locked */ |
| 110277 | u8 isWriteLock; /* True for write lock. False for a read lock */ |
| 110278 | const char *zLockName; /* Name of the table */ |
| 110279 | }; |
| 110280 | |
| 110281 | /* |
| @@ -110289,11 +110441,11 @@ | |
| 110289 | ** codeTableLocks() which occurs during sqlite3FinishCoding(). |
| 110290 | */ |
| 110291 | SQLITE_PRIVATE void sqlite3TableLock( |
| 110292 | Parse *pParse, /* Parsing context */ |
| 110293 | int iDb, /* Index of the database containing the table to lock */ |
| 110294 | int iTab, /* Root page number of the table to be locked */ |
| 110295 | u8 isWriteLock, /* True for a write lock */ |
| 110296 | const char *zName /* Name of the table to be locked */ |
| 110297 | ){ |
| 110298 | Parse *pToplevel = sqlite3ParseToplevel(pParse); |
| 110299 | int i; |
| @@ -111128,23 +111280,24 @@ | |
| 111128 | const char *zName, /* Name of the object to check */ |
| 111129 | const char *zType, /* Type of this object */ |
| 111130 | const char *zTblName /* Parent table name for triggers and indexes */ |
| 111131 | ){ |
| 111132 | sqlite3 *db = pParse->db; |
| 111133 | if( sqlite3WritableSchema(db) || db->init.imposterTable ){ |
| 111134 | /* Skip these error checks for writable_schema=ON */ |
| 111135 | return SQLITE_OK; |
| 111136 | } |
| 111137 | if( db->init.busy ){ |
| 111138 | if( sqlite3_stricmp(zType, db->init.azInit[0]) |
| 111139 | || sqlite3_stricmp(zName, db->init.azInit[1]) |
| 111140 | || sqlite3_stricmp(zTblName, db->init.azInit[2]) |
| 111141 | ){ |
| 111142 | if( sqlite3Config.bExtraSchemaChecks ){ |
| 111143 | sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ |
| 111144 | return SQLITE_ERROR; |
| 111145 | } |
| 111146 | } |
| 111147 | }else{ |
| 111148 | if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7)) |
| 111149 | || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName)) |
| 111150 | ){ |
| @@ -112354,11 +112507,11 @@ | |
| 112354 | ** table entry. This is only required if currently generating VDBE |
| 112355 | ** code for a CREATE TABLE (not when parsing one as part of reading |
| 112356 | ** a database schema). */ |
| 112357 | if( v && pPk->tnum>0 ){ |
| 112358 | assert( db->init.busy==0 ); |
| 112359 | sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto); |
| 112360 | } |
| 112361 | |
| 112362 | /* The root page of the PRIMARY KEY is the table root page */ |
| 112363 | pPk->tnum = pTab->tnum; |
| 112364 | |
| @@ -112942,14 +113095,12 @@ | |
| 112942 | ** statement that defines the view. |
| 112943 | */ |
| 112944 | assert( pTable->pSelect ); |
| 112945 | pSel = sqlite3SelectDup(db, pTable->pSelect, 0); |
| 112946 | if( pSel ){ |
| 112947 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 112948 | u8 eParseMode = pParse->eParseMode; |
| 112949 | pParse->eParseMode = PARSE_MODE_NORMAL; |
| 112950 | #endif |
| 112951 | n = pParse->nTab; |
| 112952 | sqlite3SrcListAssignCursors(pParse, pSel->pSrc); |
| 112953 | pTable->nCol = -1; |
| 112954 | DisableLookaside; |
| 112955 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| @@ -112993,13 +113144,11 @@ | |
| 112993 | } |
| 112994 | pTable->nNVCol = pTable->nCol; |
| 112995 | sqlite3DeleteTable(db, pSelTab); |
| 112996 | sqlite3SelectDelete(db, pSel); |
| 112997 | EnableLookaside; |
| 112998 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 112999 | pParse->eParseMode = eParseMode; |
| 113000 | #endif |
| 113001 | } else { |
| 113002 | nErr++; |
| 113003 | } |
| 113004 | pTable->pSchema->schemaFlags |= DB_UnresetViews; |
| 113005 | if( db->mallocFailed ){ |
| @@ -113050,11 +113199,11 @@ | |
| 113050 | ** We must continue looping until all tables and indices with |
| 113051 | ** rootpage==iFrom have been converted to have a rootpage of iTo |
| 113052 | ** in order to be certain that we got the right one. |
| 113053 | */ |
| 113054 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 113055 | SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ |
| 113056 | HashElem *pElem; |
| 113057 | Hash *pHash; |
| 113058 | Db *pDb; |
| 113059 | |
| 113060 | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| @@ -113127,22 +113276,22 @@ | |
| 113127 | ** and root page 5 happened to be the largest root-page number in the |
| 113128 | ** database, then root page 5 would be moved to page 4 by the |
| 113129 | ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit |
| 113130 | ** a free-list page. |
| 113131 | */ |
| 113132 | int iTab = pTab->tnum; |
| 113133 | int iDestroyed = 0; |
| 113134 | |
| 113135 | while( 1 ){ |
| 113136 | Index *pIdx; |
| 113137 | int iLargest = 0; |
| 113138 | |
| 113139 | if( iDestroyed==0 || iTab<iDestroyed ){ |
| 113140 | iLargest = iTab; |
| 113141 | } |
| 113142 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 113143 | int iIdx = pIdx->tnum; |
| 113144 | assert( pIdx->pSchema==pTab->pSchema ); |
| 113145 | if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){ |
| 113146 | iLargest = iIdx; |
| 113147 | } |
| 113148 | } |
| @@ -113561,11 +113710,11 @@ | |
| 113561 | int iTab = pParse->nTab++; /* Btree cursor used for pTab */ |
| 113562 | int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ |
| 113563 | int iSorter; /* Cursor opened by OpenSorter (if in use) */ |
| 113564 | int addr1; /* Address of top of loop */ |
| 113565 | int addr2; /* Address to jump to for next iteration */ |
| 113566 | int tnum; /* Root page of index */ |
| 113567 | int iPartIdxLabel; /* Jump to this label to skip a row */ |
| 113568 | Vdbe *v; /* Generate code into this virtual machine */ |
| 113569 | KeyInfo *pKey; /* KeyInfo for index */ |
| 113570 | int regRecord; /* Register holding assembled index record */ |
| 113571 | sqlite3 *db = pParse->db; /* The database connection */ |
| @@ -113582,11 +113731,11 @@ | |
| 113582 | sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); |
| 113583 | |
| 113584 | v = sqlite3GetVdbe(pParse); |
| 113585 | if( v==0 ) return; |
| 113586 | if( memRootPage>=0 ){ |
| 113587 | tnum = memRootPage; |
| 113588 | }else{ |
| 113589 | tnum = pIndex->tnum; |
| 113590 | } |
| 113591 | pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); |
| 113592 | assert( pKey!=0 || db->mallocFailed || pParse->nErr ); |
| @@ -113607,11 +113756,11 @@ | |
| 113607 | sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); |
| 113608 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 113609 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); |
| 113610 | sqlite3VdbeJumpHere(v, addr1); |
| 113611 | if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); |
| 113612 | sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, |
| 113613 | (char *)pKey, P4_KEYINFO); |
| 113614 | sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); |
| 113615 | |
| 113616 | addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v); |
| 113617 | if( IsUniqueIndex(pIndex) ){ |
| @@ -114216,11 +114365,11 @@ | |
| 114216 | ** doing so, code a Noop instruction and store its address in |
| 114217 | ** Index.tnum. This is required in case this index is actually a |
| 114218 | ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In |
| 114219 | ** that case the convertToWithoutRowidTable() routine will replace |
| 114220 | ** the Noop with a Goto to jump over the VDBE code generated below. */ |
| 114221 | pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); |
| 114222 | sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); |
| 114223 | |
| 114224 | /* Gather the complete text of the CREATE INDEX statement into |
| 114225 | ** the zStmt variable |
| 114226 | */ |
| @@ -114258,11 +114407,11 @@ | |
| 114258 | sqlite3VdbeAddParseSchemaOp(v, iDb, |
| 114259 | sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); |
| 114260 | sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); |
| 114261 | } |
| 114262 | |
| 114263 | sqlite3VdbeJumpHere(v, pIndex->tnum); |
| 114264 | } |
| 114265 | } |
| 114266 | if( db->init.busy || pTblName==0 ){ |
| 114267 | pIndex->pNext = pTab->pIndex; |
| 114268 | pTab->pIndex = pIndex; |
| @@ -114333,11 +114482,11 @@ | |
| 114333 | ** |
| 114334 | ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1 |
| 114335 | ** table but other parts we are having to guess at, then do not let the |
| 114336 | ** estimated number of rows in the table be less than 1000 (LogEst 99). |
| 114337 | ** Failure to do this can cause the indexes for which we do not have |
| 114338 | ** stat1 data to be ignored by the query planner. tag-20200527-1 |
| 114339 | */ |
| 114340 | x = pIdx->pTable->nRowLogEst; |
| 114341 | assert( 99==sqlite3LogEst(1000) ); |
| 114342 | if( x<99 ){ |
| 114343 | pIdx->pTable->nRowLogEst = x = 99; |
| @@ -120309,10 +120458,11 @@ | |
| 120309 | case OE_Cascade: |
| 120310 | if( !pChanges ){ |
| 120311 | pStep->op = TK_DELETE; |
| 120312 | break; |
| 120313 | } |
| 120314 | default: |
| 120315 | pStep->op = TK_UPDATE; |
| 120316 | } |
| 120317 | pStep->pTrig = pTrigger; |
| 120318 | pTrigger->pSchema = pTab->pSchema; |
| @@ -120581,11 +120731,11 @@ | |
| 120581 | for(i=1; i<iEnd; i++){ |
| 120582 | VdbeOp *pOp = sqlite3VdbeGetOp(v, i); |
| 120583 | assert( pOp!=0 ); |
| 120584 | if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ |
| 120585 | Index *pIndex; |
| 120586 | int tnum = pOp->p2; |
| 120587 | if( tnum==pTab->tnum ){ |
| 120588 | return 1; |
| 120589 | } |
| 120590 | for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ |
| 120591 | if( tnum==pIndex->tnum ){ |
| @@ -122013,11 +122163,11 @@ | |
| 122013 | sqlite3VdbeJumpHere(v, addr1); |
| 122014 | break; |
| 122015 | } |
| 122016 | case OE_Abort: |
| 122017 | sqlite3MayAbort(pParse); |
| 122018 | /* Fall through */ |
| 122019 | case OE_Rollback: |
| 122020 | case OE_Fail: { |
| 122021 | char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, |
| 122022 | pCol->zName); |
| 122023 | sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, |
| @@ -122241,11 +122391,11 @@ | |
| 122241 | VdbeCoverage(v); |
| 122242 | |
| 122243 | switch( onError ){ |
| 122244 | default: { |
| 122245 | onError = OE_Abort; |
| 122246 | /* Fall thru into the next case */ |
| 122247 | } |
| 122248 | case OE_Rollback: |
| 122249 | case OE_Abort: |
| 122250 | case OE_Fail: { |
| 122251 | testcase( onError==OE_Rollback ); |
| @@ -122302,11 +122452,11 @@ | |
| 122302 | break; |
| 122303 | } |
| 122304 | #ifndef SQLITE_OMIT_UPSERT |
| 122305 | case OE_Update: { |
| 122306 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); |
| 122307 | /* Fall through */ |
| 122308 | } |
| 122309 | #endif |
| 122310 | case OE_Ignore: { |
| 122311 | testcase( onError==OE_Ignore ); |
| 122312 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -122523,11 +122673,11 @@ | |
| 122523 | break; |
| 122524 | } |
| 122525 | #ifndef SQLITE_OMIT_UPSERT |
| 122526 | case OE_Update: { |
| 122527 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); |
| 122528 | /* Fall through */ |
| 122529 | } |
| 122530 | #endif |
| 122531 | case OE_Ignore: { |
| 122532 | testcase( onError==OE_Ignore ); |
| 122533 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -126204,17 +126354,23 @@ | |
| 126204 | ** |
| 126205 | ** Return the number of pages in the specified database. |
| 126206 | */ |
| 126207 | case PragTyp_PAGE_COUNT: { |
| 126208 | int iReg; |
| 126209 | sqlite3CodeVerifySchema(pParse, iDb); |
| 126210 | iReg = ++pParse->nMem; |
| 126211 | if( sqlite3Tolower(zLeft[0])=='p' ){ |
| 126212 | sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); |
| 126213 | }else{ |
| 126214 | sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, |
| 126215 | sqlite3AbsInt32(sqlite3Atoi(zRight))); |
| 126216 | } |
| 126217 | sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); |
| 126218 | break; |
| 126219 | } |
| 126220 | |
| @@ -127113,13 +127269,26 @@ | |
| 127113 | ** |
| 127114 | ** The "quick_check" is reduced version of |
| 127115 | ** integrity_check designed to detect most database corruption |
| 127116 | ** without the overhead of cross-checking indexes. Quick_check |
| 127117 | ** is linear time wherease integrity_check is O(NlogN). |
| 127118 | */ |
| 127119 | case PragTyp_INTEGRITY_CHECK: { |
| 127120 | int i, j, addr, mxErr; |
| 127121 | |
| 127122 | int isQuick = (sqlite3Tolower(zLeft[0])=='q'); |
| 127123 | |
| 127124 | /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", |
| 127125 | ** then iDb is set to the index of the database identified by <db>. |
| @@ -127138,13 +127307,17 @@ | |
| 127138 | pParse->nMem = 6; |
| 127139 | |
| 127140 | /* Set the maximum error count */ |
| 127141 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 127142 | if( zRight ){ |
| 127143 | sqlite3GetInt32(zRight, &mxErr); |
| 127144 | if( mxErr<=0 ){ |
| 127145 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 127146 | } |
| 127147 | } |
| 127148 | sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ |
| 127149 | |
| 127150 | /* Do an integrity check on each database file */ |
| @@ -127169,19 +127342,25 @@ | |
| 127169 | pTbls = &db->aDb[i].pSchema->tblHash; |
| 127170 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ |
| 127171 | Table *pTab = sqliteHashData(x); /* Current table */ |
| 127172 | Index *pIdx; /* An index on pTab */ |
| 127173 | int nIdx; /* Number of indexes on pTab */ |
| 127174 | if( HasRowid(pTab) ) cnt++; |
| 127175 | for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } |
| 127176 | if( nIdx>mxIdx ) mxIdx = nIdx; |
| 127177 | } |
| 127178 | aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); |
| 127179 | if( aRoot==0 ) break; |
| 127180 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ |
| 127181 | Table *pTab = sqliteHashData(x); |
| 127182 | Index *pIdx; |
| 127183 | if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; |
| 127184 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 127185 | aRoot[++cnt] = pIdx->tnum; |
| 127186 | } |
| 127187 | } |
| @@ -127211,10 +127390,11 @@ | |
| 127211 | int loopTop; |
| 127212 | int iDataCur, iIdxCur; |
| 127213 | int r1 = -1; |
| 127214 | |
| 127215 | if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ |
| 127216 | pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); |
| 127217 | sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, |
| 127218 | 1, 0, &iDataCur, &iIdxCur); |
| 127219 | /* reg[7] counts the number of entries in the table. |
| 127220 | ** reg[8+i] counts the number of entries in the i-th index |
| @@ -128272,11 +128452,17 @@ | |
| 128272 | sqlite3_stmt *pStmt; |
| 128273 | TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ |
| 128274 | |
| 128275 | assert( db->init.busy ); |
| 128276 | db->init.iDb = iDb; |
| 128277 | db->init.newTnum = sqlite3Atoi(argv[3]); |
| 128278 | db->init.orphanTrigger = 0; |
| 128279 | db->init.azInit = argv; |
| 128280 | pStmt = 0; |
| 128281 | TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); |
| 128282 | rc = db->errCode; |
| @@ -128305,16 +128491,21 @@ | |
| 128305 | ** been created when we processed the CREATE TABLE. All we have |
| 128306 | ** to do here is record the root page number for that index. |
| 128307 | */ |
| 128308 | Index *pIndex; |
| 128309 | pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); |
| 128310 | if( pIndex==0 |
| 128311 | || sqlite3GetInt32(argv[3],&pIndex->tnum)==0 |
| 128312 | || pIndex->tnum<2 |
| 128313 | || sqlite3IndexHasDuplicateRootPage(pIndex) |
| 128314 | ){ |
| 128315 | corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index"); |
| 128316 | } |
| 128317 | } |
| 128318 | return 0; |
| 128319 | } |
| 128320 | |
| @@ -128364,10 +128555,11 @@ | |
| 128364 | initData.iDb = iDb; |
| 128365 | initData.rc = SQLITE_OK; |
| 128366 | initData.pzErrMsg = pzErrMsg; |
| 128367 | initData.mInitFlags = mFlags; |
| 128368 | initData.nInitRow = 0; |
| 128369 | sqlite3InitCallback(&initData, 5, (char **)azArg, 0); |
| 128370 | db->mDbFlags &= mask; |
| 128371 | if( initData.rc ){ |
| 128372 | rc = initData.rc; |
| 128373 | goto error_out; |
| @@ -128486,10 +128678,11 @@ | |
| 128486 | } |
| 128487 | |
| 128488 | /* Read the schema information out of the schema tables |
| 128489 | */ |
| 128490 | assert( db->init.busy ); |
| 128491 | { |
| 128492 | char *zSql; |
| 128493 | zSql = sqlite3MPrintf(db, |
| 128494 | "SELECT*FROM\"%w\".%s ORDER BY rowid", |
| 128495 | db->aDb[iDb].zDbSName, zSchemaTabName); |
| @@ -129368,12 +129561,14 @@ | |
| 129368 | ** Return the index of a column in a table. Return -1 if the column |
| 129369 | ** is not contained in the table. |
| 129370 | */ |
| 129371 | static int columnIndex(Table *pTab, const char *zCol){ |
| 129372 | int i; |
| 129373 | for(i=0; i<pTab->nCol; i++){ |
| 129374 | if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; |
| 129375 | } |
| 129376 | return -1; |
| 129377 | } |
| 129378 | |
| 129379 | /* |
| @@ -130231,20 +130426,24 @@ | |
| 130231 | sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1); |
| 130232 | break; |
| 130233 | } |
| 130234 | |
| 130235 | case SRT_Upfrom: { |
| 130236 | #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT |
| 130237 | if( pSort ){ |
| 130238 | pushOntoSorter( |
| 130239 | pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); |
| 130240 | }else |
| 130241 | #endif |
| 130242 | { |
| 130243 | int i2 = pDest->iSDParm2; |
| 130244 | int r1 = sqlite3GetTempReg(pParse); |
| 130245 | sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1); |
| 130246 | if( i2<0 ){ |
| 130247 | sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult); |
| 130248 | }else{ |
| 130249 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2); |
| 130250 | } |
| @@ -130682,11 +130881,10 @@ | |
| 130682 | case SRT_Mem: { |
| 130683 | /* The LIMIT clause will terminate the loop for us */ |
| 130684 | break; |
| 130685 | } |
| 130686 | #endif |
| 130687 | #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT |
| 130688 | case SRT_Upfrom: { |
| 130689 | int i2 = pDest->iSDParm2; |
| 130690 | int r1 = sqlite3GetTempReg(pParse); |
| 130691 | sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1); |
| 130692 | if( i2<0 ){ |
| @@ -130694,11 +130892,10 @@ | |
| 130694 | }else{ |
| 130695 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2); |
| 130696 | } |
| 130697 | break; |
| 130698 | } |
| 130699 | #endif |
| 130700 | default: { |
| 130701 | assert( eDest==SRT_Output || eDest==SRT_Coroutine ); |
| 130702 | testcase( eDest==SRT_Output ); |
| 130703 | testcase( eDest==SRT_Coroutine ); |
| 130704 | if( eDest==SRT_Output ){ |
| @@ -132281,11 +132478,11 @@ | |
| 132281 | KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ |
| 132282 | KeyInfo *pKeyMerge; /* Comparison information for merging rows */ |
| 132283 | sqlite3 *db; /* Database connection */ |
| 132284 | ExprList *pOrderBy; /* The ORDER BY clause */ |
| 132285 | int nOrderBy; /* Number of terms in the ORDER BY clause */ |
| 132286 | int *aPermute; /* Mapping from ORDER BY terms to result set columns */ |
| 132287 | |
| 132288 | assert( p->pOrderBy!=0 ); |
| 132289 | assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ |
| 132290 | db = pParse->db; |
| 132291 | v = pParse->pVdbe; |
| @@ -132330,11 +132527,11 @@ | |
| 132330 | ** row of results comes from selectA or selectB. Also add explicit |
| 132331 | ** collations to the ORDER BY clause terms so that when the subqueries |
| 132332 | ** to the right and the left are evaluated, they use the correct |
| 132333 | ** collation. |
| 132334 | */ |
| 132335 | aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1)); |
| 132336 | if( aPermute ){ |
| 132337 | struct ExprList_item *pItem; |
| 132338 | aPermute[0] = nOrderBy; |
| 132339 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 132340 | assert( pItem->u.x.iOrderByCol>0 ); |
| @@ -133286,11 +133483,11 @@ | |
| 133286 | sqlite3AggInfoPersistWalkerInit(&w, pParse); |
| 133287 | sqlite3WalkSelect(&w,pSub1); |
| 133288 | sqlite3SelectDelete(db, pSub1); |
| 133289 | |
| 133290 | #if SELECTTRACE_ENABLED |
| 133291 | if( sqlite3SelectTrace & 0x100 ){ |
| 133292 | SELECTTRACE(0x100,pParse,p,("After flattening:\n")); |
| 133293 | sqlite3TreeViewSelect(0, p, 0); |
| 133294 | } |
| 133295 | #endif |
| 133296 | |
| @@ -134724,11 +134921,11 @@ | |
| 134724 | sWalker.pParse = pParse; |
| 134725 | sWalker.xExprCallback = havingToWhereExprCb; |
| 134726 | sWalker.u.pSelect = p; |
| 134727 | sqlite3WalkExpr(&sWalker, p->pHaving); |
| 134728 | #if SELECTTRACE_ENABLED |
| 134729 | if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){ |
| 134730 | SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); |
| 134731 | sqlite3TreeViewSelect(0, p, 0); |
| 134732 | } |
| 134733 | #endif |
| 134734 | } |
| @@ -134846,11 +135043,11 @@ | |
| 134846 | } |
| 134847 | p->pEList->a[0].pExpr = pExpr; |
| 134848 | p->selFlags &= ~SF_Aggregate; |
| 134849 | |
| 134850 | #if SELECTTRACE_ENABLED |
| 134851 | if( sqlite3SelectTrace & 0x400 ){ |
| 134852 | SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); |
| 134853 | sqlite3TreeViewSelect(0, p, 0); |
| 134854 | } |
| 134855 | #endif |
| 134856 | return 1; |
| @@ -134899,11 +135096,11 @@ | |
| 134899 | return 1; |
| 134900 | } |
| 134901 | if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; |
| 134902 | #if SELECTTRACE_ENABLED |
| 134903 | SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); |
| 134904 | if( sqlite3SelectTrace & 0x100 ){ |
| 134905 | sqlite3TreeViewSelect(0, p, 0); |
| 134906 | } |
| 134907 | #endif |
| 134908 | |
| 134909 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| @@ -134926,11 +135123,11 @@ | |
| 134926 | if( pParse->nErr || db->mallocFailed ){ |
| 134927 | goto select_end; |
| 134928 | } |
| 134929 | assert( p->pEList!=0 ); |
| 134930 | #if SELECTTRACE_ENABLED |
| 134931 | if( sqlite3SelectTrace & 0x104 ){ |
| 134932 | SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); |
| 134933 | sqlite3TreeViewSelect(0, p, 0); |
| 134934 | } |
| 134935 | #endif |
| 134936 | |
| @@ -134961,11 +135158,11 @@ | |
| 134961 | if( rc ){ |
| 134962 | assert( db->mallocFailed || pParse->nErr>0 ); |
| 134963 | goto select_end; |
| 134964 | } |
| 134965 | #if SELECTTRACE_ENABLED |
| 134966 | if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){ |
| 134967 | SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); |
| 134968 | sqlite3TreeViewSelect(0, p, 0); |
| 134969 | } |
| 134970 | #endif |
| 134971 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| @@ -135068,11 +135265,11 @@ | |
| 135068 | */ |
| 135069 | if( p->pPrior ){ |
| 135070 | rc = multiSelect(pParse, p, pDest); |
| 135071 | #if SELECTTRACE_ENABLED |
| 135072 | SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); |
| 135073 | if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ |
| 135074 | sqlite3TreeViewSelect(0, p, 0); |
| 135075 | } |
| 135076 | #endif |
| 135077 | if( p->pNext==0 ) ExplainQueryPlanPop(pParse); |
| 135078 | return rc; |
| @@ -135087,11 +135284,11 @@ | |
| 135087 | if( pTabList->nSrc>1 |
| 135088 | && OptimizationEnabled(db, SQLITE_PropagateConst) |
| 135089 | && propagateConstants(pParse, p) |
| 135090 | ){ |
| 135091 | #if SELECTTRACE_ENABLED |
| 135092 | if( sqlite3SelectTrace & 0x100 ){ |
| 135093 | SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); |
| 135094 | sqlite3TreeViewSelect(0, p, 0); |
| 135095 | } |
| 135096 | #endif |
| 135097 | }else{ |
| @@ -135175,11 +135372,11 @@ | |
| 135175 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 135176 | && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, |
| 135177 | (pItem->fg.jointype & JT_OUTER)!=0) |
| 135178 | ){ |
| 135179 | #if SELECTTRACE_ENABLED |
| 135180 | if( sqlite3SelectTrace & 0x100 ){ |
| 135181 | SELECTTRACE(0x100,pParse,p, |
| 135182 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| 135183 | sqlite3TreeViewSelect(0, p, 0); |
| 135184 | } |
| 135185 | #endif |
| @@ -135275,11 +135472,11 @@ | |
| 135275 | pGroupBy = p->pGroupBy; |
| 135276 | pHaving = p->pHaving; |
| 135277 | sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; |
| 135278 | |
| 135279 | #if SELECTTRACE_ENABLED |
| 135280 | if( sqlite3SelectTrace & 0x400 ){ |
| 135281 | SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); |
| 135282 | sqlite3TreeViewSelect(0, p, 0); |
| 135283 | } |
| 135284 | #endif |
| 135285 | |
| @@ -135311,11 +135508,11 @@ | |
| 135311 | ** the sDistinct.isTnct is still set. Hence, isTnct represents the |
| 135312 | ** original setting of the SF_Distinct flag, not the current setting */ |
| 135313 | assert( sDistinct.isTnct ); |
| 135314 | |
| 135315 | #if SELECTTRACE_ENABLED |
| 135316 | if( sqlite3SelectTrace & 0x400 ){ |
| 135317 | SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); |
| 135318 | sqlite3TreeViewSelect(0, p, 0); |
| 135319 | } |
| 135320 | #endif |
| 135321 | } |
| @@ -135559,11 +135756,11 @@ | |
| 135559 | sNC.ncFlags &= ~NC_InAggFunc; |
| 135560 | } |
| 135561 | pAggInfo->mxReg = pParse->nMem; |
| 135562 | if( db->mallocFailed ) goto select_end; |
| 135563 | #if SELECTTRACE_ENABLED |
| 135564 | if( sqlite3SelectTrace & 0x400 ){ |
| 135565 | int ii; |
| 135566 | SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); |
| 135567 | sqlite3TreeViewSelect(0, p, 0); |
| 135568 | for(ii=0; ii<pAggInfo->nColumn; ii++){ |
| 135569 | sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", |
| @@ -135823,11 +136020,11 @@ | |
| 135823 | const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
| 135824 | const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ |
| 135825 | Index *pIdx; /* Iterator variable */ |
| 135826 | KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ |
| 135827 | Index *pBest = 0; /* Best index found so far */ |
| 135828 | int iRoot = pTab->tnum; /* Root page of scanned b-tree */ |
| 135829 | |
| 135830 | sqlite3CodeVerifySchema(pParse, iDb); |
| 135831 | sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
| 135832 | |
| 135833 | /* Search for the index that has the lowest scan cost. |
| @@ -135855,11 +136052,11 @@ | |
| 135855 | iRoot = pBest->tnum; |
| 135856 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| 135857 | } |
| 135858 | |
| 135859 | /* Open a read-only cursor, execute the OP_Count, close the cursor. */ |
| 135860 | sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1); |
| 135861 | if( pKeyInfo ){ |
| 135862 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 135863 | } |
| 135864 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); |
| 135865 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| @@ -135978,11 +136175,11 @@ | |
| 135978 | } |
| 135979 | #endif |
| 135980 | |
| 135981 | #if SELECTTRACE_ENABLED |
| 135982 | SELECTTRACE(0x1,pParse,p,("end processing\n")); |
| 135983 | if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ |
| 135984 | sqlite3TreeViewSelect(0, p, 0); |
| 135985 | } |
| 135986 | #endif |
| 135987 | ExplainQueryPlanPop(pParse); |
| 135988 | return rc; |
| @@ -142375,11 +142572,11 @@ | |
| 142375 | if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) |
| 142376 | && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) |
| 142377 | ){ |
| 142378 | int i; |
| 142379 | Table *pTab = pIdx->pTable; |
| 142380 | int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1)); |
| 142381 | if( ai ){ |
| 142382 | ai[0] = pTab->nCol; |
| 142383 | for(i=0; i<pIdx->nColumn-1; i++){ |
| 142384 | int x1, x2; |
| 142385 | assert( pIdx->aiColumn[i]<pTab->nCol ); |
| @@ -151749,11 +151946,11 @@ | |
| 151749 | assert( pWin->pOwner==pExpr ); |
| 151750 | return WRC_Prune; |
| 151751 | } |
| 151752 | } |
| 151753 | } |
| 151754 | /* Fall through. */ |
| 151755 | |
| 151756 | case TK_AGG_FUNCTION: |
| 151757 | case TK_COLUMN: { |
| 151758 | int iCol = -1; |
| 151759 | if( p->pSub ){ |
| @@ -159966,10 +160163,11 @@ | |
| 159966 | *tokenType = TK_DOT; |
| 159967 | return 1; |
| 159968 | } |
| 159969 | /* If the next character is a digit, this is a floating point |
| 159970 | ** number that begins with ".". Fall thru into the next case */ |
| 159971 | } |
| 159972 | case CC_DIGIT: { |
| 159973 | testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); |
| 159974 | testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); |
| 159975 | testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); |
| @@ -160070,10 +160268,11 @@ | |
| 160070 | return i; |
| 160071 | } |
| 160072 | #endif |
| 160073 | /* If it is not a BLOB literal, then it must be an ID, since no |
| 160074 | ** SQL keywords start with the letter 'x'. Fall through */ |
| 160075 | } |
| 160076 | case CC_ID: { |
| 160077 | i = 1; |
| 160078 | break; |
| 160079 | } |
| @@ -162000,11 +162199,11 @@ | |
| 162000 | if( !sqlite3SafetyCheckSickOrOk(db) ){ |
| 162001 | return SQLITE_MISUSE_BKPT; |
| 162002 | } |
| 162003 | sqlite3_mutex_enter(db->mutex); |
| 162004 | if( db->mTrace & SQLITE_TRACE_CLOSE ){ |
| 162005 | db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); |
| 162006 | } |
| 162007 | |
| 162008 | /* Force xDisconnect calls on all virtual tables */ |
| 162009 | disconnectAllVtab(db); |
| 162010 | |
| @@ -162889,11 +163088,11 @@ | |
| 162889 | } |
| 162890 | #endif |
| 162891 | sqlite3_mutex_enter(db->mutex); |
| 162892 | pOld = db->pTraceArg; |
| 162893 | db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; |
| 162894 | db->xTrace = (int(*)(u32,void*,void*,void*))xTrace; |
| 162895 | db->pTraceArg = pArg; |
| 162896 | sqlite3_mutex_leave(db->mutex); |
| 162897 | return pOld; |
| 162898 | } |
| 162899 | #endif /* SQLITE_OMIT_DEPRECATED */ |
| @@ -162913,11 +163112,11 @@ | |
| 162913 | #endif |
| 162914 | sqlite3_mutex_enter(db->mutex); |
| 162915 | if( mTrace==0 ) xTrace = 0; |
| 162916 | if( xTrace==0 ) mTrace = 0; |
| 162917 | db->mTrace = mTrace; |
| 162918 | db->xTrace = xTrace; |
| 162919 | db->pTraceArg = pArg; |
| 162920 | sqlite3_mutex_leave(db->mutex); |
| 162921 | return SQLITE_OK; |
| 162922 | } |
| 162923 | |
| @@ -164889,10 +165088,16 @@ | |
| 164889 | /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int); |
| 164890 | ** |
| 164891 | ** Set or clear a flag that causes SQLite to verify that type, name, |
| 164892 | ** and tbl_name fields of the sqlite_schema table. This is normally |
| 164893 | ** on, but it is sometimes useful to turn it off for testing. |
| 164894 | */ |
| 164895 | case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: { |
| 164896 | sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int); |
| 164897 | break; |
| 164898 | } |
| @@ -166503,10 +166708,12 @@ | |
| 166503 | # define TESTONLY(X) |
| 166504 | #endif |
| 166505 | |
| 166506 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 166507 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 166508 | |
| 166509 | #endif /* SQLITE_AMALGAMATION */ |
| 166510 | |
| 166511 | #ifdef SQLITE_DEBUG |
| 166512 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -170146,11 +170353,11 @@ | |
| 170146 | }else if( p->zLanguageid==0 ){ |
| 170147 | sqlite3_result_int(pCtx, 0); |
| 170148 | break; |
| 170149 | }else{ |
| 170150 | iCol = p->nColumn; |
| 170151 | /* fall-through */ |
| 170152 | } |
| 170153 | |
| 170154 | default: |
| 170155 | /* A user column. Or, if this is a full-table scan, possibly the |
| 170156 | ** language-id column. Seek the cursor. */ |
| @@ -170389,13 +170596,17 @@ | |
| 170389 | } |
| 170390 | if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; |
| 170391 | |
| 170392 | switch( nVal ){ |
| 170393 | case 6: nToken = sqlite3_value_int(apVal[5]); |
| 170394 | case 5: iCol = sqlite3_value_int(apVal[4]); |
| 170395 | case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); |
| 170396 | case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); |
| 170397 | case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); |
| 170398 | } |
| 170399 | if( !zEllipsis || !zEnd || !zStart ){ |
| 170400 | sqlite3_result_error_nomem(pContext); |
| 170401 | }else if( nToken==0 ){ |
| @@ -172498,11 +172709,12 @@ | |
| 172498 | ** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK ); |
| 172499 | */ |
| 172500 | fts3EvalRestart(pCsr, pRoot, &rc); |
| 172501 | do { |
| 172502 | fts3EvalNextRow(pCsr, pRoot, &rc); |
| 172503 | assert( pRoot->bEof==0 ); |
| 172504 | }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); |
| 172505 | } |
| 172506 | } |
| 172507 | return rc; |
| 172508 | } |
| @@ -176607,11 +176819,12 @@ | |
| 176607 | rc = fts3tokQueryTokenizer((Fts3Hash*)pHash, zModule, &pMod, pzErr); |
| 176608 | } |
| 176609 | |
| 176610 | assert( (rc==SQLITE_OK)==(pMod!=0) ); |
| 176611 | if( rc==SQLITE_OK ){ |
| 176612 | const char * const *azArg = (const char * const *)&azDequote[1]; |
| 176613 | rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok); |
| 176614 | } |
| 176615 | |
| 176616 | if( rc==SQLITE_OK ){ |
| 176617 | pTab = (Fts3tokTable *)sqlite3_malloc(sizeof(Fts3tokTable)); |
| @@ -185252,10 +185465,14 @@ | |
| 185252 | #ifndef LARGEST_INT64 |
| 185253 | # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) |
| 185254 | # define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) |
| 185255 | #endif |
| 185256 | |
| 185257 | /* |
| 185258 | ** Versions of isspace(), isalnum() and isdigit() to which it is safe |
| 185259 | ** to pass signed char values. |
| 185260 | */ |
| 185261 | #ifdef sqlite3Isdigit |
| @@ -185670,11 +185887,11 @@ | |
| 185670 | case JSON_STRING: { |
| 185671 | if( pNode->jnFlags & JNODE_RAW ){ |
| 185672 | jsonAppendString(pOut, pNode->u.zJContent, pNode->n); |
| 185673 | break; |
| 185674 | } |
| 185675 | /* Fall through into the next case */ |
| 185676 | } |
| 185677 | case JSON_REAL: |
| 185678 | case JSON_INT: { |
| 185679 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 185680 | break; |
| @@ -185811,11 +186028,11 @@ | |
| 185811 | } |
| 185812 | if( pNode->u.zJContent[0]=='-' ){ i = -i; } |
| 185813 | sqlite3_result_int64(pCtx, i); |
| 185814 | int_done: |
| 185815 | break; |
| 185816 | int_as_real: /* fall through to real */; |
| 185817 | } |
| 185818 | case JSON_REAL: { |
| 185819 | double r; |
| 185820 | #ifdef SQLITE_AMALGAMATION |
| 185821 | const char *z = pNode->u.zJContent; |
| @@ -187514,10 +187731,11 @@ | |
| 187514 | jsonResult(&x); |
| 187515 | break; |
| 187516 | } |
| 187517 | /* For json_each() path and root are the same so fall through |
| 187518 | ** into the root case */ |
| 187519 | } |
| 187520 | default: { |
| 187521 | const char *zRoot = p->zRoot; |
| 187522 | if( zRoot==0 ) zRoot = "$"; |
| 187523 | sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); |
| @@ -187921,10 +188139,11 @@ | |
| 187921 | #endif |
| 187922 | |
| 187923 | /* #include <string.h> */ |
| 187924 | /* #include <stdio.h> */ |
| 187925 | /* #include <assert.h> */ |
| 187926 | |
| 187927 | /* The following macro is used to suppress compiler warnings. |
| 187928 | */ |
| 187929 | #ifndef UNUSED_PARAMETER |
| 187930 | # define UNUSED_PARAMETER(x) (void)(x) |
| @@ -188258,10 +188477,27 @@ | |
| 188258 | */ |
| 188259 | #ifndef SQLITE_AMALGAMATION |
| 188260 | # define testcase(X) |
| 188261 | #endif |
| 188262 | |
| 188263 | /* |
| 188264 | ** Macros to determine whether the machine is big or little endian, |
| 188265 | ** and whether or not that determination is run-time or compile-time. |
| 188266 | ** |
| 188267 | ** For best performance, an attempt is made to guess at the byte-order |
| @@ -205152,12 +205388,17 @@ | |
| 205152 | memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); |
| 205153 | p->in.iNext += nCopy; |
| 205154 | } |
| 205155 | |
| 205156 | p->apValue = (sqlite3_value**)p->tblhdr.aBuf; |
| 205157 | p->abPK = (u8*)&p->apValue[p->nCol*2]; |
| 205158 | p->zTab = (char*)&p->abPK[p->nCol]; |
| 205159 | return (p->rc = rc); |
| 205160 | } |
| 205161 | |
| 205162 | /* |
| 205163 | ** Advance the changeset iterator to the next change. |
| @@ -225482,11 +225723,11 @@ | |
| 225482 | int nArg, /* Number of args */ |
| 225483 | sqlite3_value **apUnused /* Function arguments */ |
| 225484 | ){ |
| 225485 | assert( nArg==0 ); |
| 225486 | UNUSED_PARAM2(nArg, apUnused); |
| 225487 | sqlite3_result_text(pCtx, "fts5: 2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3", -1, SQLITE_TRANSIENT); |
| 225488 | } |
| 225489 | |
| 225490 | /* |
| 225491 | ** Return true if zName is the extension on one of the shadow tables used |
| 225492 | ** by this module. |
| @@ -230265,12 +230506,12 @@ | |
| 230265 | } |
| 230266 | #endif /* SQLITE_CORE */ |
| 230267 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 230268 | |
| 230269 | /************** End of stmt.c ************************************************/ |
| 230270 | #if __LINE__!=230270 |
| 230271 | #undef SQLITE_SOURCE_ID |
| 230272 | #define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829alt2" |
| 230273 | #endif |
| 230274 | /* Return the source-id for this library */ |
| 230275 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 230276 | /************************** End of sqlite3.c ******************************/ |
| 230277 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -997,10 +997,19 @@ | |
| 997 | |
| 998 | #if defined(__OpenBSD__) && !defined(_BSD_SOURCE) |
| 999 | # define _BSD_SOURCE |
| 1000 | #endif |
| 1001 | |
| 1002 | /* |
| 1003 | ** Macro to disable warnings about missing "break" at the end of a "case". |
| 1004 | */ |
| 1005 | #if GCC_VERSION>=7000000 |
| 1006 | # define deliberate_fall_through __attribute__((fallthrough)); |
| 1007 | #else |
| 1008 | # define deliberate_fall_through |
| 1009 | #endif |
| 1010 | |
| 1011 | /* |
| 1012 | ** For MinGW, check to see if we can include the header file containing its |
| 1013 | ** version information, among other things. Normally, this internal MinGW |
| 1014 | ** header file would [only] be included automatically by other MinGW header |
| 1015 | ** files; however, the contained version information is now required by this |
| @@ -1162,11 +1171,11 @@ | |
| 1171 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 1172 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 1173 | */ |
| 1174 | #define SQLITE_VERSION "3.33.0" |
| 1175 | #define SQLITE_VERSION_NUMBER 3033000 |
| 1176 | #define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" |
| 1177 | |
| 1178 | /* |
| 1179 | ** CAPI3REF: Run-Time Library Version Numbers |
| 1180 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 1181 | ** |
| @@ -14532,11 +14541,11 @@ | |
| 14541 | # define SELECTTRACE_ENABLED 0 |
| 14542 | #endif |
| 14543 | #if defined(SQLITE_ENABLE_SELECTTRACE) |
| 14544 | # define SELECTTRACE_ENABLED 1 |
| 14545 | # define SELECTTRACE(K,P,S,X) \ |
| 14546 | if(sqlite3_unsupported_selecttrace&(K)) \ |
| 14547 | sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ |
| 14548 | sqlite3DebugPrintf X |
| 14549 | #else |
| 14550 | # define SELECTTRACE(K,P,S,X) |
| 14551 | # define SELECTTRACE_ENABLED 0 |
| @@ -14595,11 +14604,11 @@ | |
| 14604 | ** one parameter that destructors normally want. So we have to introduce |
| 14605 | ** this magic value that the code knows to handle differently. Any |
| 14606 | ** pointer will work here as long as it is distinct from SQLITE_STATIC |
| 14607 | ** and SQLITE_TRANSIENT. |
| 14608 | */ |
| 14609 | #define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomFault) |
| 14610 | |
| 14611 | /* |
| 14612 | ** When SQLITE_OMIT_WSD is defined, it means that the target platform does |
| 14613 | ** not support Writable Static Data (WSD) such as global and static variables. |
| 14614 | ** All variables must either be on the stack or dynamically allocated from |
| @@ -14735,10 +14744,257 @@ | |
| 14744 | /* |
| 14745 | ** Defer sourcing vdbe.h and btree.h until after the "u8" and |
| 14746 | ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque |
| 14747 | ** pointer types (i.e. FuncDef) defined above. |
| 14748 | */ |
| 14749 | /************** Include pager.h in the middle of sqliteInt.h *****************/ |
| 14750 | /************** Begin file pager.h *******************************************/ |
| 14751 | /* |
| 14752 | ** 2001 September 15 |
| 14753 | ** |
| 14754 | ** The author disclaims copyright to this source code. In place of |
| 14755 | ** a legal notice, here is a blessing: |
| 14756 | ** |
| 14757 | ** May you do good and not evil. |
| 14758 | ** May you find forgiveness for yourself and forgive others. |
| 14759 | ** May you share freely, never taking more than you give. |
| 14760 | ** |
| 14761 | ************************************************************************* |
| 14762 | ** This header file defines the interface that the sqlite page cache |
| 14763 | ** subsystem. The page cache subsystem reads and writes a file a page |
| 14764 | ** at a time and provides a journal for rollback. |
| 14765 | */ |
| 14766 | |
| 14767 | #ifndef SQLITE_PAGER_H |
| 14768 | #define SQLITE_PAGER_H |
| 14769 | |
| 14770 | /* |
| 14771 | ** Default maximum size for persistent journal files. A negative |
| 14772 | ** value means no limit. This value may be overridden using the |
| 14773 | ** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". |
| 14774 | */ |
| 14775 | #ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT |
| 14776 | #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 |
| 14777 | #endif |
| 14778 | |
| 14779 | /* |
| 14780 | ** The type used to represent a page number. The first page in a file |
| 14781 | ** is called page 1. 0 is used to represent "not a page". |
| 14782 | */ |
| 14783 | typedef u32 Pgno; |
| 14784 | |
| 14785 | /* |
| 14786 | ** Each open file is managed by a separate instance of the "Pager" structure. |
| 14787 | */ |
| 14788 | typedef struct Pager Pager; |
| 14789 | |
| 14790 | /* |
| 14791 | ** Handle type for pages. |
| 14792 | */ |
| 14793 | typedef struct PgHdr DbPage; |
| 14794 | |
| 14795 | /* |
| 14796 | ** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is |
| 14797 | ** reserved for working around a windows/posix incompatibility). It is |
| 14798 | ** used in the journal to signify that the remainder of the journal file |
| 14799 | ** is devoted to storing a super-journal name - there are no more pages to |
| 14800 | ** roll back. See comments for function writeSuperJournal() in pager.c |
| 14801 | ** for details. |
| 14802 | */ |
| 14803 | #define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) |
| 14804 | |
| 14805 | /* |
| 14806 | ** Allowed values for the flags parameter to sqlite3PagerOpen(). |
| 14807 | ** |
| 14808 | ** NOTE: These values must match the corresponding BTREE_ values in btree.h. |
| 14809 | */ |
| 14810 | #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ |
| 14811 | #define PAGER_MEMORY 0x0002 /* In-memory database */ |
| 14812 | |
| 14813 | /* |
| 14814 | ** Valid values for the second argument to sqlite3PagerLockingMode(). |
| 14815 | */ |
| 14816 | #define PAGER_LOCKINGMODE_QUERY -1 |
| 14817 | #define PAGER_LOCKINGMODE_NORMAL 0 |
| 14818 | #define PAGER_LOCKINGMODE_EXCLUSIVE 1 |
| 14819 | |
| 14820 | /* |
| 14821 | ** Numeric constants that encode the journalmode. |
| 14822 | ** |
| 14823 | ** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY) |
| 14824 | ** are exposed in the API via the "PRAGMA journal_mode" command and |
| 14825 | ** therefore cannot be changed without a compatibility break. |
| 14826 | */ |
| 14827 | #define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ |
| 14828 | #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ |
| 14829 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 14830 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 14831 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 14832 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 14833 | #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ |
| 14834 | |
| 14835 | /* |
| 14836 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 14837 | */ |
| 14838 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| 14839 | #define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ |
| 14840 | |
| 14841 | /* |
| 14842 | ** Flags for sqlite3PagerSetFlags() |
| 14843 | ** |
| 14844 | ** Value constraints (enforced via assert()): |
| 14845 | ** PAGER_FULLFSYNC == SQLITE_FullFSync |
| 14846 | ** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync |
| 14847 | ** PAGER_CACHE_SPILL == SQLITE_CacheSpill |
| 14848 | */ |
| 14849 | #define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */ |
| 14850 | #define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */ |
| 14851 | #define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */ |
| 14852 | #define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */ |
| 14853 | #define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */ |
| 14854 | #define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */ |
| 14855 | #define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */ |
| 14856 | #define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */ |
| 14857 | #define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */ |
| 14858 | |
| 14859 | /* |
| 14860 | ** The remainder of this file contains the declarations of the functions |
| 14861 | ** that make up the Pager sub-system API. See source code comments for |
| 14862 | ** a detailed description of each routine. |
| 14863 | */ |
| 14864 | |
| 14865 | /* Open and close a Pager connection. */ |
| 14866 | SQLITE_PRIVATE int sqlite3PagerOpen( |
| 14867 | sqlite3_vfs*, |
| 14868 | Pager **ppPager, |
| 14869 | const char*, |
| 14870 | int, |
| 14871 | int, |
| 14872 | int, |
| 14873 | void(*)(DbPage*) |
| 14874 | ); |
| 14875 | SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*); |
| 14876 | SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); |
| 14877 | |
| 14878 | /* Functions used to configure a Pager object. */ |
| 14879 | SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); |
| 14880 | SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); |
| 14881 | SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); |
| 14882 | SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); |
| 14883 | SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); |
| 14884 | SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); |
| 14885 | SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); |
| 14886 | SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned); |
| 14887 | SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); |
| 14888 | SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); |
| 14889 | SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); |
| 14890 | SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); |
| 14891 | SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); |
| 14892 | SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); |
| 14893 | SQLITE_PRIVATE int sqlite3PagerFlush(Pager*); |
| 14894 | |
| 14895 | /* Functions used to obtain and release page references. */ |
| 14896 | SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); |
| 14897 | SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); |
| 14898 | SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); |
| 14899 | SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); |
| 14900 | SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*); |
| 14901 | SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*); |
| 14902 | |
| 14903 | /* Operations on page references. */ |
| 14904 | SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); |
| 14905 | SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); |
| 14906 | SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); |
| 14907 | SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); |
| 14908 | SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); |
| 14909 | SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); |
| 14910 | |
| 14911 | /* Functions used to manage pager transactions and savepoints. */ |
| 14912 | SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); |
| 14913 | SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); |
| 14914 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int); |
| 14915 | SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); |
| 14916 | SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper); |
| 14917 | SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); |
| 14918 | SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); |
| 14919 | SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); |
| 14920 | SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); |
| 14921 | SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); |
| 14922 | |
| 14923 | #ifndef SQLITE_OMIT_WAL |
| 14924 | SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); |
| 14925 | SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); |
| 14926 | SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); |
| 14927 | SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); |
| 14928 | SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
| 14929 | # ifdef SQLITE_ENABLE_SNAPSHOT |
| 14930 | SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot); |
| 14931 | SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot); |
| 14932 | SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager); |
| 14933 | SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); |
| 14934 | SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager); |
| 14935 | # endif |
| 14936 | #endif |
| 14937 | |
| 14938 | #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT) |
| 14939 | SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int); |
| 14940 | SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*); |
| 14941 | #else |
| 14942 | # define sqlite3PagerWalWriteLock(y,z) SQLITE_OK |
| 14943 | # define sqlite3PagerWalDb(x,y) |
| 14944 | #endif |
| 14945 | |
| 14946 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 14947 | SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); |
| 14948 | #endif |
| 14949 | |
| 14950 | #ifdef SQLITE_ENABLE_ZIPVFS |
| 14951 | SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); |
| 14952 | #endif |
| 14953 | |
| 14954 | /* Functions used to query pager state and configuration. */ |
| 14955 | SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); |
| 14956 | SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); |
| 14957 | #ifdef SQLITE_DEBUG |
| 14958 | SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); |
| 14959 | #endif |
| 14960 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 14961 | SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int); |
| 14962 | SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 14963 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| 14964 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); |
| 14965 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 14966 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 14967 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 14968 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 14969 | SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); |
| 14970 | SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); |
| 14971 | |
| 14972 | /* Functions used to truncate the database file. */ |
| 14973 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 14974 | |
| 14975 | SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); |
| 14976 | |
| 14977 | /* Functions to support testing and debugging. */ |
| 14978 | #if !defined(NDEBUG) || defined(SQLITE_TEST) |
| 14979 | SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); |
| 14980 | SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); |
| 14981 | #endif |
| 14982 | #ifdef SQLITE_TEST |
| 14983 | SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); |
| 14984 | SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); |
| 14985 | void disable_simulated_io_errors(void); |
| 14986 | void enable_simulated_io_errors(void); |
| 14987 | #else |
| 14988 | # define disable_simulated_io_errors() |
| 14989 | # define enable_simulated_io_errors() |
| 14990 | #endif |
| 14991 | |
| 14992 | #endif /* SQLITE_PAGER_H */ |
| 14993 | |
| 14994 | /************** End of pager.h ***********************************************/ |
| 14995 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 14996 | /************** Include btree.h in the middle of sqliteInt.h *****************/ |
| 14997 | /************** Begin file btree.h *******************************************/ |
| 14998 | /* |
| 14999 | ** 2001 September 15 |
| 15000 | ** |
| @@ -14810,12 +15066,12 @@ | |
| 15066 | SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); |
| 15067 | #endif |
| 15068 | SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned); |
| 15069 | SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); |
| 15070 | SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); |
| 15071 | SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno); |
| 15072 | SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree*); |
| 15073 | SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); |
| 15074 | SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*); |
| 15075 | SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); |
| 15076 | SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); |
| 15077 | SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); |
| @@ -14823,11 +15079,11 @@ | |
| 15079 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char*); |
| 15080 | SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); |
| 15081 | SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); |
| 15082 | SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int); |
| 15083 | SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); |
| 15084 | SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags); |
| 15085 | SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); |
| 15086 | SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); |
| 15087 | SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); |
| 15088 | SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); |
| 15089 | SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); |
| @@ -14964,11 +15220,11 @@ | |
| 15220 | #define BTREE_WRCSR 0x00000004 /* read-write cursor */ |
| 15221 | #define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */ |
| 15222 | |
| 15223 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 15224 | Btree*, /* BTree containing table to open */ |
| 15225 | Pgno iTable, /* Index of root page */ |
| 15226 | int wrFlag, /* 1 for writing. 0 for read-only */ |
| 15227 | struct KeyInfo*, /* First argument to compare function */ |
| 15228 | BtCursor *pCursor /* Space to write cursor structure */ |
| 15229 | ); |
| 15230 | SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void); |
| @@ -15055,11 +15311,11 @@ | |
| 15311 | SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); |
| 15312 | SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); |
| 15313 | SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); |
| 15314 | SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); |
| 15315 | |
| 15316 | SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); |
| 15317 | SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); |
| 15318 | SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); |
| 15319 | |
| 15320 | #ifndef SQLITE_OMIT_INCRBLOB |
| 15321 | SQLITE_PRIVATE int sqlite3BtreePayloadChecked(BtCursor*, u32 offset, u32 amt, void*); |
| @@ -15192,11 +15448,11 @@ | |
| 15448 | sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */ |
| 15449 | CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ |
| 15450 | Mem *pMem; /* Used when p4type is P4_MEM */ |
| 15451 | VTable *pVtab; /* Used when p4type is P4_VTAB */ |
| 15452 | KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ |
| 15453 | u32 *ai; /* Used when p4type is P4_INTARRAY */ |
| 15454 | SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ |
| 15455 | Table *pTab; /* Used when p4type is P4_TABLE */ |
| 15456 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 15457 | Expr *pExpr; /* Used when p4type is P4_EXPR */ |
| 15458 | #endif |
| @@ -15756,257 +16012,10 @@ | |
| 16012 | #endif |
| 16013 | |
| 16014 | #endif /* SQLITE_VDBE_H */ |
| 16015 | |
| 16016 | /************** End of vdbe.h ************************************************/ |
| 16017 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 16018 | /************** Include pcache.h in the middle of sqliteInt.h ****************/ |
| 16019 | /************** Begin file pcache.h ******************************************/ |
| 16020 | /* |
| 16021 | ** 2008 August 05 |
| @@ -16843,11 +16852,11 @@ | |
| 16852 | int nChange; /* Value returned by sqlite3_changes() */ |
| 16853 | int nTotalChange; /* Value returned by sqlite3_total_changes() */ |
| 16854 | int aLimit[SQLITE_N_LIMIT]; /* Limits */ |
| 16855 | int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */ |
| 16856 | struct sqlite3InitInfo { /* Information used during initialization */ |
| 16857 | Pgno newTnum; /* Rootpage of table being initialized */ |
| 16858 | u8 iDb; /* Which db file is being initialized */ |
| 16859 | u8 busy; /* TRUE if currently initializing */ |
| 16860 | unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */ |
| 16861 | unsigned imposterTable : 1; /* Building an imposter table */ |
| 16862 | unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */ |
| @@ -16858,11 +16867,14 @@ | |
| 16867 | int nVdbeWrite; /* Number of active VDBEs that read and write */ |
| 16868 | int nVdbeExec; /* Number of nested calls to VdbeExec() */ |
| 16869 | int nVDestroy; /* Number of active OP_VDestroy operations */ |
| 16870 | int nExtension; /* Number of loaded extensions */ |
| 16871 | void **aExtension; /* Array of shared library handles */ |
| 16872 | union { |
| 16873 | void (*xLegacy)(void*,const char*); /* Legacy trace function */ |
| 16874 | int (*xV2)(u32,void*,void*,void*); /* V2 Trace function */ |
| 16875 | } trace; |
| 16876 | void *pTraceArg; /* Argument to the trace function */ |
| 16877 | #ifndef SQLITE_OMIT_DEPRECATED |
| 16878 | void (*xProfile)(void*,const char*,u64); /* Profiling function */ |
| 16879 | void *pProfileArg; /* Argument to profile function */ |
| 16880 | #endif |
| @@ -17482,11 +17494,11 @@ | |
| 17494 | Select *pSelect; /* NULL for tables. Points to definition if a view. */ |
| 17495 | FKey *pFKey; /* Linked list of all foreign keys in this table */ |
| 17496 | char *zColAff; /* String defining the affinity of each column */ |
| 17497 | ExprList *pCheck; /* All CHECK constraints */ |
| 17498 | /* ... also used as column name list in a VIEW */ |
| 17499 | Pgno tnum; /* Root BTree page for this table */ |
| 17500 | u32 nTabRef; /* Number of pointers to this Table */ |
| 17501 | u32 tabFlags; /* Mask of TF_* values */ |
| 17502 | i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ |
| 17503 | i16 nCol; /* Number of columns in this table */ |
| 17504 | i16 nNVCol; /* Number of columns that are not VIRTUAL */ |
| @@ -17775,11 +17787,11 @@ | |
| 17787 | Schema *pSchema; /* Schema containing this index */ |
| 17788 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 17789 | const char **azColl; /* Array of collation sequence names for index */ |
| 17790 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 17791 | ExprList *aColExpr; /* Column expressions */ |
| 17792 | Pgno tnum; /* DB Page containing root of this index */ |
| 17793 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 17794 | u16 nKeyCol; /* Number of columns forming the key */ |
| 17795 | u16 nColumn; /* Number of columns stored in the index */ |
| 17796 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 17797 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| @@ -17901,15 +17913,10 @@ | |
| 17913 | int nFunc; /* Number of entries in aFunc[] */ |
| 17914 | u32 selId; /* Select to which this AggInfo belongs */ |
| 17915 | AggInfo *pNext; /* Next in list of them all */ |
| 17916 | }; |
| 17917 | |
| 17918 | /* |
| 17919 | ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. |
| 17920 | ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater |
| 17921 | ** than 32767 we have to make it 32-bit. 16-bit is preferred because |
| 17922 | ** it uses less memory in the Expr object, which is a big memory user |
| @@ -18744,13 +18751,11 @@ | |
| 18751 | |
| 18752 | Token sLastToken; /* The last token parsed */ |
| 18753 | ynVar nVar; /* Number of '?' variables seen in the SQL so far */ |
| 18754 | u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ |
| 18755 | u8 explain; /* True if the EXPLAIN flag is found on the query */ |
| 18756 | u8 eParseMode; /* PARSE_MODE_XXX constant */ |
| 18757 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 18758 | int nVtabLock; /* Number of virtual tables to lock */ |
| 18759 | #endif |
| 18760 | int nHeight; /* Expression tree height of current sub-select */ |
| 18761 | #ifndef SQLITE_OMIT_EXPLAIN |
| @@ -18990,10 +18995,11 @@ | |
| 18995 | char **pzErrMsg; /* Error message stored here */ |
| 18996 | int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ |
| 18997 | int rc; /* Result code stored here */ |
| 18998 | u32 mInitFlags; /* Flags controlling error messages */ |
| 18999 | u32 nInitRow; /* Number of rows processed */ |
| 19000 | Pgno mxPage; /* Maximum page number. 0 for no limit. */ |
| 19001 | } InitData; |
| 19002 | |
| 19003 | /* |
| 19004 | ** Allowed values for mInitFlags |
| 19005 | */ |
| @@ -19823,12 +19829,14 @@ | |
| 19829 | SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); |
| 19830 | SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); |
| 19831 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); |
| 19832 | SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); |
| 19833 | SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); |
| 19834 | SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); |
| 19835 | SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); |
| 19836 | SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); |
| 19837 | SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); |
| 19838 | SQLITE_PRIVATE int sqlite3Atoi(const char*); |
| 19839 | #ifndef SQLITE_OMIT_UTF16 |
| 19840 | SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); |
| 19841 | #endif |
| 19842 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); |
| @@ -19944,19 +19952,19 @@ | |
| 19952 | SQLITE_PRIVATE const char sqlite3StrBINARY[]; |
| 19953 | SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; |
| 19954 | SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; |
| 19955 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; |
| 19956 | SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions; |
| 19957 | SQLITE_API extern u32 sqlite3_unsupported_selecttrace; |
| 19958 | #ifndef SQLITE_OMIT_WSD |
| 19959 | SQLITE_PRIVATE int sqlite3PendingByte; |
| 19960 | #endif |
| 19961 | #endif /* SQLITE_AMALGAMATION */ |
| 19962 | #ifdef VDBE_PROFILE |
| 19963 | SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt; |
| 19964 | #endif |
| 19965 | SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno); |
| 19966 | SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); |
| 19967 | SQLITE_PRIVATE void sqlite3AlterFunctions(void); |
| 19968 | SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); |
| 19969 | SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); |
| 19970 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| @@ -20066,11 +20074,11 @@ | |
| 20074 | #else |
| 20075 | # define sqlite3CloseExtensions(X) |
| 20076 | #endif |
| 20077 | |
| 20078 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 20079 | SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, Pgno, u8, const char *); |
| 20080 | #else |
| 20081 | #define sqlite3TableLock(v,w,x,y,z) |
| 20082 | #endif |
| 20083 | |
| 20084 | #ifdef SQLITE_TEST |
| @@ -20661,11 +20669,11 @@ | |
| 20669 | #endif |
| 20670 | |
| 20671 | /* |
| 20672 | ** Flags for select tracing and the ".selecttrace" macro of the CLI |
| 20673 | */ |
| 20674 | SQLITE_API u32 sqlite3_unsupported_selecttrace = 0; |
| 20675 | |
| 20676 | /* #include "opcodes.h" */ |
| 20677 | /* |
| 20678 | ** Properties of opcodes. The OPFLG_INITIALIZER macro is |
| 20679 | ** created by mkopcodeh.awk during compilation. Data is obtained |
| @@ -20788,11 +20796,11 @@ | |
| 20796 | Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ |
| 20797 | Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ |
| 20798 | Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */ |
| 20799 | Btree *pBtx; /* Separate file holding temporary table */ |
| 20800 | i64 seqCount; /* Sequence counter */ |
| 20801 | u32 *aAltMap; /* Mapping from table to index column numbers */ |
| 20802 | |
| 20803 | /* Cached OP_Column parse information is only valid if cacheStatus matches |
| 20804 | ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of |
| 20805 | ** CACHE_STALE (0) and so setting cacheStatus=CACHE_STALE guarantees that |
| 20806 | ** the cache is out of date. */ |
| @@ -21184,11 +21192,11 @@ | |
| 21192 | */ |
| 21193 | SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...); |
| 21194 | SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); |
| 21195 | void sqliteVdbePopStack(Vdbe*,int); |
| 21196 | SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*); |
| 21197 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, u32*); |
| 21198 | SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); |
| 21199 | SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); |
| 21200 | SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8); |
| 21201 | SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32); |
| 21202 | SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); |
| @@ -21660,11 +21668,11 @@ | |
| 21668 | ** pagers the database handle is connected to. *pHighwater is always set |
| 21669 | ** to zero. |
| 21670 | */ |
| 21671 | case SQLITE_DBSTATUS_CACHE_SPILL: |
| 21672 | op = SQLITE_DBSTATUS_CACHE_WRITE+1; |
| 21673 | /* no break */ deliberate_fall_through |
| 21674 | case SQLITE_DBSTATUS_CACHE_HIT: |
| 21675 | case SQLITE_DBSTATUS_CACHE_MISS: |
| 21676 | case SQLITE_DBSTATUS_CACHE_WRITE:{ |
| 21677 | int i; |
| 21678 | int nRet = 0; |
| @@ -22816,12 +22824,12 @@ | |
| 22824 | break; |
| 22825 | } |
| 22826 | case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break; |
| 22827 | case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break; |
| 22828 | case 's': { |
| 22829 | i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); |
| 22830 | sqlite3Int64ToText(iS, &z[j]); |
| 22831 | j += sqlite3Strlen30(&z[j]); |
| 22832 | break; |
| 22833 | } |
| 22834 | case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break; |
| 22835 | case 'w': { |
| @@ -28537,15 +28545,15 @@ | |
| 28545 | assert( precision>=(-1) ); |
| 28546 | switch( xtype ){ |
| 28547 | case etPOINTER: |
| 28548 | flag_long = sizeof(char*)==sizeof(i64) ? 2 : |
| 28549 | sizeof(char*)==sizeof(long int) ? 1 : 0; |
| 28550 | /* no break */ deliberate_fall_through |
| 28551 | case etORDINAL: |
| 28552 | case etRADIX: |
| 28553 | cThousand = 0; |
| 28554 | /* no break */ deliberate_fall_through |
| 28555 | case etDECIMAL: |
| 28556 | if( infop->flags & FLAG_SIGNED ){ |
| 28557 | i64 v; |
| 28558 | if( bArgList ){ |
| 28559 | v = getIntArg(pArgList); |
| @@ -31770,10 +31778,34 @@ | |
| 31778 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 31779 | } |
| 31780 | #if defined(_MSC_VER) |
| 31781 | #pragma warning(default : 4756) |
| 31782 | #endif |
| 31783 | |
| 31784 | /* |
| 31785 | ** Render an signed 64-bit integer as text. Store the result in zOut[]. |
| 31786 | ** |
| 31787 | ** The caller must ensure that zOut[] is at least 21 bytes in size. |
| 31788 | */ |
| 31789 | SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ |
| 31790 | int i; |
| 31791 | u64 x; |
| 31792 | char zTemp[22]; |
| 31793 | if( v<0 ){ |
| 31794 | x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; |
| 31795 | }else{ |
| 31796 | x = v; |
| 31797 | } |
| 31798 | i = sizeof(zTemp)-2; |
| 31799 | zTemp[sizeof(zTemp)-1] = 0; |
| 31800 | do{ |
| 31801 | zTemp[i--] = (x%10) + '0'; |
| 31802 | x = x/10; |
| 31803 | }while( x ); |
| 31804 | if( v<0 ) zTemp[i--] = '-'; |
| 31805 | memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); |
| 31806 | } |
| 31807 | |
| 31808 | /* |
| 31809 | ** Compare the 19-character string zNum against the text representation |
| 31810 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| 31811 | ** if zNum is less than, equal to, or greater than the string. |
| @@ -32011,13 +32043,31 @@ | |
| 32043 | ** Return a 32-bit integer value extracted from a string. If the |
| 32044 | ** string is not an integer, just return 0. |
| 32045 | */ |
| 32046 | SQLITE_PRIVATE int sqlite3Atoi(const char *z){ |
| 32047 | int x = 0; |
| 32048 | sqlite3GetInt32(z, &x); |
| 32049 | return x; |
| 32050 | } |
| 32051 | |
| 32052 | /* |
| 32053 | ** Try to convert z into an unsigned 32-bit integer. Return true on |
| 32054 | ** success and false if there is an error. |
| 32055 | ** |
| 32056 | ** Only decimal notation is accepted. |
| 32057 | */ |
| 32058 | SQLITE_PRIVATE int sqlite3GetUInt32(const char *z, u32 *pI){ |
| 32059 | u64 v = 0; |
| 32060 | int i; |
| 32061 | for(i=0; sqlite3Isdigit(z[i]); i++){ |
| 32062 | v = v*10 + z[i] - '0'; |
| 32063 | if( v>4294967296LL ){ *pI = 0; return 0; } |
| 32064 | } |
| 32065 | if( i==0 || z[i]!=0 ){ *pI = 0; return 0; } |
| 32066 | *pI = (u32)v; |
| 32067 | return 1; |
| 32068 | } |
| 32069 | |
| 32070 | /* |
| 32071 | ** The variable-length integer encoding is as follows: |
| 32072 | ** |
| 32073 | ** KEY: |
| @@ -39188,11 +39238,11 @@ | |
| 39238 | } |
| 39239 | #endif |
| 39240 | if( rc!=SQLITE_OK ){ |
| 39241 | if( h>=0 ) robust_close(pNew, h, __LINE__); |
| 39242 | }else{ |
| 39243 | pId->pMethods = pLockingStyle; |
| 39244 | OpenCounter(+1); |
| 39245 | verifyDbFile(pNew); |
| 39246 | } |
| 39247 | return rc; |
| 39248 | } |
| @@ -46899,11 +46949,11 @@ | |
| 46949 | { |
| 46950 | sqlite3_free(zConverted); |
| 46951 | } |
| 46952 | |
| 46953 | sqlite3_free(zTmpname); |
| 46954 | id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod; |
| 46955 | pFile->pVfs = pVfs; |
| 46956 | pFile->h = h; |
| 46957 | if( isReadonly ){ |
| 46958 | pFile->ctrlFlags |= WINFILE_RDONLY; |
| 46959 | } |
| @@ -48125,11 +48175,11 @@ | |
| 48175 | } |
| 48176 | memset(p, 0, sizeof(*p)); |
| 48177 | p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE; |
| 48178 | assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ |
| 48179 | *pOutFlags = flags | SQLITE_OPEN_MEMORY; |
| 48180 | pFile->pMethods = &memdb_io_methods; |
| 48181 | p->szMax = sqlite3GlobalConfig.mxMemdbSize; |
| 48182 | return SQLITE_OK; |
| 48183 | } |
| 48184 | |
| 48185 | #if 0 /* Only used to delete rollback journals, super-journals, and WAL |
| @@ -52442,15 +52492,10 @@ | |
| 52492 | # define USEFETCH(x) ((x)->bUseFetch) |
| 52493 | #else |
| 52494 | # define USEFETCH(x) 0 |
| 52495 | #endif |
| 52496 | |
| 52497 | /* |
| 52498 | ** The argument to this macro is a file descriptor (type sqlite3_file*). |
| 52499 | ** Return 0 if it is not open, or non-zero (but not 1) if it is. |
| 52500 | ** |
| 52501 | ** This is so that expressions can be written as: |
| @@ -54153,16 +54198,17 @@ | |
| 54198 | |
| 54199 | /* Allocate space for both the pJournal and pSuper file descriptors. |
| 54200 | ** If successful, open the super-journal file for reading. |
| 54201 | */ |
| 54202 | pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2); |
| 54203 | if( !pSuper ){ |
| 54204 | rc = SQLITE_NOMEM_BKPT; |
| 54205 | pJournal = 0; |
| 54206 | }else{ |
| 54207 | const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); |
| 54208 | rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0); |
| 54209 | pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile); |
| 54210 | } |
| 54211 | if( rc!=SQLITE_OK ) goto delsuper_out; |
| 54212 | |
| 54213 | /* Load the entire super-journal file into space obtained from |
| 54214 | ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain |
| @@ -55419,11 +55465,11 @@ | |
| 55465 | ** Make no changes if mxPage is zero or negative. And never reduce the |
| 55466 | ** maximum page count below the current size of the database. |
| 55467 | ** |
| 55468 | ** Regardless of mxPage, return the current maximum page count. |
| 55469 | */ |
| 55470 | SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){ |
| 55471 | if( mxPage>0 ){ |
| 55472 | pPager->mxPgno = mxPage; |
| 55473 | } |
| 55474 | assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ |
| 55475 | /* assert( pPager->mxPgno>=pPager->dbSize ); */ |
| @@ -57146,22 +57192,22 @@ | |
| 57192 | |
| 57193 | noContent = (flags & PAGER_GET_NOCONTENT)!=0; |
| 57194 | if( pPg->pPager && !noContent ){ |
| 57195 | /* In this case the pcache already contains an initialized copy of |
| 57196 | ** the page. Return without further ado. */ |
| 57197 | assert( pgno!=PAGER_MJ_PGNO(pPager) ); |
| 57198 | pPager->aStat[PAGER_STAT_HIT]++; |
| 57199 | return SQLITE_OK; |
| 57200 | |
| 57201 | }else{ |
| 57202 | /* The pager cache has created a new page. Its content needs to |
| 57203 | ** be initialized. But first some error checks: |
| 57204 | ** |
| 57205 | ** (*) obsolete. Was: maximum page number is 2^31 |
| 57206 | ** (2) Never try to fetch the locking page |
| 57207 | */ |
| 57208 | if( pgno==PAGER_MJ_PGNO(pPager) ){ |
| 57209 | rc = SQLITE_CORRUPT_BKPT; |
| 57210 | goto pager_acquire_err; |
| 57211 | } |
| 57212 | |
| 57213 | pPg->pPager = pPager; |
| @@ -59863,11 +59909,11 @@ | |
| 59909 | ** walIteratorFree() - Free an iterator. |
| 59910 | ** |
| 59911 | ** This functionality is used by the checkpoint code (see walCheckpoint()). |
| 59912 | */ |
| 59913 | struct WalIterator { |
| 59914 | u32 iPrior; /* Last result returned from the iterator */ |
| 59915 | int nSegment; /* Number of entries in aSegment[] */ |
| 59916 | struct WalSegment { |
| 59917 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 59918 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 59919 | u32 *aPgno; /* Array of page numbers. */ |
| @@ -59945,11 +59991,13 @@ | |
| 59991 | rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, |
| 59992 | pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] |
| 59993 | ); |
| 59994 | assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 ); |
| 59995 | testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK ); |
| 59996 | if( rc==SQLITE_OK ){ |
| 59997 | if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; |
| 59998 | }else if( (rc&0xff)==SQLITE_READONLY ){ |
| 59999 | pWal->readOnly |= WAL_SHM_RDONLY; |
| 60000 | if( rc==SQLITE_READONLY ){ |
| 60001 | rc = SQLITE_OK; |
| 60002 | } |
| 60003 | } |
| @@ -60320,10 +60368,11 @@ | |
| 60368 | && (iHash>=1 || iFrame<=HASHTABLE_NPAGE_ONE) |
| 60369 | && (iHash<=1 || iFrame>(HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)) |
| 60370 | && (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE) |
| 60371 | && (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE)) |
| 60372 | ); |
| 60373 | assert( iHash>=0 ); |
| 60374 | return iHash; |
| 60375 | } |
| 60376 | |
| 60377 | /* |
| 60378 | ** Return the page number associated with frame iFrame in this WAL. |
| @@ -60516,16 +60565,10 @@ | |
| 60565 | assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); |
| 60566 | assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); |
| 60567 | assert( pWal->writeLock ); |
| 60568 | iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; |
| 60569 | rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60570 | if( rc ){ |
| 60571 | return rc; |
| 60572 | } |
| 60573 | |
| 60574 | WALTRACE(("WAL%p: recovery begin...\n", pWal)); |
| @@ -60537,19 +60580,20 @@ | |
| 60580 | goto recovery_error; |
| 60581 | } |
| 60582 | |
| 60583 | if( nSize>WAL_HDRSIZE ){ |
| 60584 | u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */ |
| 60585 | u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */ |
| 60586 | u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */ |
| 60587 | int szFrame; /* Number of bytes in buffer aFrame[] */ |
| 60588 | u8 *aData; /* Pointer to data part of aFrame buffer */ |
| 60589 | int szPage; /* Page size according to the log */ |
| 60590 | u32 magic; /* Magic value read from WAL header */ |
| 60591 | u32 version; /* Magic value read from WAL header */ |
| 60592 | int isValid; /* True if this frame is valid */ |
| 60593 | u32 iPg; /* Current 32KB wal-index page */ |
| 60594 | u32 iLastFrame; /* Last frame in wal, based on nSize alone */ |
| 60595 | |
| 60596 | /* Read in the WAL header. */ |
| 60597 | rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); |
| 60598 | if( rc!=SQLITE_OK ){ |
| 60599 | goto recovery_error; |
| @@ -60592,42 +60636,86 @@ | |
| 60636 | goto finished; |
| 60637 | } |
| 60638 | |
| 60639 | /* Malloc a buffer to read frames into. */ |
| 60640 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 60641 | aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ); |
| 60642 | if( !aFrame ){ |
| 60643 | rc = SQLITE_NOMEM_BKPT; |
| 60644 | goto recovery_error; |
| 60645 | } |
| 60646 | aData = &aFrame[WAL_FRAME_HDRSIZE]; |
| 60647 | aPrivate = (u32*)&aData[szPage]; |
| 60648 | |
| 60649 | /* Read all frames from the log file. */ |
| 60650 | iLastFrame = (nSize - WAL_HDRSIZE) / szFrame; |
| 60651 | for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){ |
| 60652 | u32 *aShare; |
| 60653 | u32 iFrame; /* Index of last frame read */ |
| 60654 | u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE); |
| 60655 | u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE); |
| 60656 | u32 nHdr, nHdr32; |
| 60657 | rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare); |
| 60658 | if( rc ) break; |
| 60659 | pWal->apWiData[iPg] = aPrivate; |
| 60660 | |
| 60661 | for(iFrame=iFirst; iFrame<=iLast; iFrame++){ |
| 60662 | i64 iOffset = walFrameOffset(iFrame, szPage); |
| 60663 | u32 pgno; /* Database page number for frame */ |
| 60664 | u32 nTruncate; /* dbsize field from frame header */ |
| 60665 | |
| 60666 | /* Read and decode the next log frame. */ |
| 60667 | rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset); |
| 60668 | if( rc!=SQLITE_OK ) break; |
| 60669 | isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame); |
| 60670 | if( !isValid ) break; |
| 60671 | rc = walIndexAppend(pWal, iFrame, pgno); |
| 60672 | if( NEVER(rc!=SQLITE_OK) ) break; |
| 60673 | |
| 60674 | /* If nTruncate is non-zero, this is a commit record. */ |
| 60675 | if( nTruncate ){ |
| 60676 | pWal->hdr.mxFrame = iFrame; |
| 60677 | pWal->hdr.nPage = nTruncate; |
| 60678 | pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16)); |
| 60679 | testcase( szPage<=32768 ); |
| 60680 | testcase( szPage>=65536 ); |
| 60681 | aFrameCksum[0] = pWal->hdr.aFrameCksum[0]; |
| 60682 | aFrameCksum[1] = pWal->hdr.aFrameCksum[1]; |
| 60683 | } |
| 60684 | } |
| 60685 | pWal->apWiData[iPg] = aShare; |
| 60686 | nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); |
| 60687 | nHdr32 = nHdr / sizeof(u32); |
| 60688 | #ifndef SQLITE_SAFER_WALINDEX_RECOVERY |
| 60689 | /* Memcpy() should work fine here, on all reasonable implementations. |
| 60690 | ** Technically, memcpy() might change the destination to some |
| 60691 | ** intermediate value before setting to the final value, and that might |
| 60692 | ** cause a concurrent reader to malfunction. Memcpy() is allowed to |
| 60693 | ** do that, according to the spec, but no memcpy() implementation that |
| 60694 | ** we know of actually does that, which is why we say that memcpy() |
| 60695 | ** is safe for this. Memcpy() is certainly a lot faster. |
| 60696 | */ |
| 60697 | memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); |
| 60698 | #else |
| 60699 | /* In the event that some platform is found for which memcpy() |
| 60700 | ** changes the destination to some intermediate value before |
| 60701 | ** setting the final value, this alternative copy routine is |
| 60702 | ** provided. |
| 60703 | */ |
| 60704 | { |
| 60705 | int i; |
| 60706 | for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){ |
| 60707 | if( aShare[i]!=aPrivate[i] ){ |
| 60708 | /* Atomic memory operations are not required here because if |
| 60709 | ** the value needs to be changed, that means it is not being |
| 60710 | ** accessed concurrently. */ |
| 60711 | aShare[i] = aPrivate[i]; |
| 60712 | } |
| 60713 | } |
| 60714 | } |
| 60715 | #endif |
| 60716 | if( iFrame<=iLast ) break; |
| 60717 | } |
| 60718 | |
| 60719 | sqlite3_free(aFrame); |
| 60720 | } |
| 60721 | |
| @@ -60638,19 +60726,30 @@ | |
| 60726 | pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; |
| 60727 | pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; |
| 60728 | walIndexWriteHdr(pWal); |
| 60729 | |
| 60730 | /* Reset the checkpoint-header. This is safe because this thread is |
| 60731 | ** currently holding locks that exclude all other writers and |
| 60732 | ** checkpointers. Then set the values of read-mark slots 1 through N. |
| 60733 | */ |
| 60734 | pInfo = walCkptInfo(pWal); |
| 60735 | pInfo->nBackfill = 0; |
| 60736 | pInfo->nBackfillAttempted = pWal->hdr.mxFrame; |
| 60737 | pInfo->aReadMark[0] = 0; |
| 60738 | for(i=1; i<WAL_NREADER; i++){ |
| 60739 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 60740 | if( rc==SQLITE_OK ){ |
| 60741 | if( i==1 && pWal->hdr.mxFrame ){ |
| 60742 | pInfo->aReadMark[i] = pWal->hdr.mxFrame; |
| 60743 | }else{ |
| 60744 | pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 60745 | } |
| 60746 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 60747 | }else if( rc!=SQLITE_BUSY ){ |
| 60748 | goto recovery_error; |
| 60749 | } |
| 60750 | } |
| 60751 | |
| 60752 | /* If more than one frame was recovered from the log file, report an |
| 60753 | ** event via sqlite3_log(). This is to help with identifying performance |
| 60754 | ** problems caused by applications routinely shutting down without |
| 60755 | ** checkpointing the log file. |
| @@ -60664,11 +60763,10 @@ | |
| 60763 | } |
| 60764 | |
| 60765 | recovery_error: |
| 60766 | WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok")); |
| 60767 | walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); |
| 60768 | return rc; |
| 60769 | } |
| 60770 | |
| 60771 | /* |
| 60772 | ** Close an open wal-index. |
| @@ -61312,14 +61410,22 @@ | |
| 61410 | i64 nReq = ((i64)mxPage * szPage); |
| 61411 | i64 nSize; /* Current size of database file */ |
| 61412 | sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0); |
| 61413 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 61414 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 61415 | if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){ |
| 61416 | /* If the size of the final database is larger than the current |
| 61417 | ** database plus the amount of data in the wal file, plus the |
| 61418 | ** maximum size of the pending-byte page (65536 bytes), then |
| 61419 | ** must be corruption somewhere. */ |
| 61420 | rc = SQLITE_CORRUPT_BKPT; |
| 61421 | }else{ |
| 61422 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); |
| 61423 | } |
| 61424 | } |
| 61425 | |
| 61426 | } |
| 61427 | |
| 61428 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 61429 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 61430 | i64 iOffset; |
| 61431 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| @@ -64048,11 +64154,12 @@ | |
| 64154 | Pgno nPage; /* Number of pages in the database */ |
| 64155 | int mxErr; /* Stop accumulating errors when this reaches zero */ |
| 64156 | int nErr; /* Number of messages written to zErrMsg so far */ |
| 64157 | int bOomFault; /* A memory allocation error has occurred */ |
| 64158 | const char *zPfx; /* Error message prefix */ |
| 64159 | Pgno v1; /* Value for first %u substitution in zPfx */ |
| 64160 | int v2; /* Value for second %d substitution in zPfx */ |
| 64161 | StrAccum errMsg; /* Accumulate the error message text here */ |
| 64162 | u32 *heap; /* Min-heap used for analyzing cell coverage */ |
| 64163 | sqlite3 *db; /* Database connection running the check */ |
| 64164 | }; |
| 64165 | |
| @@ -66513,16 +66620,15 @@ | |
| 66620 | /* |
| 66621 | ** Return the size of the database file in pages. If there is any kind of |
| 66622 | ** error, return ((unsigned int)-1). |
| 66623 | */ |
| 66624 | static Pgno btreePagecount(BtShared *pBt){ |
| 66625 | return pBt->nPage; |
| 66626 | } |
| 66627 | SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ |
| 66628 | assert( sqlite3BtreeHoldsMutex(p) ); |
| 66629 | return btreePagecount(p->pBt); |
| 66630 | } |
| 66631 | |
| 66632 | /* |
| 66633 | ** Get a page from the pager and initialize it. |
| 66634 | ** |
| @@ -67306,12 +67412,12 @@ | |
| 67412 | /* |
| 67413 | ** Set the maximum page count for a database if mxPage is positive. |
| 67414 | ** No changes are made if mxPage is 0 or negative. |
| 67415 | ** Regardless of the value of mxPage, return the maximum page count. |
| 67416 | */ |
| 67417 | SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){ |
| 67418 | Pgno n; |
| 67419 | sqlite3BtreeEnter(p); |
| 67420 | n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage); |
| 67421 | sqlite3BtreeLeave(p); |
| 67422 | return n; |
| 67423 | } |
| @@ -68746,11 +68852,11 @@ | |
| 68852 | ** It is assumed that the sqlite3BtreeCursorZero() has been called |
| 68853 | ** on pCur to initialize the memory space prior to invoking this routine. |
| 68854 | */ |
| 68855 | static int btreeCursor( |
| 68856 | Btree *p, /* The btree */ |
| 68857 | Pgno iTable, /* Root page of table to open */ |
| 68858 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68859 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68860 | BtCursor *pCur /* Space for new cursor */ |
| 68861 | ){ |
| 68862 | BtShared *pBt = p->pBt; /* Shared b-tree handle */ |
| @@ -68789,21 +68895,21 @@ | |
| 68895 | } |
| 68896 | } |
| 68897 | |
| 68898 | /* Now that no other errors can occur, finish filling in the BtCursor |
| 68899 | ** variables and link the cursor into the BtShared list. */ |
| 68900 | pCur->pgnoRoot = iTable; |
| 68901 | pCur->iPage = -1; |
| 68902 | pCur->pKeyInfo = pKeyInfo; |
| 68903 | pCur->pBtree = p; |
| 68904 | pCur->pBt = pBt; |
| 68905 | pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0; |
| 68906 | pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY; |
| 68907 | /* If there are two or more cursors on the same btree, then all such |
| 68908 | ** cursors *must* have the BTCF_Multiple flag set. */ |
| 68909 | for(pX=pBt->pCursor; pX; pX=pX->pNext){ |
| 68910 | if( pX->pgnoRoot==iTable ){ |
| 68911 | pX->curFlags |= BTCF_Multiple; |
| 68912 | pCur->curFlags |= BTCF_Multiple; |
| 68913 | } |
| 68914 | } |
| 68915 | pCur->pNext = pBt->pCursor; |
| @@ -68811,11 +68917,11 @@ | |
| 68917 | pCur->eState = CURSOR_INVALID; |
| 68918 | return SQLITE_OK; |
| 68919 | } |
| 68920 | static int btreeCursorWithLock( |
| 68921 | Btree *p, /* The btree */ |
| 68922 | Pgno iTable, /* Root page of table to open */ |
| 68923 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68924 | struct KeyInfo *pKeyInfo, /* First arg to comparison function */ |
| 68925 | BtCursor *pCur /* Space for new cursor */ |
| 68926 | ){ |
| 68927 | int rc; |
| @@ -68824,11 +68930,11 @@ | |
| 68930 | sqlite3BtreeLeave(p); |
| 68931 | return rc; |
| 68932 | } |
| 68933 | SQLITE_PRIVATE int sqlite3BtreeCursor( |
| 68934 | Btree *p, /* The btree */ |
| 68935 | Pgno iTable, /* Root page of table to open */ |
| 68936 | int wrFlag, /* 1 to write. 0 read-only */ |
| 68937 | struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ |
| 68938 | BtCursor *pCur /* Write new cursor here */ |
| 68939 | ){ |
| 68940 | if( p->sharable ){ |
| @@ -69249,10 +69355,11 @@ | |
| 69355 | } |
| 69356 | |
| 69357 | assert( rc==SQLITE_OK && amt>0 ); |
| 69358 | while( nextPage ){ |
| 69359 | /* If required, populate the overflow page-list cache. */ |
| 69360 | if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT; |
| 69361 | assert( pCur->aOverflow[iIdx]==0 |
| 69362 | || pCur->aOverflow[iIdx]==nextPage |
| 69363 | || CORRUPT_DB ); |
| 69364 | pCur->aOverflow[iIdx] = nextPage; |
| 69365 | |
| @@ -70664,10 +70771,14 @@ | |
| 70771 | */ |
| 70772 | if( nFree!=0 ){ |
| 70773 | u32 nLeaf; /* Initial number of leaf cells on trunk page */ |
| 70774 | |
| 70775 | iTrunk = get4byte(&pPage1->aData[32]); |
| 70776 | if( iTrunk>btreePagecount(pBt) ){ |
| 70777 | rc = SQLITE_CORRUPT_BKPT; |
| 70778 | goto freepage_out; |
| 70779 | } |
| 70780 | rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); |
| 70781 | if( rc!=SQLITE_OK ){ |
| 70782 | goto freepage_out; |
| 70783 | } |
| 70784 | |
| @@ -73468,11 +73579,11 @@ | |
| 73579 | ** flags might not work: |
| 73580 | ** |
| 73581 | ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys |
| 73582 | ** BTREE_ZERODATA Used for SQL indices |
| 73583 | */ |
| 73584 | static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ |
| 73585 | BtShared *pBt = p->pBt; |
| 73586 | MemPage *pRoot; |
| 73587 | Pgno pgnoRoot; |
| 73588 | int rc; |
| 73589 | int ptfFlags; /* Page-type flage for the root page of new table */ |
| @@ -73501,21 +73612,23 @@ | |
| 73612 | /* Read the value of meta[3] from the database to determine where the |
| 73613 | ** root page of the new table should go. meta[3] is the largest root-page |
| 73614 | ** created so far, so the new root-page is (meta[3]+1). |
| 73615 | */ |
| 73616 | sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); |
| 73617 | if( pgnoRoot>btreePagecount(pBt) ){ |
| 73618 | return SQLITE_CORRUPT_BKPT; |
| 73619 | } |
| 73620 | pgnoRoot++; |
| 73621 | |
| 73622 | /* The new root-page may not be allocated on a pointer-map page, or the |
| 73623 | ** PENDING_BYTE page. |
| 73624 | */ |
| 73625 | while( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || |
| 73626 | pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ |
| 73627 | pgnoRoot++; |
| 73628 | } |
| 73629 | assert( pgnoRoot>=3 ); |
| 73630 | |
| 73631 | /* Allocate a page. The page that currently resides at pgnoRoot will |
| 73632 | ** be moved to the allocated page (unless the allocated page happens |
| 73633 | ** to reside at pgnoRoot). |
| 73634 | */ |
| @@ -73608,14 +73721,14 @@ | |
| 73721 | ptfFlags = PTF_ZERODATA | PTF_LEAF; |
| 73722 | } |
| 73723 | zeroPage(pRoot, ptfFlags); |
| 73724 | sqlite3PagerUnref(pRoot->pDbPage); |
| 73725 | assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); |
| 73726 | *piTable = pgnoRoot; |
| 73727 | return SQLITE_OK; |
| 73728 | } |
| 73729 | SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){ |
| 73730 | int rc; |
| 73731 | sqlite3BtreeEnter(p); |
| 73732 | rc = btreeCreateTable(p, piTable, flags); |
| 73733 | sqlite3BtreeLeave(p); |
| 73734 | return rc; |
| @@ -74095,11 +74208,11 @@ | |
| 74208 | ** Verify that the number of pages on the list is N. |
| 74209 | */ |
| 74210 | static void checkList( |
| 74211 | IntegrityCk *pCheck, /* Integrity checking context */ |
| 74212 | int isFreeList, /* True for a freelist. False for overflow page list */ |
| 74213 | Pgno iPage, /* Page number for first page in the list */ |
| 74214 | u32 N /* Expected number of pages in the list */ |
| 74215 | ){ |
| 74216 | int i; |
| 74217 | u32 expected = N; |
| 74218 | int nErrAtStart = pCheck->nErr; |
| @@ -74227,11 +74340,11 @@ | |
| 74340 | ** 4. Recursively call checkTreePage on all children. |
| 74341 | ** 5. Verify that the depth of all children is the same. |
| 74342 | */ |
| 74343 | static int checkTreePage( |
| 74344 | IntegrityCk *pCheck, /* Context for the sanity check */ |
| 74345 | Pgno iPage, /* Page number of the page to check */ |
| 74346 | i64 *piMinKey, /* Write minimum integer primary key here */ |
| 74347 | i64 maxKey /* Error if integer primary key greater than this */ |
| 74348 | ){ |
| 74349 | MemPage *pPage = 0; /* The page being analyzed */ |
| 74350 | int i; /* Loop counter */ |
| @@ -74263,13 +74376,13 @@ | |
| 74376 | */ |
| 74377 | pBt = pCheck->pBt; |
| 74378 | usableSize = pBt->usableSize; |
| 74379 | if( iPage==0 ) return 0; |
| 74380 | if( checkRef(pCheck, iPage) ) return 0; |
| 74381 | pCheck->zPfx = "Page %u: "; |
| 74382 | pCheck->v1 = iPage; |
| 74383 | if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ |
| 74384 | checkAppendMsg(pCheck, |
| 74385 | "unable to get the page. error code=%d", rc); |
| 74386 | goto end_of_check; |
| 74387 | } |
| 74388 | |
| @@ -74290,11 +74403,11 @@ | |
| 74403 | } |
| 74404 | data = pPage->aData; |
| 74405 | hdr = pPage->hdrOffset; |
| 74406 | |
| 74407 | /* Set up for cell analysis */ |
| 74408 | pCheck->zPfx = "On tree page %u cell %d: "; |
| 74409 | contentOffset = get2byteNotZero(&data[hdr+5]); |
| 74410 | assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ |
| 74411 | |
| 74412 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 74413 | ** number of cells on the page. */ |
| @@ -74310,11 +74423,11 @@ | |
| 74423 | if( !pPage->leaf ){ |
| 74424 | /* Analyze the right-child page of internal pages */ |
| 74425 | pgno = get4byte(&data[hdr+8]); |
| 74426 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74427 | if( pBt->autoVacuum ){ |
| 74428 | pCheck->zPfx = "On page %u at right child: "; |
| 74429 | checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage); |
| 74430 | } |
| 74431 | #endif |
| 74432 | depth = checkTreePage(pCheck, pgno, &maxKey, maxKey); |
| 74433 | keyCanBeEqual = 0; |
| @@ -74451,11 +74564,11 @@ | |
| 74564 | nFrag = 0; |
| 74565 | prev = contentOffset - 1; /* Implied first min-heap entry */ |
| 74566 | while( btreeHeapPull(heap,&x) ){ |
| 74567 | if( (prev&0xffff)>=(x>>16) ){ |
| 74568 | checkAppendMsg(pCheck, |
| 74569 | "Multiple uses for byte %u of page %u", x>>16, iPage); |
| 74570 | break; |
| 74571 | }else{ |
| 74572 | nFrag += (x>>16) - (prev&0xffff) - 1; |
| 74573 | prev = x; |
| 74574 | } |
| @@ -74466,11 +74579,11 @@ | |
| 74579 | ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the |
| 74580 | ** number of fragmented free bytes within the cell content area. |
| 74581 | */ |
| 74582 | if( heap[0]==0 && nFrag!=data[hdr+7] ){ |
| 74583 | checkAppendMsg(pCheck, |
| 74584 | "Fragmentation of %d bytes reported as %d on page %u", |
| 74585 | nFrag, data[hdr+7], iPage); |
| 74586 | } |
| 74587 | } |
| 74588 | |
| 74589 | end_of_check: |
| @@ -74494,25 +74607,44 @@ | |
| 74607 | ** |
| 74608 | ** Write the number of error seen in *pnErr. Except for some memory |
| 74609 | ** allocation errors, an error message held in memory obtained from |
| 74610 | ** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is |
| 74611 | ** returned. If a memory allocation error occurs, NULL is returned. |
| 74612 | ** |
| 74613 | ** If the first entry in aRoot[] is 0, that indicates that the list of |
| 74614 | ** root pages is incomplete. This is a "partial integrity-check". This |
| 74615 | ** happens when performing an integrity check on a single table. The |
| 74616 | ** zero is skipped, of course. But in addition, the freelist checks |
| 74617 | ** and the checks to make sure every page is referenced are also skipped, |
| 74618 | ** since obviously it is not possible to know which pages are covered by |
| 74619 | ** the unverified btrees. Except, if aRoot[1] is 1, then the freelist |
| 74620 | ** checks are still performed. |
| 74621 | */ |
| 74622 | SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( |
| 74623 | sqlite3 *db, /* Database connection that is running the check */ |
| 74624 | Btree *p, /* The btree to be checked */ |
| 74625 | Pgno *aRoot, /* An array of root pages numbers for individual trees */ |
| 74626 | int nRoot, /* Number of entries in aRoot[] */ |
| 74627 | int mxErr, /* Stop reporting errors after this many */ |
| 74628 | int *pnErr /* Write number of errors seen to this variable */ |
| 74629 | ){ |
| 74630 | Pgno i; |
| 74631 | IntegrityCk sCheck; |
| 74632 | BtShared *pBt = p->pBt; |
| 74633 | u64 savedDbFlags = pBt->db->flags; |
| 74634 | char zErr[100]; |
| 74635 | int bPartial = 0; /* True if not checking all btrees */ |
| 74636 | int bCkFreelist = 1; /* True to scan the freelist */ |
| 74637 | VVA_ONLY( int nRef ); |
| 74638 | assert( nRoot>0 ); |
| 74639 | |
| 74640 | /* aRoot[0]==0 means this is a partial check */ |
| 74641 | if( aRoot[0]==0 ){ |
| 74642 | assert( nRoot>1 ); |
| 74643 | bPartial = 1; |
| 74644 | if( aRoot[1]!=1 ) bCkFreelist = 0; |
| 74645 | } |
| 74646 | |
| 74647 | sqlite3BtreeEnter(p); |
| 74648 | assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); |
| 74649 | VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); |
| 74650 | assert( nRef>=0 ); |
| @@ -74548,69 +74680,75 @@ | |
| 74680 | i = PENDING_BYTE_PAGE(pBt); |
| 74681 | if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); |
| 74682 | |
| 74683 | /* Check the integrity of the freelist |
| 74684 | */ |
| 74685 | if( bCkFreelist ){ |
| 74686 | sCheck.zPfx = "Main freelist: "; |
| 74687 | checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]), |
| 74688 | get4byte(&pBt->pPage1->aData[36])); |
| 74689 | sCheck.zPfx = 0; |
| 74690 | } |
| 74691 | |
| 74692 | /* Check all the tables. |
| 74693 | */ |
| 74694 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74695 | if( !bPartial ){ |
| 74696 | if( pBt->autoVacuum ){ |
| 74697 | Pgno mx = 0; |
| 74698 | Pgno mxInHdr; |
| 74699 | for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i]; |
| 74700 | mxInHdr = get4byte(&pBt->pPage1->aData[52]); |
| 74701 | if( mx!=mxInHdr ){ |
| 74702 | checkAppendMsg(&sCheck, |
| 74703 | "max rootpage (%d) disagrees with header (%d)", |
| 74704 | mx, mxInHdr |
| 74705 | ); |
| 74706 | } |
| 74707 | }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){ |
| 74708 | checkAppendMsg(&sCheck, |
| 74709 | "incremental_vacuum enabled with a max rootpage of zero" |
| 74710 | ); |
| 74711 | } |
| 74712 | } |
| 74713 | #endif |
| 74714 | testcase( pBt->db->flags & SQLITE_CellSizeCk ); |
| 74715 | pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; |
| 74716 | for(i=0; (int)i<nRoot && sCheck.mxErr; i++){ |
| 74717 | i64 notUsed; |
| 74718 | if( aRoot[i]==0 ) continue; |
| 74719 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 74720 | if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ |
| 74721 | checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); |
| 74722 | } |
| 74723 | #endif |
| 74724 | checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); |
| 74725 | } |
| 74726 | pBt->db->flags = savedDbFlags; |
| 74727 | |
| 74728 | /* Make sure every page in the file is referenced |
| 74729 | */ |
| 74730 | if( !bPartial ){ |
| 74731 | for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ |
| 74732 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 74733 | if( getPageReferenced(&sCheck, i)==0 ){ |
| 74734 | checkAppendMsg(&sCheck, "Page %d is never used", i); |
| 74735 | } |
| 74736 | #else |
| 74737 | /* If the database supports auto-vacuum, make sure no tables contain |
| 74738 | ** references to pointer-map pages. |
| 74739 | */ |
| 74740 | if( getPageReferenced(&sCheck, i)==0 && |
| 74741 | (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ |
| 74742 | checkAppendMsg(&sCheck, "Page %d is never used", i); |
| 74743 | } |
| 74744 | if( getPageReferenced(&sCheck, i)!=0 && |
| 74745 | (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ |
| 74746 | checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i); |
| 74747 | } |
| 74748 | #endif |
| 74749 | } |
| 74750 | } |
| 74751 | |
| 74752 | /* Clean up and report errors. |
| 74753 | */ |
| 74754 | integrity_ck_cleanup: |
| @@ -75794,20 +75932,29 @@ | |
| 75932 | ** into a buffer. |
| 75933 | */ |
| 75934 | static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ |
| 75935 | StrAccum acc; |
| 75936 | assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); |
| 75937 | assert( sz>22 ); |
| 75938 | if( p->flags & MEM_Int ){ |
| 75939 | #if GCC_VERSION>=7000000 |
| 75940 | /* Work-around for GCC bug |
| 75941 | ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ |
| 75942 | i64 x; |
| 75943 | assert( (p->flags&MEM_Int)*2==sizeof(x) ); |
| 75944 | memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); |
| 75945 | sqlite3Int64ToText(x, zBuf); |
| 75946 | #else |
| 75947 | sqlite3Int64ToText(p->u.i, zBuf); |
| 75948 | #endif |
| 75949 | }else{ |
| 75950 | sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); |
| 75951 | sqlite3_str_appendf(&acc, "%!.15g", |
| 75952 | (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); |
| 75953 | assert( acc.zText==zBuf && acc.mxAlloc<=0 ); |
| 75954 | zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ |
| 75955 | } |
| 75956 | } |
| 75957 | |
| 75958 | #ifdef SQLITE_DEBUG |
| 75959 | /* |
| 75960 | ** Validity checks on pMem. pMem holds a string. |
| @@ -78397,11 +78544,11 @@ | |
| 78544 | /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing |
| 78545 | ** cases from this switch! */ |
| 78546 | switch( pOp->opcode ){ |
| 78547 | case OP_Transaction: { |
| 78548 | if( pOp->p2!=0 ) p->readOnly = 0; |
| 78549 | /* no break */ deliberate_fall_through |
| 78550 | } |
| 78551 | case OP_AutoCommit: |
| 78552 | case OP_Savepoint: { |
| 78553 | p->bIsReader = 1; |
| 78554 | break; |
| @@ -78444,10 +78591,11 @@ | |
| 78591 | assert( (pOp - p->aOp) >= 3 ); |
| 78592 | assert( pOp[-1].opcode==OP_Integer ); |
| 78593 | n = pOp[-1].p1; |
| 78594 | if( n>nMaxArgs ) nMaxArgs = n; |
| 78595 | /* Fall through into the default case */ |
| 78596 | /* no break */ deliberate_fall_through |
| 78597 | } |
| 78598 | #endif |
| 78599 | default: { |
| 78600 | if( pOp->p2<0 ){ |
| 78601 | /* The mkopcodeh.tcl script has so arranged things that the only |
| @@ -79301,16 +79449,16 @@ | |
| 79449 | sqlite3_str_appendf(&x, "vtab:%p", pVtab); |
| 79450 | break; |
| 79451 | } |
| 79452 | #endif |
| 79453 | case P4_INTARRAY: { |
| 79454 | u32 i; |
| 79455 | u32 *ai = pOp->p4.ai; |
| 79456 | u32 n = ai[0]; /* The first element of an INTARRAY is always the |
| 79457 | ** count of the number of elements to follow */ |
| 79458 | for(i=1; i<=n; i++){ |
| 79459 | sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]); |
| 79460 | } |
| 79461 | sqlite3_str_append(&x, "]", 1); |
| 79462 | break; |
| 79463 | } |
| 79464 | case P4_SUBPROGRAM: { |
| @@ -81150,15 +81298,15 @@ | |
| 81298 | ** a NULL row. |
| 81299 | ** |
| 81300 | ** If the cursor is already pointing to the correct row and that row has |
| 81301 | ** not been deleted out from under the cursor, then this routine is a no-op. |
| 81302 | */ |
| 81303 | SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){ |
| 81304 | VdbeCursor *p = *pp; |
| 81305 | assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO ); |
| 81306 | if( p->deferredMoveto ){ |
| 81307 | u32 iMap; |
| 81308 | if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){ |
| 81309 | *pp = p->pAltCursor; |
| 81310 | *piCol = iMap - 1; |
| 81311 | return SQLITE_OK; |
| 81312 | } |
| @@ -82910,11 +83058,11 @@ | |
| 83058 | if( db->xProfile ){ |
| 83059 | db->xProfile(db->pProfileArg, p->zSql, iElapse); |
| 83060 | } |
| 83061 | #endif |
| 83062 | if( db->mTrace & SQLITE_TRACE_PROFILE ){ |
| 83063 | db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); |
| 83064 | } |
| 83065 | p->startTime = 0; |
| 83066 | } |
| 83067 | /* |
| 83068 | ** The checkProfileCallback(DB,P) macro checks to see if a profile callback |
| @@ -86210,10 +86358,11 @@ | |
| 86358 | #ifdef SQLITE_DEBUG |
| 86359 | if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } |
| 86360 | #endif |
| 86361 | if( (pIn3->flags & MEM_Null)==0 ) break; |
| 86362 | /* Fall through into OP_Halt */ |
| 86363 | /* no break */ deliberate_fall_through |
| 86364 | } |
| 86365 | |
| 86366 | /* Opcode: Halt P1 P2 * P4 P5 |
| 86367 | ** |
| 86368 | ** Exit immediately. All open cursors, etc are closed |
| @@ -86380,10 +86529,11 @@ | |
| 86529 | goto too_big; |
| 86530 | } |
| 86531 | pOp->opcode = OP_String; |
| 86532 | assert( rc==SQLITE_OK ); |
| 86533 | /* Fall through to the next case, OP_String */ |
| 86534 | /* no break */ deliberate_fall_through |
| 86535 | } |
| 86536 | |
| 86537 | /* Opcode: String P1 P2 P3 P4 P5 |
| 86538 | ** Synopsis: r[P2]='P4' (len=P1) |
| 86539 | ** |
| @@ -86691,11 +86841,11 @@ | |
| 86841 | #endif |
| 86842 | } |
| 86843 | if( db->mallocFailed ) goto no_mem; |
| 86844 | |
| 86845 | if( db->mTrace & SQLITE_TRACE_ROW ){ |
| 86846 | db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0); |
| 86847 | } |
| 86848 | |
| 86849 | |
| 86850 | /* Return SQLITE_ROW |
| 86851 | */ |
| @@ -87427,14 +87577,14 @@ | |
| 87577 | int n; |
| 87578 | int i; |
| 87579 | int p1; |
| 87580 | int p2; |
| 87581 | const KeyInfo *pKeyInfo; |
| 87582 | u32 idx; |
| 87583 | CollSeq *pColl; /* Collating sequence to use on this term */ |
| 87584 | int bRev; /* True for DESCENDING sort order */ |
| 87585 | u32 *aPermute; /* The permutation */ |
| 87586 | |
| 87587 | if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){ |
| 87588 | aPermute = 0; |
| 87589 | }else{ |
| 87590 | assert( pOp>aOp ); |
| @@ -87450,20 +87600,20 @@ | |
| 87600 | p1 = pOp->p1; |
| 87601 | p2 = pOp->p2; |
| 87602 | #ifdef SQLITE_DEBUG |
| 87603 | if( aPermute ){ |
| 87604 | int k, mx = 0; |
| 87605 | for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k]; |
| 87606 | assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87607 | assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); |
| 87608 | }else{ |
| 87609 | assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87610 | assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); |
| 87611 | } |
| 87612 | #endif /* SQLITE_DEBUG */ |
| 87613 | for(i=0; i<n; i++){ |
| 87614 | idx = aPermute ? aPermute[i] : (u32)i; |
| 87615 | assert( memIsValid(&aMem[p1+idx]) ); |
| 87616 | assert( memIsValid(&aMem[p2+idx]) ); |
| 87617 | REGISTER_TRACE(p1+idx, &aMem[p1+idx]); |
| 87618 | REGISTER_TRACE(p2+idx, &aMem[p2+idx]); |
| 87619 | assert( i<pKeyInfo->nKeyField ); |
| @@ -87770,11 +87920,11 @@ | |
| 87920 | ** the result is guaranteed to only be used as the argument of a length() |
| 87921 | ** or typeof() function, respectively. The loading of large blobs can be |
| 87922 | ** skipped for length() and all content loading can be skipped for typeof(). |
| 87923 | */ |
| 87924 | case OP_Column: { |
| 87925 | u32 p2; /* column number to retrieve */ |
| 87926 | VdbeCursor *pC; /* The VDBE cursor */ |
| 87927 | BtCursor *pCrsr; /* The BTree cursor */ |
| 87928 | u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ |
| 87929 | int len; /* The length of the serialized data for the column */ |
| 87930 | int i; /* Loop counter */ |
| @@ -87788,11 +87938,11 @@ | |
| 87938 | Mem *pReg; /* PseudoTable input register */ |
| 87939 | |
| 87940 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 87941 | pC = p->apCsr[pOp->p1]; |
| 87942 | assert( pC!=0 ); |
| 87943 | p2 = (u32)pOp->p2; |
| 87944 | |
| 87945 | /* If the cursor cache is stale (meaning it is not currently point at |
| 87946 | ** the correct row) then bring it up-to-date by doing the necessary |
| 87947 | ** B-Tree seek. */ |
| 87948 | rc = sqlite3VdbeCursorMoveto(&pC, &p2); |
| @@ -87800,11 +87950,11 @@ | |
| 87950 | |
| 87951 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 87952 | pDest = &aMem[pOp->p3]; |
| 87953 | memAboutToChange(p, pDest); |
| 87954 | assert( pC!=0 ); |
| 87955 | assert( p2<(u32)pC->nField ); |
| 87956 | aOffset = pC->aOffset; |
| 87957 | assert( pC->eCurType!=CURTYPE_VTAB ); |
| 87958 | assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); |
| 87959 | assert( pC->eCurType!=CURTYPE_SORTER ); |
| 87960 | |
| @@ -87915,11 +88065,11 @@ | |
| 88065 | zHdr += sqlite3GetVarint32(zHdr, &t); |
| 88066 | pC->aType[i] = t; |
| 88067 | offset64 += sqlite3VdbeSerialTypeLen(t); |
| 88068 | } |
| 88069 | aOffset[++i] = (u32)(offset64 & 0xffffffff); |
| 88070 | }while( (u32)i<=p2 && zHdr<zEndHdr ); |
| 88071 | |
| 88072 | /* The record is corrupt if any of the following are true: |
| 88073 | ** (1) the bytes of the header extend past the declared header size |
| 88074 | ** (2) the entire header was used but not all data was used |
| 88075 | ** (3) the end of the data extends beyond the end of the record. |
| @@ -88939,11 +89089,11 @@ | |
| 89089 | ** See also: OP_OpenRead, OP_ReopenIdx |
| 89090 | */ |
| 89091 | case OP_ReopenIdx: { |
| 89092 | int nField; |
| 89093 | KeyInfo *pKeyInfo; |
| 89094 | u32 p2; |
| 89095 | int iDb; |
| 89096 | int wrFlag; |
| 89097 | Btree *pX; |
| 89098 | VdbeCursor *pCur; |
| 89099 | Db *pDb; |
| @@ -88970,11 +89120,11 @@ | |
| 89120 | goto abort_due_to_error; |
| 89121 | } |
| 89122 | |
| 89123 | nField = 0; |
| 89124 | pKeyInfo = 0; |
| 89125 | p2 = (u32)pOp->p2; |
| 89126 | iDb = pOp->p3; |
| 89127 | assert( iDb>=0 && iDb<db->nDb ); |
| 89128 | assert( DbMaskTest(p->btreeMask, iDb) ); |
| 89129 | pDb = &db->aDb[iDb]; |
| 89130 | pX = pDb->pBt; |
| @@ -88989,11 +89139,11 @@ | |
| 89139 | }else{ |
| 89140 | wrFlag = 0; |
| 89141 | } |
| 89142 | if( pOp->p5 & OPFLAG_P2ISREG ){ |
| 89143 | assert( p2>0 ); |
| 89144 | assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); |
| 89145 | assert( pOp->opcode==OP_OpenWrite ); |
| 89146 | pIn2 = &aMem[p2]; |
| 89147 | assert( memIsValid(pIn2) ); |
| 89148 | assert( (pIn2->flags & MEM_Int)!=0 ); |
| 89149 | sqlite3VdbeMemIntegerify(pIn2); |
| @@ -89142,11 +89292,11 @@ | |
| 89292 | ** opening it. If a transient table is required, just use the |
| 89293 | ** automatically created table with root-page 1 (an BLOB_INTKEY table). |
| 89294 | */ |
| 89295 | if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ |
| 89296 | assert( pOp->p4type==P4_KEYINFO ); |
| 89297 | rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, |
| 89298 | BTREE_BLOBKEY | pOp->p5); |
| 89299 | if( rc==SQLITE_OK ){ |
| 89300 | assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); |
| 89301 | assert( pKeyInfo->db==db ); |
| 89302 | assert( pKeyInfo->enc==ENC(db) ); |
| @@ -89684,10 +89834,11 @@ | |
| 89834 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89835 | pC = p->apCsr[pOp->p1]; |
| 89836 | assert( pC!=0 ); |
| 89837 | if( pC->seekHit ) break; |
| 89838 | /* Fall through into OP_NotFound */ |
| 89839 | /* no break */ deliberate_fall_through |
| 89840 | } |
| 89841 | case OP_NoConflict: /* jump, in3 */ |
| 89842 | case OP_NotFound: /* jump, in3 */ |
| 89843 | case OP_Found: { /* jump, in3 */ |
| 89844 | int alreadyExists; |
| @@ -89838,10 +89989,11 @@ | |
| 89989 | if( (x.flags & MEM_Int)==0 ) goto jump_to_p2; |
| 89990 | iKey = x.u.i; |
| 89991 | goto notExistsWithKey; |
| 89992 | } |
| 89993 | /* Fall through into OP_NotExists */ |
| 89994 | /* no break */ deliberate_fall_through |
| 89995 | case OP_NotExists: /* jump, in3 */ |
| 89996 | pIn3 = &aMem[pOp->p3]; |
| 89997 | assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid ); |
| 89998 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 89999 | iKey = pIn3->u.i; |
| @@ -90406,14 +90558,10 @@ | |
| 90558 | ** generator) then the fix would be to insert a call to |
| 90559 | ** sqlite3VdbeCursorMoveto(). |
| 90560 | */ |
| 90561 | assert( pC->deferredMoveto==0 ); |
| 90562 | assert( sqlite3BtreeCursorIsValid(pCrsr) ); |
| 90563 | |
| 90564 | n = sqlite3BtreePayloadSize(pCrsr); |
| 90565 | if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ |
| 90566 | goto too_big; |
| 90567 | } |
| @@ -90613,10 +90761,11 @@ | |
| 90761 | sqlite3_sort_count++; |
| 90762 | sqlite3_search_count--; |
| 90763 | #endif |
| 90764 | p->aCounter[SQLITE_STMTSTATUS_SORT]++; |
| 90765 | /* Fall through into OP_Rewind */ |
| 90766 | /* no break */ deliberate_fall_through |
| 90767 | } |
| 90768 | /* Opcode: Rewind P1 P2 * * * |
| 90769 | ** |
| 90770 | ** The next use of the Rowid or Column or Next instruction for P1 |
| 90771 | ** will refer to the first entry in the database table or index. |
| @@ -91179,11 +91328,11 @@ | |
| 91328 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91329 | nChange = 0; |
| 91330 | assert( p->readOnly==0 ); |
| 91331 | assert( DbMaskTest(p->btreeMask, pOp->p2) ); |
| 91332 | rc = sqlite3BtreeClearTable( |
| 91333 | db->aDb[pOp->p2].pBt, (u32)pOp->p1, (pOp->p3 ? &nChange : 0) |
| 91334 | ); |
| 91335 | if( pOp->p3 ){ |
| 91336 | p->nChange += nChange; |
| 91337 | if( pOp->p3>0 ){ |
| 91338 | assert( memIsValid(&aMem[pOp->p3]) ); |
| @@ -91228,11 +91377,11 @@ | |
| 91377 | ** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table |
| 91378 | ** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table. |
| 91379 | ** The root page number of the new b-tree is stored in register P2. |
| 91380 | */ |
| 91381 | case OP_CreateBtree: { /* out2 */ |
| 91382 | Pgno pgno; |
| 91383 | Db *pDb; |
| 91384 | |
| 91385 | sqlite3VdbeIncrWriteCounter(p, 0); |
| 91386 | pOut = out2Prerelease(p, pOp); |
| 91387 | pgno = 0; |
| @@ -91303,10 +91452,11 @@ | |
| 91452 | zSchema = DFLT_SCHEMA_TABLE; |
| 91453 | initData.db = db; |
| 91454 | initData.iDb = iDb; |
| 91455 | initData.pzErrMsg = &p->zErrMsg; |
| 91456 | initData.mInitFlags = 0; |
| 91457 | initData.mxPage = sqlite3BtreeLastPage(db->aDb[iDb].pBt); |
| 91458 | zSql = sqlite3MPrintf(db, |
| 91459 | "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid", |
| 91460 | db->aDb[iDb].zDbSName, zSchema, pOp->p4.z); |
| 91461 | if( zSql==0 ){ |
| 91462 | rc = SQLITE_NOMEM_BKPT; |
| @@ -91416,20 +91566,20 @@ | |
| 91566 | ** |
| 91567 | ** This opcode is used to implement the integrity_check pragma. |
| 91568 | */ |
| 91569 | case OP_IntegrityCk: { |
| 91570 | int nRoot; /* Number of tables to check. (Number of root pages.) */ |
| 91571 | Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */ |
| 91572 | int nErr; /* Number of errors reported */ |
| 91573 | char *z; /* Text of the error report */ |
| 91574 | Mem *pnErr; /* Register keeping track of errors remaining */ |
| 91575 | |
| 91576 | assert( p->bIsReader ); |
| 91577 | nRoot = pOp->p2; |
| 91578 | aRoot = pOp->p4.ai; |
| 91579 | assert( nRoot>0 ); |
| 91580 | assert( aRoot[0]==(Pgno)nRoot ); |
| 91581 | assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); |
| 91582 | pnErr = &aMem[pOp->p3]; |
| 91583 | assert( (pnErr->flags & MEM_Int)!=0 ); |
| 91584 | assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); |
| 91585 | pIn1 = &aMem[pOp->p1]; |
| @@ -91965,10 +92115,11 @@ | |
| 92115 | /* OP_AggInverse must have P1==1 and OP_AggStep must have P1==0 */ |
| 92116 | assert( pOp->p1==(pOp->opcode==OP_AggInverse) ); |
| 92117 | |
| 92118 | pOp->opcode = OP_AggStep1; |
| 92119 | /* Fall through into OP_AggStep */ |
| 92120 | /* no break */ deliberate_fall_through |
| 92121 | } |
| 92122 | case OP_AggStep1: { |
| 92123 | int i; |
| 92124 | sqlite3_context *pCtx; |
| 92125 | Mem *pMem; |
| @@ -92954,22 +93105,21 @@ | |
| 93105 | && !p->doingRerun |
| 93106 | && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 |
| 93107 | ){ |
| 93108 | #ifndef SQLITE_OMIT_DEPRECATED |
| 93109 | if( db->mTrace & SQLITE_TRACE_LEGACY ){ |
| 93110 | char *z = sqlite3VdbeExpandSql(p, zTrace); |
| 93111 | db->trace.xLegacy(db->pTraceArg, z); |
| 93112 | sqlite3_free(z); |
| 93113 | }else |
| 93114 | #endif |
| 93115 | if( db->nVdbeExec>1 ){ |
| 93116 | char *z = sqlite3MPrintf(db, "-- %s", zTrace); |
| 93117 | (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z); |
| 93118 | sqlite3DbFree(db, z); |
| 93119 | }else{ |
| 93120 | (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace); |
| 93121 | } |
| 93122 | } |
| 93123 | #ifdef SQLITE_USE_FCNTL_TRACE |
| 93124 | zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); |
| 93125 | if( zTrace ){ |
| @@ -96701,11 +96851,11 @@ | |
| 96851 | }else{ |
| 96852 | if( i<=2 && pCur->zType==0 ){ |
| 96853 | Schema *pSchema; |
| 96854 | HashElem *k; |
| 96855 | int iDb = pOp->p3; |
| 96856 | Pgno iRoot = (Pgno)pOp->p2; |
| 96857 | sqlite3 *db = pVTab->db; |
| 96858 | pSchema = db->aDb[iDb].pSchema; |
| 96859 | pCur->zSchema = db->aDb[iDb].zDbSName; |
| 96860 | for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| 96861 | Table *pTab = (Table*)sqliteHashData(k); |
| @@ -97288,11 +97438,11 @@ | |
| 97438 | }else{ |
| 97439 | p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk); |
| 97440 | assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) ); |
| 97441 | } |
| 97442 | |
| 97443 | pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods; |
| 97444 | p->nSpill = nSpill; |
| 97445 | p->flags = flags; |
| 97446 | p->zJournal = zName; |
| 97447 | p->pVfs = pVfs; |
| 97448 | return SQLITE_OK; |
| @@ -97314,11 +97464,11 @@ | |
| 97464 | ** file has not yet been created, create it now. |
| 97465 | */ |
| 97466 | SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){ |
| 97467 | int rc = SQLITE_OK; |
| 97468 | MemJournal *p = (MemJournal*)pJfd; |
| 97469 | if( pJfd->pMethods==&MemJournalMethods && ( |
| 97470 | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
| 97471 | p->nSpill>0 |
| 97472 | #else |
| 97473 | /* While this appears to not be possible without ATOMIC_WRITE, the |
| 97474 | ** paths are complex, so it seems prudent to leave the test in as |
| @@ -98687,11 +98837,11 @@ | |
| 98837 | pExpr->op2 = pExpr->op; |
| 98838 | pExpr->op = TK_TRUTH; |
| 98839 | return WRC_Continue; |
| 98840 | } |
| 98841 | } |
| 98842 | /* no break */ deliberate_fall_through |
| 98843 | } |
| 98844 | case TK_BETWEEN: |
| 98845 | case TK_EQ: |
| 98846 | case TK_NE: |
| 98847 | case TK_LT: |
| @@ -101591,11 +101741,11 @@ | |
| 101741 | /* Convert "true" or "false" in a DEFAULT clause into the |
| 101742 | ** appropriate TK_TRUEFALSE operator */ |
| 101743 | if( sqlite3ExprIdToTrueFalse(pExpr) ){ |
| 101744 | return WRC_Prune; |
| 101745 | } |
| 101746 | /* no break */ deliberate_fall_through |
| 101747 | case TK_COLUMN: |
| 101748 | case TK_AGG_FUNCTION: |
| 101749 | case TK_AGG_COLUMN: |
| 101750 | testcase( pExpr->op==TK_ID ); |
| 101751 | testcase( pExpr->op==TK_COLUMN ); |
| @@ -101605,11 +101755,11 @@ | |
| 101755 | return WRC_Continue; |
| 101756 | } |
| 101757 | if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){ |
| 101758 | return WRC_Continue; |
| 101759 | } |
| 101760 | /* no break */ deliberate_fall_through |
| 101761 | case TK_IF_NULL_ROW: |
| 101762 | case TK_REGISTER: |
| 101763 | case TK_DOT: |
| 101764 | testcase( pExpr->op==TK_REGISTER ); |
| 101765 | testcase( pExpr->op==TK_IF_NULL_ROW ); |
| @@ -101626,11 +101776,11 @@ | |
| 101776 | /* A bound parameter in a CREATE statement that originates from |
| 101777 | ** sqlite3_prepare() causes an error */ |
| 101778 | pWalker->eCode = 0; |
| 101779 | return WRC_Abort; |
| 101780 | } |
| 101781 | /* no break */ deliberate_fall_through |
| 101782 | default: |
| 101783 | testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */ |
| 101784 | testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */ |
| 101785 | return WRC_Continue; |
| 101786 | } |
| @@ -103398,10 +103548,11 @@ | |
| 103548 | } |
| 103549 | } |
| 103550 | return target; |
| 103551 | } |
| 103552 | /* Otherwise, fall thru into the TK_COLUMN case */ |
| 103553 | /* no break */ deliberate_fall_through |
| 103554 | } |
| 103555 | case TK_COLUMN: { |
| 103556 | int iTab = pExpr->iTable; |
| 103557 | int iReg; |
| 103558 | if( ExprHasProperty(pExpr, EP_FixedCol) ){ |
| @@ -104463,11 +104614,11 @@ | |
| 104614 | case TK_ISNOT: |
| 104615 | testcase( op==TK_IS ); |
| 104616 | testcase( op==TK_ISNOT ); |
| 104617 | op = (op==TK_IS) ? TK_EQ : TK_NE; |
| 104618 | jumpIfNull = SQLITE_NULLEQ; |
| 104619 | /* no break */ deliberate_fall_through |
| 104620 | case TK_LT: |
| 104621 | case TK_LE: |
| 104622 | case TK_GT: |
| 104623 | case TK_GE: |
| 104624 | case TK_NE: |
| @@ -104639,11 +104790,11 @@ | |
| 104790 | case TK_ISNOT: |
| 104791 | testcase( pExpr->op==TK_IS ); |
| 104792 | testcase( pExpr->op==TK_ISNOT ); |
| 104793 | op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; |
| 104794 | jumpIfNull = SQLITE_NULLEQ; |
| 104795 | /* no break */ deliberate_fall_through |
| 104796 | case TK_LT: |
| 104797 | case TK_LE: |
| 104798 | case TK_GT: |
| 104799 | case TK_GE: |
| 104800 | case TK_NE: |
| @@ -104951,17 +105102,17 @@ | |
| 105102 | case TK_BITOR: |
| 105103 | case TK_LSHIFT: |
| 105104 | case TK_RSHIFT: |
| 105105 | case TK_CONCAT: |
| 105106 | seenNot = 1; |
| 105107 | /* no break */ deliberate_fall_through |
| 105108 | case TK_STAR: |
| 105109 | case TK_REM: |
| 105110 | case TK_BITAND: |
| 105111 | case TK_SLASH: { |
| 105112 | if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1; |
| 105113 | /* no break */ deliberate_fall_through |
| 105114 | } |
| 105115 | case TK_SPAN: |
| 105116 | case TK_COLLATE: |
| 105117 | case TK_UPLUS: |
| 105118 | case TK_UMINUS: { |
| @@ -105106,10 +105257,11 @@ | |
| 105257 | || (pRight->op==TK_COLUMN && ALWAYS(pRight->y.pTab!=0) |
| 105258 | && IsVirtual(pRight->y.pTab)) |
| 105259 | ){ |
| 105260 | return WRC_Prune; |
| 105261 | } |
| 105262 | /* no break */ deliberate_fall_through |
| 105263 | } |
| 105264 | default: |
| 105265 | return WRC_Continue; |
| 105266 | } |
| 105267 | } |
| @@ -106839,11 +106991,11 @@ | |
| 106991 | } |
| 106992 | if( rc==SQLITE_OK && pStep->zTarget ){ |
| 106993 | SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); |
| 106994 | if( pSrc ){ |
| 106995 | int i; |
| 106996 | for(i=0; i<pSrc->nSrc && rc==SQLITE_OK; i++){ |
| 106997 | struct SrcList_item *p = &pSrc->a[i]; |
| 106998 | p->pTab = sqlite3LocateTableItem(pParse, 0, p); |
| 106999 | p->iCursor = pParse->nTab++; |
| 107000 | if( p->pTab==0 ){ |
| 107001 | rc = SQLITE_ERROR; |
| @@ -107581,11 +107733,11 @@ | |
| 107733 | }; |
| 107734 | int i; |
| 107735 | sqlite3 *db = pParse->db; |
| 107736 | Db *pDb; |
| 107737 | Vdbe *v = sqlite3GetVdbe(pParse); |
| 107738 | u32 aRoot[ArraySize(aTable)]; |
| 107739 | u8 aCreateTbl[ArraySize(aTable)]; |
| 107740 | #ifdef SQLITE_ENABLE_STAT4 |
| 107741 | const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; |
| 107742 | #else |
| 107743 | const int nToOpen = 1; |
| @@ -107610,11 +107762,11 @@ | |
| 107762 | ** of the new table in register pParse->regRoot. This is important |
| 107763 | ** because the OpenWrite opcode below will be needing it. */ |
| 107764 | sqlite3NestedParse(pParse, |
| 107765 | "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols |
| 107766 | ); |
| 107767 | aRoot[i] = (u32)pParse->regRoot; |
| 107768 | aCreateTbl[i] = OPFLAG_P2ISREG; |
| 107769 | } |
| 107770 | }else{ |
| 107771 | /* The table already exists. If zWhere is not NULL, delete all entries |
| 107772 | ** associated with the table zWhere. If zWhere is NULL, delete the |
| @@ -107630,19 +107782,19 @@ | |
| 107782 | }else if( db->xPreUpdateCallback ){ |
| 107783 | sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab); |
| 107784 | #endif |
| 107785 | }else{ |
| 107786 | /* The sqlite_stat[134] table already exists. Delete all rows. */ |
| 107787 | sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb); |
| 107788 | } |
| 107789 | } |
| 107790 | } |
| 107791 | |
| 107792 | /* Open the sqlite_stat[134] tables for writing. */ |
| 107793 | for(i=0; i<nToOpen; i++){ |
| 107794 | assert( i<ArraySize(aTable) ); |
| 107795 | sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3); |
| 107796 | sqlite3VdbeChangeP5(v, aCreateTbl[i]); |
| 107797 | VdbeComment((v, aTable[i].zName)); |
| 107798 | } |
| 107799 | } |
| 107800 | |
| @@ -110271,11 +110423,11 @@ | |
| 110423 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 110424 | ** codeTableLocks() functions. |
| 110425 | */ |
| 110426 | struct TableLock { |
| 110427 | int iDb; /* The database containing the table to be locked */ |
| 110428 | Pgno iTab; /* The root page of the table to be locked */ |
| 110429 | u8 isWriteLock; /* True for write lock. False for a read lock */ |
| 110430 | const char *zLockName; /* Name of the table */ |
| 110431 | }; |
| 110432 | |
| 110433 | /* |
| @@ -110289,11 +110441,11 @@ | |
| 110441 | ** codeTableLocks() which occurs during sqlite3FinishCoding(). |
| 110442 | */ |
| 110443 | SQLITE_PRIVATE void sqlite3TableLock( |
| 110444 | Parse *pParse, /* Parsing context */ |
| 110445 | int iDb, /* Index of the database containing the table to lock */ |
| 110446 | Pgno iTab, /* Root page number of the table to be locked */ |
| 110447 | u8 isWriteLock, /* True for a write lock */ |
| 110448 | const char *zName /* Name of the table to be locked */ |
| 110449 | ){ |
| 110450 | Parse *pToplevel = sqlite3ParseToplevel(pParse); |
| 110451 | int i; |
| @@ -111128,23 +111280,24 @@ | |
| 111280 | const char *zName, /* Name of the object to check */ |
| 111281 | const char *zType, /* Type of this object */ |
| 111282 | const char *zTblName /* Parent table name for triggers and indexes */ |
| 111283 | ){ |
| 111284 | sqlite3 *db = pParse->db; |
| 111285 | if( sqlite3WritableSchema(db) |
| 111286 | || db->init.imposterTable |
| 111287 | || !sqlite3Config.bExtraSchemaChecks |
| 111288 | ){ |
| 111289 | /* Skip these error checks for writable_schema=ON */ |
| 111290 | return SQLITE_OK; |
| 111291 | } |
| 111292 | if( db->init.busy ){ |
| 111293 | if( sqlite3_stricmp(zType, db->init.azInit[0]) |
| 111294 | || sqlite3_stricmp(zName, db->init.azInit[1]) |
| 111295 | || sqlite3_stricmp(zTblName, db->init.azInit[2]) |
| 111296 | ){ |
| 111297 | sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ |
| 111298 | return SQLITE_ERROR; |
| 111299 | } |
| 111300 | }else{ |
| 111301 | if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7)) |
| 111302 | || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName)) |
| 111303 | ){ |
| @@ -112354,11 +112507,11 @@ | |
| 112507 | ** table entry. This is only required if currently generating VDBE |
| 112508 | ** code for a CREATE TABLE (not when parsing one as part of reading |
| 112509 | ** a database schema). */ |
| 112510 | if( v && pPk->tnum>0 ){ |
| 112511 | assert( db->init.busy==0 ); |
| 112512 | sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto); |
| 112513 | } |
| 112514 | |
| 112515 | /* The root page of the PRIMARY KEY is the table root page */ |
| 112516 | pPk->tnum = pTab->tnum; |
| 112517 | |
| @@ -112942,14 +113095,12 @@ | |
| 113095 | ** statement that defines the view. |
| 113096 | */ |
| 113097 | assert( pTable->pSelect ); |
| 113098 | pSel = sqlite3SelectDup(db, pTable->pSelect, 0); |
| 113099 | if( pSel ){ |
| 113100 | u8 eParseMode = pParse->eParseMode; |
| 113101 | pParse->eParseMode = PARSE_MODE_NORMAL; |
| 113102 | n = pParse->nTab; |
| 113103 | sqlite3SrcListAssignCursors(pParse, pSel->pSrc); |
| 113104 | pTable->nCol = -1; |
| 113105 | DisableLookaside; |
| 113106 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| @@ -112993,13 +113144,11 @@ | |
| 113144 | } |
| 113145 | pTable->nNVCol = pTable->nCol; |
| 113146 | sqlite3DeleteTable(db, pSelTab); |
| 113147 | sqlite3SelectDelete(db, pSel); |
| 113148 | EnableLookaside; |
| 113149 | pParse->eParseMode = eParseMode; |
| 113150 | } else { |
| 113151 | nErr++; |
| 113152 | } |
| 113153 | pTable->pSchema->schemaFlags |= DB_UnresetViews; |
| 113154 | if( db->mallocFailed ){ |
| @@ -113050,11 +113199,11 @@ | |
| 113199 | ** We must continue looping until all tables and indices with |
| 113200 | ** rootpage==iFrom have been converted to have a rootpage of iTo |
| 113201 | ** in order to be certain that we got the right one. |
| 113202 | */ |
| 113203 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 113204 | SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){ |
| 113205 | HashElem *pElem; |
| 113206 | Hash *pHash; |
| 113207 | Db *pDb; |
| 113208 | |
| 113209 | assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); |
| @@ -113127,22 +113276,22 @@ | |
| 113276 | ** and root page 5 happened to be the largest root-page number in the |
| 113277 | ** database, then root page 5 would be moved to page 4 by the |
| 113278 | ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit |
| 113279 | ** a free-list page. |
| 113280 | */ |
| 113281 | Pgno iTab = pTab->tnum; |
| 113282 | Pgno iDestroyed = 0; |
| 113283 | |
| 113284 | while( 1 ){ |
| 113285 | Index *pIdx; |
| 113286 | Pgno iLargest = 0; |
| 113287 | |
| 113288 | if( iDestroyed==0 || iTab<iDestroyed ){ |
| 113289 | iLargest = iTab; |
| 113290 | } |
| 113291 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 113292 | Pgno iIdx = pIdx->tnum; |
| 113293 | assert( pIdx->pSchema==pTab->pSchema ); |
| 113294 | if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){ |
| 113295 | iLargest = iIdx; |
| 113296 | } |
| 113297 | } |
| @@ -113561,11 +113710,11 @@ | |
| 113710 | int iTab = pParse->nTab++; /* Btree cursor used for pTab */ |
| 113711 | int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */ |
| 113712 | int iSorter; /* Cursor opened by OpenSorter (if in use) */ |
| 113713 | int addr1; /* Address of top of loop */ |
| 113714 | int addr2; /* Address to jump to for next iteration */ |
| 113715 | Pgno tnum; /* Root page of index */ |
| 113716 | int iPartIdxLabel; /* Jump to this label to skip a row */ |
| 113717 | Vdbe *v; /* Generate code into this virtual machine */ |
| 113718 | KeyInfo *pKey; /* KeyInfo for index */ |
| 113719 | int regRecord; /* Register holding assembled index record */ |
| 113720 | sqlite3 *db = pParse->db; /* The database connection */ |
| @@ -113582,11 +113731,11 @@ | |
| 113731 | sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); |
| 113732 | |
| 113733 | v = sqlite3GetVdbe(pParse); |
| 113734 | if( v==0 ) return; |
| 113735 | if( memRootPage>=0 ){ |
| 113736 | tnum = (Pgno)memRootPage; |
| 113737 | }else{ |
| 113738 | tnum = pIndex->tnum; |
| 113739 | } |
| 113740 | pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); |
| 113741 | assert( pKey!=0 || db->mallocFailed || pParse->nErr ); |
| @@ -113607,11 +113756,11 @@ | |
| 113756 | sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); |
| 113757 | sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| 113758 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); |
| 113759 | sqlite3VdbeJumpHere(v, addr1); |
| 113760 | if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); |
| 113761 | sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb, |
| 113762 | (char *)pKey, P4_KEYINFO); |
| 113763 | sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); |
| 113764 | |
| 113765 | addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v); |
| 113766 | if( IsUniqueIndex(pIndex) ){ |
| @@ -114216,11 +114365,11 @@ | |
| 114365 | ** doing so, code a Noop instruction and store its address in |
| 114366 | ** Index.tnum. This is required in case this index is actually a |
| 114367 | ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In |
| 114368 | ** that case the convertToWithoutRowidTable() routine will replace |
| 114369 | ** the Noop with a Goto to jump over the VDBE code generated below. */ |
| 114370 | pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop); |
| 114371 | sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); |
| 114372 | |
| 114373 | /* Gather the complete text of the CREATE INDEX statement into |
| 114374 | ** the zStmt variable |
| 114375 | */ |
| @@ -114258,11 +114407,11 @@ | |
| 114407 | sqlite3VdbeAddParseSchemaOp(v, iDb, |
| 114408 | sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); |
| 114409 | sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); |
| 114410 | } |
| 114411 | |
| 114412 | sqlite3VdbeJumpHere(v, (int)pIndex->tnum); |
| 114413 | } |
| 114414 | } |
| 114415 | if( db->init.busy || pTblName==0 ){ |
| 114416 | pIndex->pNext = pTab->pIndex; |
| 114417 | pTab->pIndex = pIndex; |
| @@ -114333,11 +114482,11 @@ | |
| 114482 | ** |
| 114483 | ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1 |
| 114484 | ** table but other parts we are having to guess at, then do not let the |
| 114485 | ** estimated number of rows in the table be less than 1000 (LogEst 99). |
| 114486 | ** Failure to do this can cause the indexes for which we do not have |
| 114487 | ** stat1 data to be ignored by the query planner. |
| 114488 | */ |
| 114489 | x = pIdx->pTable->nRowLogEst; |
| 114490 | assert( 99==sqlite3LogEst(1000) ); |
| 114491 | if( x<99 ){ |
| 114492 | pIdx->pTable->nRowLogEst = x = 99; |
| @@ -120309,10 +120458,11 @@ | |
| 120458 | case OE_Cascade: |
| 120459 | if( !pChanges ){ |
| 120460 | pStep->op = TK_DELETE; |
| 120461 | break; |
| 120462 | } |
| 120463 | /* no break */ deliberate_fall_through |
| 120464 | default: |
| 120465 | pStep->op = TK_UPDATE; |
| 120466 | } |
| 120467 | pStep->pTrig = pTrigger; |
| 120468 | pTrigger->pSchema = pTab->pSchema; |
| @@ -120581,11 +120731,11 @@ | |
| 120731 | for(i=1; i<iEnd; i++){ |
| 120732 | VdbeOp *pOp = sqlite3VdbeGetOp(v, i); |
| 120733 | assert( pOp!=0 ); |
| 120734 | if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ |
| 120735 | Index *pIndex; |
| 120736 | Pgno tnum = pOp->p2; |
| 120737 | if( tnum==pTab->tnum ){ |
| 120738 | return 1; |
| 120739 | } |
| 120740 | for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ |
| 120741 | if( tnum==pIndex->tnum ){ |
| @@ -122013,11 +122163,11 @@ | |
| 122163 | sqlite3VdbeJumpHere(v, addr1); |
| 122164 | break; |
| 122165 | } |
| 122166 | case OE_Abort: |
| 122167 | sqlite3MayAbort(pParse); |
| 122168 | /* no break */ deliberate_fall_through |
| 122169 | case OE_Rollback: |
| 122170 | case OE_Fail: { |
| 122171 | char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName, |
| 122172 | pCol->zName); |
| 122173 | sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, |
| @@ -122241,11 +122391,11 @@ | |
| 122391 | VdbeCoverage(v); |
| 122392 | |
| 122393 | switch( onError ){ |
| 122394 | default: { |
| 122395 | onError = OE_Abort; |
| 122396 | /* no break */ deliberate_fall_through |
| 122397 | } |
| 122398 | case OE_Rollback: |
| 122399 | case OE_Abort: |
| 122400 | case OE_Fail: { |
| 122401 | testcase( onError==OE_Rollback ); |
| @@ -122302,11 +122452,11 @@ | |
| 122452 | break; |
| 122453 | } |
| 122454 | #ifndef SQLITE_OMIT_UPSERT |
| 122455 | case OE_Update: { |
| 122456 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur); |
| 122457 | /* no break */ deliberate_fall_through |
| 122458 | } |
| 122459 | #endif |
| 122460 | case OE_Ignore: { |
| 122461 | testcase( onError==OE_Ignore ); |
| 122462 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -122523,11 +122673,11 @@ | |
| 122673 | break; |
| 122674 | } |
| 122675 | #ifndef SQLITE_OMIT_UPSERT |
| 122676 | case OE_Update: { |
| 122677 | sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix); |
| 122678 | /* no break */ deliberate_fall_through |
| 122679 | } |
| 122680 | #endif |
| 122681 | case OE_Ignore: { |
| 122682 | testcase( onError==OE_Ignore ); |
| 122683 | sqlite3VdbeGoto(v, ignoreDest); |
| @@ -126204,17 +126354,23 @@ | |
| 126354 | ** |
| 126355 | ** Return the number of pages in the specified database. |
| 126356 | */ |
| 126357 | case PragTyp_PAGE_COUNT: { |
| 126358 | int iReg; |
| 126359 | i64 x = 0; |
| 126360 | sqlite3CodeVerifySchema(pParse, iDb); |
| 126361 | iReg = ++pParse->nMem; |
| 126362 | if( sqlite3Tolower(zLeft[0])=='p' ){ |
| 126363 | sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg); |
| 126364 | }else{ |
| 126365 | if( zRight && sqlite3DecOrHexToI64(zRight,&x)==0 ){ |
| 126366 | if( x<0 ) x = 0; |
| 126367 | else if( x>0xfffffffe ) x = 0xfffffffe; |
| 126368 | }else{ |
| 126369 | x = 0; |
| 126370 | } |
| 126371 | sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, (int)x); |
| 126372 | } |
| 126373 | sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1); |
| 126374 | break; |
| 126375 | } |
| 126376 | |
| @@ -127113,13 +127269,26 @@ | |
| 127269 | ** |
| 127270 | ** The "quick_check" is reduced version of |
| 127271 | ** integrity_check designed to detect most database corruption |
| 127272 | ** without the overhead of cross-checking indexes. Quick_check |
| 127273 | ** is linear time wherease integrity_check is O(NlogN). |
| 127274 | ** |
| 127275 | ** The maximum nubmer of errors is 100 by default. A different default |
| 127276 | ** can be specified using a numeric parameter N. |
| 127277 | ** |
| 127278 | ** Or, the parameter N can be the name of a table. In that case, only |
| 127279 | ** the one table named is verified. The freelist is only verified if |
| 127280 | ** the named table is "sqlite_schema" (or one of its aliases). |
| 127281 | ** |
| 127282 | ** All schemas are checked by default. To check just a single |
| 127283 | ** schema, use the form: |
| 127284 | ** |
| 127285 | ** PRAGMA schema.integrity_check; |
| 127286 | */ |
| 127287 | case PragTyp_INTEGRITY_CHECK: { |
| 127288 | int i, j, addr, mxErr; |
| 127289 | Table *pObjTab = 0; /* Check only this one table, if not NULL */ |
| 127290 | |
| 127291 | int isQuick = (sqlite3Tolower(zLeft[0])=='q'); |
| 127292 | |
| 127293 | /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check", |
| 127294 | ** then iDb is set to the index of the database identified by <db>. |
| @@ -127138,13 +127307,17 @@ | |
| 127307 | pParse->nMem = 6; |
| 127308 | |
| 127309 | /* Set the maximum error count */ |
| 127310 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 127311 | if( zRight ){ |
| 127312 | if( sqlite3GetInt32(zRight, &mxErr) ){ |
| 127313 | if( mxErr<=0 ){ |
| 127314 | mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; |
| 127315 | } |
| 127316 | }else{ |
| 127317 | pObjTab = sqlite3LocateTable(pParse, 0, zRight, |
| 127318 | iDb>=0 ? db->aDb[iDb].zDbSName : 0); |
| 127319 | } |
| 127320 | } |
| 127321 | sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */ |
| 127322 | |
| 127323 | /* Do an integrity check on each database file */ |
| @@ -127169,19 +127342,25 @@ | |
| 127342 | pTbls = &db->aDb[i].pSchema->tblHash; |
| 127343 | for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ |
| 127344 | Table *pTab = sqliteHashData(x); /* Current table */ |
| 127345 | Index *pIdx; /* An index on pTab */ |
| 127346 | int nIdx; /* Number of indexes on pTab */ |
| 127347 | if( pObjTab && pObjTab!=pTab ) continue; |
| 127348 | if( HasRowid(pTab) ) cnt++; |
| 127349 | for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } |
| 127350 | if( nIdx>mxIdx ) mxIdx = nIdx; |
| 127351 | } |
| 127352 | if( cnt==0 ) continue; |
| 127353 | if( pObjTab ) cnt++; |
| 127354 | aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1)); |
| 127355 | if( aRoot==0 ) break; |
| 127356 | cnt = 0; |
| 127357 | if( pObjTab ) aRoot[++cnt] = 0; |
| 127358 | for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ |
| 127359 | Table *pTab = sqliteHashData(x); |
| 127360 | Index *pIdx; |
| 127361 | if( pObjTab && pObjTab!=pTab ) continue; |
| 127362 | if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum; |
| 127363 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 127364 | aRoot[++cnt] = pIdx->tnum; |
| 127365 | } |
| 127366 | } |
| @@ -127211,10 +127390,11 @@ | |
| 127390 | int loopTop; |
| 127391 | int iDataCur, iIdxCur; |
| 127392 | int r1 = -1; |
| 127393 | |
| 127394 | if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */ |
| 127395 | if( pObjTab && pObjTab!=pTab ) continue; |
| 127396 | pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); |
| 127397 | sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0, |
| 127398 | 1, 0, &iDataCur, &iIdxCur); |
| 127399 | /* reg[7] counts the number of entries in the table. |
| 127400 | ** reg[8+i] counts the number of entries in the i-th index |
| @@ -128272,11 +128452,17 @@ | |
| 128452 | sqlite3_stmt *pStmt; |
| 128453 | TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ |
| 128454 | |
| 128455 | assert( db->init.busy ); |
| 128456 | db->init.iDb = iDb; |
| 128457 | if( sqlite3GetUInt32(argv[3], &db->init.newTnum)==0 |
| 128458 | || (db->init.newTnum>pData->mxPage && pData->mxPage>0) |
| 128459 | ){ |
| 128460 | if( sqlite3Config.bExtraSchemaChecks ){ |
| 128461 | corruptSchema(pData, argv[1], "invalid rootpage"); |
| 128462 | } |
| 128463 | } |
| 128464 | db->init.orphanTrigger = 0; |
| 128465 | db->init.azInit = argv; |
| 128466 | pStmt = 0; |
| 128467 | TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0); |
| 128468 | rc = db->errCode; |
| @@ -128305,16 +128491,21 @@ | |
| 128491 | ** been created when we processed the CREATE TABLE. All we have |
| 128492 | ** to do here is record the root page number for that index. |
| 128493 | */ |
| 128494 | Index *pIndex; |
| 128495 | pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName); |
| 128496 | if( pIndex==0 ){ |
| 128497 | corruptSchema(pData, argv[1], "orphan index"); |
| 128498 | }else |
| 128499 | if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0 |
| 128500 | || pIndex->tnum<2 |
| 128501 | || pIndex->tnum>pData->mxPage |
| 128502 | || sqlite3IndexHasDuplicateRootPage(pIndex) |
| 128503 | ){ |
| 128504 | if( sqlite3Config.bExtraSchemaChecks ){ |
| 128505 | corruptSchema(pData, argv[1], "invalid rootpage"); |
| 128506 | } |
| 128507 | } |
| 128508 | } |
| 128509 | return 0; |
| 128510 | } |
| 128511 | |
| @@ -128364,10 +128555,11 @@ | |
| 128555 | initData.iDb = iDb; |
| 128556 | initData.rc = SQLITE_OK; |
| 128557 | initData.pzErrMsg = pzErrMsg; |
| 128558 | initData.mInitFlags = mFlags; |
| 128559 | initData.nInitRow = 0; |
| 128560 | initData.mxPage = 0; |
| 128561 | sqlite3InitCallback(&initData, 5, (char **)azArg, 0); |
| 128562 | db->mDbFlags &= mask; |
| 128563 | if( initData.rc ){ |
| 128564 | rc = initData.rc; |
| 128565 | goto error_out; |
| @@ -128486,10 +128678,11 @@ | |
| 128678 | } |
| 128679 | |
| 128680 | /* Read the schema information out of the schema tables |
| 128681 | */ |
| 128682 | assert( db->init.busy ); |
| 128683 | initData.mxPage = sqlite3BtreeLastPage(pDb->pBt); |
| 128684 | { |
| 128685 | char *zSql; |
| 128686 | zSql = sqlite3MPrintf(db, |
| 128687 | "SELECT*FROM\"%w\".%s ORDER BY rowid", |
| 128688 | db->aDb[iDb].zDbSName, zSchemaTabName); |
| @@ -129368,12 +129561,14 @@ | |
| 129561 | ** Return the index of a column in a table. Return -1 if the column |
| 129562 | ** is not contained in the table. |
| 129563 | */ |
| 129564 | static int columnIndex(Table *pTab, const char *zCol){ |
| 129565 | int i; |
| 129566 | u8 h = sqlite3StrIHash(zCol); |
| 129567 | Column *pCol; |
| 129568 | for(pCol=pTab->aCol, i=0; i<pTab->nCol; pCol++, i++){ |
| 129569 | if( pCol->hName==h && sqlite3StrICmp(pCol->zName, zCol)==0 ) return i; |
| 129570 | } |
| 129571 | return -1; |
| 129572 | } |
| 129573 | |
| 129574 | /* |
| @@ -130231,20 +130426,24 @@ | |
| 130426 | sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1); |
| 130427 | break; |
| 130428 | } |
| 130429 | |
| 130430 | case SRT_Upfrom: { |
| 130431 | if( pSort ){ |
| 130432 | pushOntoSorter( |
| 130433 | pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); |
| 130434 | }else{ |
| 130435 | int i2 = pDest->iSDParm2; |
| 130436 | int r1 = sqlite3GetTempReg(pParse); |
| 130437 | |
| 130438 | /* If the UPDATE FROM join is an aggregate that matches no rows, it |
| 130439 | ** might still be trying to return one row, because that is what |
| 130440 | ** aggregates do. Don't record that empty row in the output table. */ |
| 130441 | sqlite3VdbeAddOp2(v, OP_IsNull, regResult, iBreak); VdbeCoverage(v); |
| 130442 | |
| 130443 | sqlite3VdbeAddOp3(v, OP_MakeRecord, |
| 130444 | regResult+(i2<0), nResultCol-(i2<0), r1); |
| 130445 | if( i2<0 ){ |
| 130446 | sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult); |
| 130447 | }else{ |
| 130448 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2); |
| 130449 | } |
| @@ -130682,11 +130881,10 @@ | |
| 130881 | case SRT_Mem: { |
| 130882 | /* The LIMIT clause will terminate the loop for us */ |
| 130883 | break; |
| 130884 | } |
| 130885 | #endif |
| 130886 | case SRT_Upfrom: { |
| 130887 | int i2 = pDest->iSDParm2; |
| 130888 | int r1 = sqlite3GetTempReg(pParse); |
| 130889 | sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1); |
| 130890 | if( i2<0 ){ |
| @@ -130694,11 +130892,10 @@ | |
| 130892 | }else{ |
| 130893 | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2); |
| 130894 | } |
| 130895 | break; |
| 130896 | } |
| 130897 | default: { |
| 130898 | assert( eDest==SRT_Output || eDest==SRT_Coroutine ); |
| 130899 | testcase( eDest==SRT_Output ); |
| 130900 | testcase( eDest==SRT_Coroutine ); |
| 130901 | if( eDest==SRT_Output ){ |
| @@ -132281,11 +132478,11 @@ | |
| 132478 | KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ |
| 132479 | KeyInfo *pKeyMerge; /* Comparison information for merging rows */ |
| 132480 | sqlite3 *db; /* Database connection */ |
| 132481 | ExprList *pOrderBy; /* The ORDER BY clause */ |
| 132482 | int nOrderBy; /* Number of terms in the ORDER BY clause */ |
| 132483 | u32 *aPermute; /* Mapping from ORDER BY terms to result set columns */ |
| 132484 | |
| 132485 | assert( p->pOrderBy!=0 ); |
| 132486 | assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ |
| 132487 | db = pParse->db; |
| 132488 | v = pParse->pVdbe; |
| @@ -132330,11 +132527,11 @@ | |
| 132527 | ** row of results comes from selectA or selectB. Also add explicit |
| 132528 | ** collations to the ORDER BY clause terms so that when the subqueries |
| 132529 | ** to the right and the left are evaluated, they use the correct |
| 132530 | ** collation. |
| 132531 | */ |
| 132532 | aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); |
| 132533 | if( aPermute ){ |
| 132534 | struct ExprList_item *pItem; |
| 132535 | aPermute[0] = nOrderBy; |
| 132536 | for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ |
| 132537 | assert( pItem->u.x.iOrderByCol>0 ); |
| @@ -133286,11 +133483,11 @@ | |
| 133483 | sqlite3AggInfoPersistWalkerInit(&w, pParse); |
| 133484 | sqlite3WalkSelect(&w,pSub1); |
| 133485 | sqlite3SelectDelete(db, pSub1); |
| 133486 | |
| 133487 | #if SELECTTRACE_ENABLED |
| 133488 | if( sqlite3_unsupported_selecttrace & 0x100 ){ |
| 133489 | SELECTTRACE(0x100,pParse,p,("After flattening:\n")); |
| 133490 | sqlite3TreeViewSelect(0, p, 0); |
| 133491 | } |
| 133492 | #endif |
| 133493 | |
| @@ -134724,11 +134921,11 @@ | |
| 134921 | sWalker.pParse = pParse; |
| 134922 | sWalker.xExprCallback = havingToWhereExprCb; |
| 134923 | sWalker.u.pSelect = p; |
| 134924 | sqlite3WalkExpr(&sWalker, p->pHaving); |
| 134925 | #if SELECTTRACE_ENABLED |
| 134926 | if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){ |
| 134927 | SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n")); |
| 134928 | sqlite3TreeViewSelect(0, p, 0); |
| 134929 | } |
| 134930 | #endif |
| 134931 | } |
| @@ -134846,11 +135043,11 @@ | |
| 135043 | } |
| 135044 | p->pEList->a[0].pExpr = pExpr; |
| 135045 | p->selFlags &= ~SF_Aggregate; |
| 135046 | |
| 135047 | #if SELECTTRACE_ENABLED |
| 135048 | if( sqlite3_unsupported_selecttrace & 0x400 ){ |
| 135049 | SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); |
| 135050 | sqlite3TreeViewSelect(0, p, 0); |
| 135051 | } |
| 135052 | #endif |
| 135053 | return 1; |
| @@ -134899,11 +135096,11 @@ | |
| 135096 | return 1; |
| 135097 | } |
| 135098 | if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; |
| 135099 | #if SELECTTRACE_ENABLED |
| 135100 | SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); |
| 135101 | if( sqlite3_unsupported_selecttrace & 0x100 ){ |
| 135102 | sqlite3TreeViewSelect(0, p, 0); |
| 135103 | } |
| 135104 | #endif |
| 135105 | |
| 135106 | assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); |
| @@ -134926,11 +135123,11 @@ | |
| 135123 | if( pParse->nErr || db->mallocFailed ){ |
| 135124 | goto select_end; |
| 135125 | } |
| 135126 | assert( p->pEList!=0 ); |
| 135127 | #if SELECTTRACE_ENABLED |
| 135128 | if( sqlite3_unsupported_selecttrace & 0x104 ){ |
| 135129 | SELECTTRACE(0x104,pParse,p, ("after name resolution:\n")); |
| 135130 | sqlite3TreeViewSelect(0, p, 0); |
| 135131 | } |
| 135132 | #endif |
| 135133 | |
| @@ -134961,11 +135158,11 @@ | |
| 135158 | if( rc ){ |
| 135159 | assert( db->mallocFailed || pParse->nErr>0 ); |
| 135160 | goto select_end; |
| 135161 | } |
| 135162 | #if SELECTTRACE_ENABLED |
| 135163 | if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){ |
| 135164 | SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n")); |
| 135165 | sqlite3TreeViewSelect(0, p, 0); |
| 135166 | } |
| 135167 | #endif |
| 135168 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| @@ -135068,11 +135265,11 @@ | |
| 135265 | */ |
| 135266 | if( p->pPrior ){ |
| 135267 | rc = multiSelect(pParse, p, pDest); |
| 135268 | #if SELECTTRACE_ENABLED |
| 135269 | SELECTTRACE(0x1,pParse,p,("end compound-select processing\n")); |
| 135270 | if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ |
| 135271 | sqlite3TreeViewSelect(0, p, 0); |
| 135272 | } |
| 135273 | #endif |
| 135274 | if( p->pNext==0 ) ExplainQueryPlanPop(pParse); |
| 135275 | return rc; |
| @@ -135087,11 +135284,11 @@ | |
| 135284 | if( pTabList->nSrc>1 |
| 135285 | && OptimizationEnabled(db, SQLITE_PropagateConst) |
| 135286 | && propagateConstants(pParse, p) |
| 135287 | ){ |
| 135288 | #if SELECTTRACE_ENABLED |
| 135289 | if( sqlite3_unsupported_selecttrace & 0x100 ){ |
| 135290 | SELECTTRACE(0x100,pParse,p,("After constant propagation:\n")); |
| 135291 | sqlite3TreeViewSelect(0, p, 0); |
| 135292 | } |
| 135293 | #endif |
| 135294 | }else{ |
| @@ -135175,11 +135372,11 @@ | |
| 135372 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 135373 | && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor, |
| 135374 | (pItem->fg.jointype & JT_OUTER)!=0) |
| 135375 | ){ |
| 135376 | #if SELECTTRACE_ENABLED |
| 135377 | if( sqlite3_unsupported_selecttrace & 0x100 ){ |
| 135378 | SELECTTRACE(0x100,pParse,p, |
| 135379 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| 135380 | sqlite3TreeViewSelect(0, p, 0); |
| 135381 | } |
| 135382 | #endif |
| @@ -135275,11 +135472,11 @@ | |
| 135472 | pGroupBy = p->pGroupBy; |
| 135473 | pHaving = p->pHaving; |
| 135474 | sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; |
| 135475 | |
| 135476 | #if SELECTTRACE_ENABLED |
| 135477 | if( sqlite3_unsupported_selecttrace & 0x400 ){ |
| 135478 | SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n")); |
| 135479 | sqlite3TreeViewSelect(0, p, 0); |
| 135480 | } |
| 135481 | #endif |
| 135482 | |
| @@ -135311,11 +135508,11 @@ | |
| 135508 | ** the sDistinct.isTnct is still set. Hence, isTnct represents the |
| 135509 | ** original setting of the SF_Distinct flag, not the current setting */ |
| 135510 | assert( sDistinct.isTnct ); |
| 135511 | |
| 135512 | #if SELECTTRACE_ENABLED |
| 135513 | if( sqlite3_unsupported_selecttrace & 0x400 ){ |
| 135514 | SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n")); |
| 135515 | sqlite3TreeViewSelect(0, p, 0); |
| 135516 | } |
| 135517 | #endif |
| 135518 | } |
| @@ -135559,11 +135756,11 @@ | |
| 135756 | sNC.ncFlags &= ~NC_InAggFunc; |
| 135757 | } |
| 135758 | pAggInfo->mxReg = pParse->nMem; |
| 135759 | if( db->mallocFailed ) goto select_end; |
| 135760 | #if SELECTTRACE_ENABLED |
| 135761 | if( sqlite3_unsupported_selecttrace & 0x400 ){ |
| 135762 | int ii; |
| 135763 | SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); |
| 135764 | sqlite3TreeViewSelect(0, p, 0); |
| 135765 | for(ii=0; ii<pAggInfo->nColumn; ii++){ |
| 135766 | sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", |
| @@ -135823,11 +136020,11 @@ | |
| 136020 | const int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
| 136021 | const int iCsr = pParse->nTab++; /* Cursor to scan b-tree */ |
| 136022 | Index *pIdx; /* Iterator variable */ |
| 136023 | KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */ |
| 136024 | Index *pBest = 0; /* Best index found so far */ |
| 136025 | Pgno iRoot = pTab->tnum; /* Root page of scanned b-tree */ |
| 136026 | |
| 136027 | sqlite3CodeVerifySchema(pParse, iDb); |
| 136028 | sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
| 136029 | |
| 136030 | /* Search for the index that has the lowest scan cost. |
| @@ -135855,11 +136052,11 @@ | |
| 136052 | iRoot = pBest->tnum; |
| 136053 | pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest); |
| 136054 | } |
| 136055 | |
| 136056 | /* Open a read-only cursor, execute the OP_Count, close the cursor. */ |
| 136057 | sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, (int)iRoot, iDb, 1); |
| 136058 | if( pKeyInfo ){ |
| 136059 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); |
| 136060 | } |
| 136061 | sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); |
| 136062 | sqlite3VdbeAddOp1(v, OP_Close, iCsr); |
| @@ -135978,11 +136175,11 @@ | |
| 136175 | } |
| 136176 | #endif |
| 136177 | |
| 136178 | #if SELECTTRACE_ENABLED |
| 136179 | SELECTTRACE(0x1,pParse,p,("end processing\n")); |
| 136180 | if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ |
| 136181 | sqlite3TreeViewSelect(0, p, 0); |
| 136182 | } |
| 136183 | #endif |
| 136184 | ExplainQueryPlanPop(pParse); |
| 136185 | return rc; |
| @@ -142375,11 +142572,11 @@ | |
| 142572 | if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) |
| 142573 | && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask) |
| 142574 | ){ |
| 142575 | int i; |
| 142576 | Table *pTab = pIdx->pTable; |
| 142577 | u32 *ai = (u32*)sqlite3DbMallocZero(pParse->db, sizeof(u32)*(pTab->nCol+1)); |
| 142578 | if( ai ){ |
| 142579 | ai[0] = pTab->nCol; |
| 142580 | for(i=0; i<pIdx->nColumn-1; i++){ |
| 142581 | int x1, x2; |
| 142582 | assert( pIdx->aiColumn[i]<pTab->nCol ); |
| @@ -151749,11 +151946,11 @@ | |
| 151946 | assert( pWin->pOwner==pExpr ); |
| 151947 | return WRC_Prune; |
| 151948 | } |
| 151949 | } |
| 151950 | } |
| 151951 | /* no break */ deliberate_fall_through |
| 151952 | |
| 151953 | case TK_AGG_FUNCTION: |
| 151954 | case TK_COLUMN: { |
| 151955 | int iCol = -1; |
| 151956 | if( p->pSub ){ |
| @@ -159966,10 +160163,11 @@ | |
| 160163 | *tokenType = TK_DOT; |
| 160164 | return 1; |
| 160165 | } |
| 160166 | /* If the next character is a digit, this is a floating point |
| 160167 | ** number that begins with ".". Fall thru into the next case */ |
| 160168 | /* no break */ deliberate_fall_through |
| 160169 | } |
| 160170 | case CC_DIGIT: { |
| 160171 | testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' ); |
| 160172 | testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' ); |
| 160173 | testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' ); |
| @@ -160070,10 +160268,11 @@ | |
| 160268 | return i; |
| 160269 | } |
| 160270 | #endif |
| 160271 | /* If it is not a BLOB literal, then it must be an ID, since no |
| 160272 | ** SQL keywords start with the letter 'x'. Fall through */ |
| 160273 | /* no break */ deliberate_fall_through |
| 160274 | } |
| 160275 | case CC_ID: { |
| 160276 | i = 1; |
| 160277 | break; |
| 160278 | } |
| @@ -162000,11 +162199,11 @@ | |
| 162199 | if( !sqlite3SafetyCheckSickOrOk(db) ){ |
| 162200 | return SQLITE_MISUSE_BKPT; |
| 162201 | } |
| 162202 | sqlite3_mutex_enter(db->mutex); |
| 162203 | if( db->mTrace & SQLITE_TRACE_CLOSE ){ |
| 162204 | db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0); |
| 162205 | } |
| 162206 | |
| 162207 | /* Force xDisconnect calls on all virtual tables */ |
| 162208 | disconnectAllVtab(db); |
| 162209 | |
| @@ -162889,11 +163088,11 @@ | |
| 163088 | } |
| 163089 | #endif |
| 163090 | sqlite3_mutex_enter(db->mutex); |
| 163091 | pOld = db->pTraceArg; |
| 163092 | db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0; |
| 163093 | db->trace.xLegacy = xTrace; |
| 163094 | db->pTraceArg = pArg; |
| 163095 | sqlite3_mutex_leave(db->mutex); |
| 163096 | return pOld; |
| 163097 | } |
| 163098 | #endif /* SQLITE_OMIT_DEPRECATED */ |
| @@ -162913,11 +163112,11 @@ | |
| 163112 | #endif |
| 163113 | sqlite3_mutex_enter(db->mutex); |
| 163114 | if( mTrace==0 ) xTrace = 0; |
| 163115 | if( xTrace==0 ) mTrace = 0; |
| 163116 | db->mTrace = mTrace; |
| 163117 | db->trace.xV2 = xTrace; |
| 163118 | db->pTraceArg = pArg; |
| 163119 | sqlite3_mutex_leave(db->mutex); |
| 163120 | return SQLITE_OK; |
| 163121 | } |
| 163122 | |
| @@ -164889,10 +165088,16 @@ | |
| 165088 | /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int); |
| 165089 | ** |
| 165090 | ** Set or clear a flag that causes SQLite to verify that type, name, |
| 165091 | ** and tbl_name fields of the sqlite_schema table. This is normally |
| 165092 | ** on, but it is sometimes useful to turn it off for testing. |
| 165093 | ** |
| 165094 | ** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the |
| 165095 | ** verification of rootpage numbers when parsing the schema. This |
| 165096 | ** is useful to make it easier to reach strange internal error states |
| 165097 | ** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled |
| 165098 | ** in production. |
| 165099 | */ |
| 165100 | case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: { |
| 165101 | sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int); |
| 165102 | break; |
| 165103 | } |
| @@ -166503,10 +166708,12 @@ | |
| 166708 | # define TESTONLY(X) |
| 166709 | #endif |
| 166710 | |
| 166711 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 166712 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 166713 | |
| 166714 | #define deliberate_fall_through |
| 166715 | |
| 166716 | #endif /* SQLITE_AMALGAMATION */ |
| 166717 | |
| 166718 | #ifdef SQLITE_DEBUG |
| 166719 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -170146,11 +170353,11 @@ | |
| 170353 | }else if( p->zLanguageid==0 ){ |
| 170354 | sqlite3_result_int(pCtx, 0); |
| 170355 | break; |
| 170356 | }else{ |
| 170357 | iCol = p->nColumn; |
| 170358 | /* no break */ deliberate_fall_through |
| 170359 | } |
| 170360 | |
| 170361 | default: |
| 170362 | /* A user column. Or, if this is a full-table scan, possibly the |
| 170363 | ** language-id column. Seek the cursor. */ |
| @@ -170389,13 +170596,17 @@ | |
| 170596 | } |
| 170597 | if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; |
| 170598 | |
| 170599 | switch( nVal ){ |
| 170600 | case 6: nToken = sqlite3_value_int(apVal[5]); |
| 170601 | /* no break */ deliberate_fall_through |
| 170602 | case 5: iCol = sqlite3_value_int(apVal[4]); |
| 170603 | /* no break */ deliberate_fall_through |
| 170604 | case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); |
| 170605 | /* no break */ deliberate_fall_through |
| 170606 | case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); |
| 170607 | /* no break */ deliberate_fall_through |
| 170608 | case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); |
| 170609 | } |
| 170610 | if( !zEllipsis || !zEnd || !zStart ){ |
| 170611 | sqlite3_result_error_nomem(pContext); |
| 170612 | }else if( nToken==0 ){ |
| @@ -172498,11 +172709,12 @@ | |
| 172709 | ** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK ); |
| 172710 | */ |
| 172711 | fts3EvalRestart(pCsr, pRoot, &rc); |
| 172712 | do { |
| 172713 | fts3EvalNextRow(pCsr, pRoot, &rc); |
| 172714 | assert_fts3_nc( pRoot->bEof==0 ); |
| 172715 | if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB; |
| 172716 | }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); |
| 172717 | } |
| 172718 | } |
| 172719 | return rc; |
| 172720 | } |
| @@ -176607,11 +176819,12 @@ | |
| 176819 | rc = fts3tokQueryTokenizer((Fts3Hash*)pHash, zModule, &pMod, pzErr); |
| 176820 | } |
| 176821 | |
| 176822 | assert( (rc==SQLITE_OK)==(pMod!=0) ); |
| 176823 | if( rc==SQLITE_OK ){ |
| 176824 | const char * const *azArg = 0; |
| 176825 | if( nDequote>1 ) azArg = (const char * const *)&azDequote[1]; |
| 176826 | rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok); |
| 176827 | } |
| 176828 | |
| 176829 | if( rc==SQLITE_OK ){ |
| 176830 | pTab = (Fts3tokTable *)sqlite3_malloc(sizeof(Fts3tokTable)); |
| @@ -185252,10 +185465,14 @@ | |
| 185465 | #ifndef LARGEST_INT64 |
| 185466 | # define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) |
| 185467 | # define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) |
| 185468 | #endif |
| 185469 | |
| 185470 | #ifndef deliberate_fall_through |
| 185471 | # define deliberate_fall_through |
| 185472 | #endif |
| 185473 | |
| 185474 | /* |
| 185475 | ** Versions of isspace(), isalnum() and isdigit() to which it is safe |
| 185476 | ** to pass signed char values. |
| 185477 | */ |
| 185478 | #ifdef sqlite3Isdigit |
| @@ -185670,11 +185887,11 @@ | |
| 185887 | case JSON_STRING: { |
| 185888 | if( pNode->jnFlags & JNODE_RAW ){ |
| 185889 | jsonAppendString(pOut, pNode->u.zJContent, pNode->n); |
| 185890 | break; |
| 185891 | } |
| 185892 | /* no break */ deliberate_fall_through |
| 185893 | } |
| 185894 | case JSON_REAL: |
| 185895 | case JSON_INT: { |
| 185896 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 185897 | break; |
| @@ -185811,11 +186028,11 @@ | |
| 186028 | } |
| 186029 | if( pNode->u.zJContent[0]=='-' ){ i = -i; } |
| 186030 | sqlite3_result_int64(pCtx, i); |
| 186031 | int_done: |
| 186032 | break; |
| 186033 | int_as_real: i=0; /* no break */ deliberate_fall_through |
| 186034 | } |
| 186035 | case JSON_REAL: { |
| 186036 | double r; |
| 186037 | #ifdef SQLITE_AMALGAMATION |
| 186038 | const char *z = pNode->u.zJContent; |
| @@ -187514,10 +187731,11 @@ | |
| 187731 | jsonResult(&x); |
| 187732 | break; |
| 187733 | } |
| 187734 | /* For json_each() path and root are the same so fall through |
| 187735 | ** into the root case */ |
| 187736 | /* no break */ deliberate_fall_through |
| 187737 | } |
| 187738 | default: { |
| 187739 | const char *zRoot = p->zRoot; |
| 187740 | if( zRoot==0 ) zRoot = "$"; |
| 187741 | sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); |
| @@ -187921,10 +188139,11 @@ | |
| 188139 | #endif |
| 188140 | |
| 188141 | /* #include <string.h> */ |
| 188142 | /* #include <stdio.h> */ |
| 188143 | /* #include <assert.h> */ |
| 188144 | /* #include <stdlib.h> */ |
| 188145 | |
| 188146 | /* The following macro is used to suppress compiler warnings. |
| 188147 | */ |
| 188148 | #ifndef UNUSED_PARAMETER |
| 188149 | # define UNUSED_PARAMETER(x) (void)(x) |
| @@ -188258,10 +188477,27 @@ | |
| 188477 | */ |
| 188478 | #ifndef SQLITE_AMALGAMATION |
| 188479 | # define testcase(X) |
| 188480 | #endif |
| 188481 | |
| 188482 | /* |
| 188483 | ** Make sure that the compiler intrinsics we desire are enabled when |
| 188484 | ** compiling with an appropriate version of MSVC unless prevented by |
| 188485 | ** the SQLITE_DISABLE_INTRINSIC define. |
| 188486 | */ |
| 188487 | #if !defined(SQLITE_DISABLE_INTRINSIC) |
| 188488 | # if defined(_MSC_VER) && _MSC_VER>=1400 |
| 188489 | # if !defined(_WIN32_WCE) |
| 188490 | /* # include <intrin.h> */ |
| 188491 | # pragma intrinsic(_byteswap_ulong) |
| 188492 | # pragma intrinsic(_byteswap_uint64) |
| 188493 | # else |
| 188494 | /* # include <cmnintrin.h> */ |
| 188495 | # endif |
| 188496 | # endif |
| 188497 | #endif |
| 188498 | |
| 188499 | /* |
| 188500 | ** Macros to determine whether the machine is big or little endian, |
| 188501 | ** and whether or not that determination is run-time or compile-time. |
| 188502 | ** |
| 188503 | ** For best performance, an attempt is made to guess at the byte-order |
| @@ -205152,12 +205388,17 @@ | |
| 205388 | memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); |
| 205389 | p->in.iNext += nCopy; |
| 205390 | } |
| 205391 | |
| 205392 | p->apValue = (sqlite3_value**)p->tblhdr.aBuf; |
| 205393 | if( p->apValue==0 ){ |
| 205394 | p->abPK = 0; |
| 205395 | p->zTab = 0; |
| 205396 | }else{ |
| 205397 | p->abPK = (u8*)&p->apValue[p->nCol*2]; |
| 205398 | p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0; |
| 205399 | } |
| 205400 | return (p->rc = rc); |
| 205401 | } |
| 205402 | |
| 205403 | /* |
| 205404 | ** Advance the changeset iterator to the next change. |
| @@ -225482,11 +225723,11 @@ | |
| 225723 | int nArg, /* Number of args */ |
| 225724 | sqlite3_value **apUnused /* Function arguments */ |
| 225725 | ){ |
| 225726 | assert( nArg==0 ); |
| 225727 | UNUSED_PARAM2(nArg, apUnused); |
| 225728 | sqlite3_result_text(pCtx, "fts5: 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f", -1, SQLITE_TRANSIENT); |
| 225729 | } |
| 225730 | |
| 225731 | /* |
| 225732 | ** Return true if zName is the extension on one of the shadow tables used |
| 225733 | ** by this module. |
| @@ -230265,12 +230506,12 @@ | |
| 230506 | } |
| 230507 | #endif /* SQLITE_CORE */ |
| 230508 | #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ |
| 230509 | |
| 230510 | /************** End of stmt.c ************************************************/ |
| 230511 | #if __LINE__!=230511 |
| 230512 | #undef SQLITE_SOURCE_ID |
| 230513 | #define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0alt2" |
| 230514 | #endif |
| 230515 | /* Return the source-id for this library */ |
| 230516 | SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } |
| 230517 | /************************** End of sqlite3.c ******************************/ |
| 230518 |
+1
-1
| --- 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.33.0" |
| 127 | 127 | #define SQLITE_VERSION_NUMBER 3033000 |
| 128 | -#define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3" | |
| 128 | +#define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" | |
| 129 | 129 | |
| 130 | 130 | /* |
| 131 | 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | 133 | ** |
| 134 | 134 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -123,11 +123,11 @@ | |
| 123 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 124 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 125 | */ |
| 126 | #define SQLITE_VERSION "3.33.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3033000 |
| 128 | #define SQLITE_SOURCE_ID "2020-07-18 18:59:11 020dbfa2aef20e5872cc3e785d99f45903843401292114b5092b9c8aa829b9c3" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
| --- 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.33.0" |
| 127 | #define SQLITE_VERSION_NUMBER 3033000 |
| 128 | #define SQLITE_SOURCE_ID "2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f" |
| 129 | |
| 130 | /* |
| 131 | ** CAPI3REF: Run-Time Library Version Numbers |
| 132 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 133 | ** |
| 134 |
-13
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -559,23 +559,10 @@ | ||
| 559 | 559 | ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? |
| 560 | 560 | ** |
| 561 | 561 | ** Show diffs of the current working directory and what that |
| 562 | 562 | ** directory would be if STASHID were applied. With gdiff, |
| 563 | 563 | ** gdiff-command is used instead of internal diff logic. |
| 564 | -** | |
| 565 | -** SUMMARY: | |
| 566 | -** * fossil stash | |
| 567 | -** * fossil stash save ?-m|--comment COMMENT? ?FILES...? | |
| 568 | -** * fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? | |
| 569 | -** * fossil stash list|ls ?-v|--verbose? ?-W|--width <num>? | |
| 570 | -** * fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? | |
| 571 | -** * fossil stash gshow|gcat ?STASHID? ?DIFF-OPTIONS? | |
| 572 | -** * fossil stash pop | |
| 573 | -** * fossil stash apply|goto ?STASHID? | |
| 574 | -** * fossil stash drop|rm ?STASHID? ?-a|--all? | |
| 575 | -** * fossil stash diff ?STASHID? ?DIFF-OPTIONS? | |
| 576 | -** * fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? | |
| 577 | 564 | */ |
| 578 | 565 | void stash_cmd(void){ |
| 579 | 566 | const char *zCmd; |
| 580 | 567 | int nCmd; |
| 581 | 568 | int stashid = 0; |
| 582 | 569 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -559,23 +559,10 @@ | |
| 559 | ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? |
| 560 | ** |
| 561 | ** Show diffs of the current working directory and what that |
| 562 | ** directory would be if STASHID were applied. With gdiff, |
| 563 | ** gdiff-command is used instead of internal diff logic. |
| 564 | ** |
| 565 | ** SUMMARY: |
| 566 | ** * fossil stash |
| 567 | ** * fossil stash save ?-m|--comment COMMENT? ?FILES...? |
| 568 | ** * fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? |
| 569 | ** * fossil stash list|ls ?-v|--verbose? ?-W|--width <num>? |
| 570 | ** * fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? |
| 571 | ** * fossil stash gshow|gcat ?STASHID? ?DIFF-OPTIONS? |
| 572 | ** * fossil stash pop |
| 573 | ** * fossil stash apply|goto ?STASHID? |
| 574 | ** * fossil stash drop|rm ?STASHID? ?-a|--all? |
| 575 | ** * fossil stash diff ?STASHID? ?DIFF-OPTIONS? |
| 576 | ** * fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? |
| 577 | */ |
| 578 | void stash_cmd(void){ |
| 579 | const char *zCmd; |
| 580 | int nCmd; |
| 581 | int stashid = 0; |
| 582 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -559,23 +559,10 @@ | |
| 559 | ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? |
| 560 | ** |
| 561 | ** Show diffs of the current working directory and what that |
| 562 | ** directory would be if STASHID were applied. With gdiff, |
| 563 | ** gdiff-command is used instead of internal diff logic. |
| 564 | */ |
| 565 | void stash_cmd(void){ |
| 566 | const char *zCmd; |
| 567 | int nCmd; |
| 568 | int stashid = 0; |
| 569 |
+28
-10
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -292,21 +292,25 @@ | ||
| 292 | 292 | @ </table> |
| 293 | 293 | style_footer(); |
| 294 | 294 | } |
| 295 | 295 | |
| 296 | 296 | /* |
| 297 | -** COMMAND: dbstat* | |
| 297 | +** COMMAND: dbstat | |
| 298 | 298 | ** |
| 299 | 299 | ** Usage: %fossil dbstat OPTIONS |
| 300 | 300 | ** |
| 301 | -** Shows statistics and global information about the repository. | |
| 301 | +** Shows statistics and global information about the repository and/or | |
| 302 | +** verify the integrity of a repository. | |
| 302 | 303 | ** |
| 303 | 304 | ** Options: |
| 304 | 305 | ** |
| 305 | -** --brief|-b Only show essential elements | |
| 306 | -** --db-check Run a PRAGMA quick_check on the repository database | |
| 307 | -** --omit-version-info Omit the SQLite and Fossil version information | |
| 306 | +** --brief|-b Only show essential elements. | |
| 307 | +** --db-check Run "PRAGMA quick_check" on the repository database. | |
| 308 | +** --db-verify Run a full verification of the repository integrity. | |
| 309 | +** This involves decoding and reparsing all artifacts | |
| 310 | +** and can take significant time. | |
| 311 | +** --omit-version-info Omit the SQLite and Fossil version information. | |
| 308 | 312 | */ |
| 309 | 313 | void dbstat_cmd(void){ |
| 310 | 314 | i64 t, fsize; |
| 311 | 315 | int n, m; |
| 312 | 316 | int szMax, szAvg; |
| @@ -317,10 +321,11 @@ | ||
| 317 | 321 | const char *p, *z; |
| 318 | 322 | |
| 319 | 323 | brief = find_option("brief", "b",0)!=0; |
| 320 | 324 | omitVers = find_option("omit-version-info", 0, 0)!=0; |
| 321 | 325 | dbCheck = find_option("db-check",0,0)!=0; |
| 326 | + if( find_option("db-verify",0,0)!=0 ) dbCheck = 2; | |
| 322 | 327 | db_find_and_open_repository(0,0); |
| 323 | 328 | |
| 324 | 329 | /* We should be done with options.. */ |
| 325 | 330 | verify_all_options(); |
| 326 | 331 | |
| @@ -384,13 +389,15 @@ | ||
| 384 | 389 | } |
| 385 | 390 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 386 | 391 | " + 0.99"); |
| 387 | 392 | fossil_print("%*s%,d days or approximately %.2f years.\n", |
| 388 | 393 | colWidth, "project-age:", n, n/365.2425); |
| 389 | - p = db_get("project-code", 0); | |
| 390 | - if( p ){ | |
| 391 | - fossil_print("%*s%s\n", colWidth, "project-id:", p); | |
| 394 | + if( !brief ){ | |
| 395 | + p = db_get("project-code", 0); | |
| 396 | + if( p ){ | |
| 397 | + fossil_print("%*s%s\n", colWidth, "project-id:", p); | |
| 398 | + } | |
| 392 | 399 | } |
| 393 | 400 | #if 0 |
| 394 | 401 | /* Server-id is not useful information any more */ |
| 395 | 402 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 396 | 403 | #endif |
| @@ -412,12 +419,23 @@ | ||
| 412 | 419 | db_int(0, "PRAGMA repository.page_size"), |
| 413 | 420 | db_int(0, "PRAGMA repository.freelist_count"), |
| 414 | 421 | db_text(0, "PRAGMA repository.encoding"), |
| 415 | 422 | db_text(0, "PRAGMA repository.journal_mode")); |
| 416 | 423 | if( dbCheck ){ |
| 417 | - fossil_print("%*s%s\n", colWidth, "database-check:", | |
| 418 | - db_text(0, "PRAGMA quick_check(1)")); | |
| 424 | + if( dbCheck<2 ){ | |
| 425 | + char *zRes = db_text(0, "PRAGMA repository.quick_check(1)"); | |
| 426 | + fossil_print("%*s%s\n", colWidth, "database-check:", zRes); | |
| 427 | + }else{ | |
| 428 | + char *newArgv[3]; | |
| 429 | + newArgv[0] = g.argv[0]; | |
| 430 | + newArgv[1] = "test-integrity"; | |
| 431 | + newArgv[2] = 0; | |
| 432 | + g.argv = newArgv; | |
| 433 | + g.argc = 2; | |
| 434 | + fossil_print("Full repository verification follows:\n"); | |
| 435 | + test_integrity(); | |
| 436 | + } | |
| 419 | 437 | } |
| 420 | 438 | } |
| 421 | 439 | |
| 422 | 440 | /* |
| 423 | 441 | ** WEBPAGE: urllist |
| 424 | 442 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -292,21 +292,25 @@ | |
| 292 | @ </table> |
| 293 | style_footer(); |
| 294 | } |
| 295 | |
| 296 | /* |
| 297 | ** COMMAND: dbstat* |
| 298 | ** |
| 299 | ** Usage: %fossil dbstat OPTIONS |
| 300 | ** |
| 301 | ** Shows statistics and global information about the repository. |
| 302 | ** |
| 303 | ** Options: |
| 304 | ** |
| 305 | ** --brief|-b Only show essential elements |
| 306 | ** --db-check Run a PRAGMA quick_check on the repository database |
| 307 | ** --omit-version-info Omit the SQLite and Fossil version information |
| 308 | */ |
| 309 | void dbstat_cmd(void){ |
| 310 | i64 t, fsize; |
| 311 | int n, m; |
| 312 | int szMax, szAvg; |
| @@ -317,10 +321,11 @@ | |
| 317 | const char *p, *z; |
| 318 | |
| 319 | brief = find_option("brief", "b",0)!=0; |
| 320 | omitVers = find_option("omit-version-info", 0, 0)!=0; |
| 321 | dbCheck = find_option("db-check",0,0)!=0; |
| 322 | db_find_and_open_repository(0,0); |
| 323 | |
| 324 | /* We should be done with options.. */ |
| 325 | verify_all_options(); |
| 326 | |
| @@ -384,13 +389,15 @@ | |
| 384 | } |
| 385 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 386 | " + 0.99"); |
| 387 | fossil_print("%*s%,d days or approximately %.2f years.\n", |
| 388 | colWidth, "project-age:", n, n/365.2425); |
| 389 | p = db_get("project-code", 0); |
| 390 | if( p ){ |
| 391 | fossil_print("%*s%s\n", colWidth, "project-id:", p); |
| 392 | } |
| 393 | #if 0 |
| 394 | /* Server-id is not useful information any more */ |
| 395 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 396 | #endif |
| @@ -412,12 +419,23 @@ | |
| 412 | db_int(0, "PRAGMA repository.page_size"), |
| 413 | db_int(0, "PRAGMA repository.freelist_count"), |
| 414 | db_text(0, "PRAGMA repository.encoding"), |
| 415 | db_text(0, "PRAGMA repository.journal_mode")); |
| 416 | if( dbCheck ){ |
| 417 | fossil_print("%*s%s\n", colWidth, "database-check:", |
| 418 | db_text(0, "PRAGMA quick_check(1)")); |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | ** WEBPAGE: urllist |
| 424 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -292,21 +292,25 @@ | |
| 292 | @ </table> |
| 293 | style_footer(); |
| 294 | } |
| 295 | |
| 296 | /* |
| 297 | ** COMMAND: dbstat |
| 298 | ** |
| 299 | ** Usage: %fossil dbstat OPTIONS |
| 300 | ** |
| 301 | ** Shows statistics and global information about the repository and/or |
| 302 | ** verify the integrity of a repository. |
| 303 | ** |
| 304 | ** Options: |
| 305 | ** |
| 306 | ** --brief|-b Only show essential elements. |
| 307 | ** --db-check Run "PRAGMA quick_check" on the repository database. |
| 308 | ** --db-verify Run a full verification of the repository integrity. |
| 309 | ** This involves decoding and reparsing all artifacts |
| 310 | ** and can take significant time. |
| 311 | ** --omit-version-info Omit the SQLite and Fossil version information. |
| 312 | */ |
| 313 | void dbstat_cmd(void){ |
| 314 | i64 t, fsize; |
| 315 | int n, m; |
| 316 | int szMax, szAvg; |
| @@ -317,10 +321,11 @@ | |
| 321 | const char *p, *z; |
| 322 | |
| 323 | brief = find_option("brief", "b",0)!=0; |
| 324 | omitVers = find_option("omit-version-info", 0, 0)!=0; |
| 325 | dbCheck = find_option("db-check",0,0)!=0; |
| 326 | if( find_option("db-verify",0,0)!=0 ) dbCheck = 2; |
| 327 | db_find_and_open_repository(0,0); |
| 328 | |
| 329 | /* We should be done with options.. */ |
| 330 | verify_all_options(); |
| 331 | |
| @@ -384,13 +389,15 @@ | |
| 389 | } |
| 390 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 391 | " + 0.99"); |
| 392 | fossil_print("%*s%,d days or approximately %.2f years.\n", |
| 393 | colWidth, "project-age:", n, n/365.2425); |
| 394 | if( !brief ){ |
| 395 | p = db_get("project-code", 0); |
| 396 | if( p ){ |
| 397 | fossil_print("%*s%s\n", colWidth, "project-id:", p); |
| 398 | } |
| 399 | } |
| 400 | #if 0 |
| 401 | /* Server-id is not useful information any more */ |
| 402 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 403 | #endif |
| @@ -412,12 +419,23 @@ | |
| 419 | db_int(0, "PRAGMA repository.page_size"), |
| 420 | db_int(0, "PRAGMA repository.freelist_count"), |
| 421 | db_text(0, "PRAGMA repository.encoding"), |
| 422 | db_text(0, "PRAGMA repository.journal_mode")); |
| 423 | if( dbCheck ){ |
| 424 | if( dbCheck<2 ){ |
| 425 | char *zRes = db_text(0, "PRAGMA repository.quick_check(1)"); |
| 426 | fossil_print("%*s%s\n", colWidth, "database-check:", zRes); |
| 427 | }else{ |
| 428 | char *newArgv[3]; |
| 429 | newArgv[0] = g.argv[0]; |
| 430 | newArgv[1] = "test-integrity"; |
| 431 | newArgv[2] = 0; |
| 432 | g.argv = newArgv; |
| 433 | g.argc = 2; |
| 434 | fossil_print("Full repository verification follows:\n"); |
| 435 | test_integrity(); |
| 436 | } |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | /* |
| 441 | ** WEBPAGE: urllist |
| 442 |
+69
-231
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -88,14 +88,10 @@ | ||
| 88 | 88 | |
| 89 | 89 | /* |
| 90 | 90 | ** Flags for various javascript files needed prior to </body> |
| 91 | 91 | */ |
| 92 | 92 | static int needHrefJs = 0; /* href.js */ |
| 93 | -static int needSortJs = 0; /* sorttable.js */ | |
| 94 | -static int needGraphJs = 0; /* graph.js */ | |
| 95 | -static int needCopyBtnJs = 0; /* copybtn.js */ | |
| 96 | -static int needAccordionJs = 0; /* accordion.js */ | |
| 97 | 93 | |
| 98 | 94 | /* |
| 99 | 95 | ** Extra JS added to the end of the file. |
| 100 | 96 | */ |
| 101 | 97 | static Blob blobOnLoad = BLOB_INITIALIZER; |
| @@ -484,11 +480,11 @@ | ||
| 484 | 480 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 485 | 481 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 486 | 482 | } |
| 487 | 483 | } |
| 488 | 484 | free(zText); |
| 489 | - style_copybutton_control(); | |
| 485 | + builtin_request_js("copybtn.js"); | |
| 490 | 486 | return zResult; |
| 491 | 487 | } |
| 492 | 488 | |
| 493 | 489 | /* |
| 494 | 490 | ** Return a random nonce that is stored in static space. For a particular |
| @@ -694,66 +690,17 @@ | ||
| 694 | 690 | |
| 695 | 691 | /* |
| 696 | 692 | ** Indicate that the table-sorting javascript is needed. |
| 697 | 693 | */ |
| 698 | 694 | void style_table_sorter(void){ |
| 699 | - needSortJs = 1; | |
| 700 | -} | |
| 701 | - | |
| 702 | -/* | |
| 703 | -** Indicate that the accordion javascript is needed. | |
| 704 | -*/ | |
| 705 | -void style_accordion(void){ | |
| 706 | - needAccordionJs = 1; | |
| 707 | -} | |
| 708 | - | |
| 709 | -/* | |
| 710 | -** Indicate that the timeline graph javascript is needed. | |
| 711 | -*/ | |
| 712 | -void style_graph_generator(void){ | |
| 713 | - needGraphJs = 1; | |
| 714 | -} | |
| 715 | - | |
| 716 | -/* | |
| 717 | -** Indicate that the copy button javascript is needed. | |
| 718 | -*/ | |
| 719 | -void style_copybutton_control(void){ | |
| 720 | - needCopyBtnJs = 1; | |
| 721 | -} | |
| 722 | - | |
| 723 | -/* | |
| 724 | -** Generate code to load a single javascript file | |
| 725 | -*/ | |
| 726 | -void style_load_one_js_file(const char *zFile){ | |
| 727 | - @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script> | |
| 728 | -} | |
| 729 | - | |
| 730 | -/* | |
| 731 | -** All extra JS files to load. | |
| 732 | -*/ | |
| 733 | -static const char *azJsToLoad[4]; | |
| 734 | -static int nJsToLoad = 0; | |
| 735 | - | |
| 736 | -/* | |
| 737 | -** Register a new JS file to load at the end of the document. | |
| 738 | -*/ | |
| 739 | -void style_load_js(const char *zName){ | |
| 740 | - int i; | |
| 741 | - for(i=0; i<nJsToLoad; i++){ | |
| 742 | - if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return; | |
| 743 | - } | |
| 744 | - if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){ | |
| 745 | - fossil_panic("too many JS files"); | |
| 746 | - } | |
| 747 | - azJsToLoad[nJsToLoad++] = zName; | |
| 695 | + builtin_request_js("sorttable.js"); | |
| 748 | 696 | } |
| 749 | 697 | |
| 750 | 698 | /* |
| 751 | 699 | ** Generate code to load all required javascript files. |
| 752 | 700 | */ |
| 753 | 701 | static void style_load_all_js_files(void){ |
| 754 | - int i; | |
| 755 | 702 | if( needHrefJs ){ |
| 756 | 703 | int nDelay = db_get_int("auto-hyperlink-delay",0); |
| 757 | 704 | int bMouseover = db_get_boolean("auto-hyperlink-mouseover",0); |
| 758 | 705 | @ <script id='href-data' type='application/json'>\ |
| 759 | 706 | @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> |
| @@ -762,43 +709,20 @@ | ||
| 762 | 709 | @ function debugMsg(msg){ |
| 763 | 710 | @ var n = document.getElementById("debugMsg"); |
| 764 | 711 | @ if(n){n.textContent=msg;} |
| 765 | 712 | @ } |
| 766 | 713 | if( needHrefJs ){ |
| 714 | + @ /* href.js */ | |
| 767 | 715 | cgi_append_content(builtin_text("href.js"),-1); |
| 768 | 716 | } |
| 769 | - if( needSortJs ){ | |
| 770 | - cgi_append_content(builtin_text("sorttable.js"),-1); | |
| 771 | - } | |
| 772 | - if( needGraphJs ){ | |
| 773 | - cgi_append_content(builtin_text("graph.js"),-1); | |
| 774 | - } | |
| 775 | - if( needCopyBtnJs ){ | |
| 776 | - cgi_append_content(builtin_text("copybtn.js"),-1); | |
| 777 | - } | |
| 778 | - if( needAccordionJs ){ | |
| 779 | - cgi_append_content(builtin_text("accordion.js"),-1); | |
| 780 | - } | |
| 781 | - for(i=0; i<nJsToLoad; i++){ | |
| 782 | - cgi_append_content(builtin_text(azJsToLoad[i]),-1); | |
| 783 | - } | |
| 784 | 717 | if( blob_size(&blobOnLoad)>0 ){ |
| 785 | 718 | @ window.onload = function(){ |
| 786 | 719 | cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad)); |
| 787 | 720 | cgi_append_content("\n}\n", -1); |
| 788 | 721 | } |
| 789 | 722 | @ </script> |
| 790 | -} | |
| 791 | - | |
| 792 | -/* | |
| 793 | -** Extra JS to run after all content is loaded. | |
| 794 | -*/ | |
| 795 | -void style_js_onload(const char *zFormat, ...){ | |
| 796 | - va_list ap; | |
| 797 | - va_start(ap, zFormat); | |
| 798 | - blob_vappendf(&blobOnLoad, zFormat, ap); | |
| 799 | - va_end(ap); | |
| 723 | + builtin_fulfill_js_requests(); | |
| 800 | 724 | } |
| 801 | 725 | |
| 802 | 726 | /* |
| 803 | 727 | ** Draw the footer at the bottom of the page. |
| 804 | 728 | */ |
| @@ -916,11 +840,11 @@ | ||
| 916 | 840 | @ </div> |
| 917 | 841 | if( nSubmenuCtrl ){ |
| 918 | 842 | cgi_query_parameters_to_hidden(); |
| 919 | 843 | cgi_tag_query_parameter(0); |
| 920 | 844 | @ </form> |
| 921 | - style_load_one_js_file("menu.js"); | |
| 845 | + builtin_request_js("menu.js"); | |
| 922 | 846 | } |
| 923 | 847 | } |
| 924 | 848 | |
| 925 | 849 | zAd = style_adunit_text(&mAdFlags); |
| 926 | 850 | if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ |
| @@ -1102,10 +1026,11 @@ | ||
| 1102 | 1026 | Blob css = empty_blob; |
| 1103 | 1027 | int i; |
| 1104 | 1028 | const char * zDefaults; |
| 1105 | 1029 | |
| 1106 | 1030 | cgi_set_content_type("text/css"); |
| 1031 | + etag_check(0, 0); | |
| 1107 | 1032 | /* Emit all default rules... */ |
| 1108 | 1033 | zDefaults = (const char*)builtin_file("default.css", &i); |
| 1109 | 1034 | blob_append(&css, zDefaults, i); |
| 1110 | 1035 | /* Page-specific CSS, if any... */ |
| 1111 | 1036 | page_style_css_append_page_style(&css); |
| @@ -1127,46 +1052,10 @@ | ||
| 1127 | 1052 | |
| 1128 | 1053 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1129 | 1054 | g.isConst = 1; |
| 1130 | 1055 | } |
| 1131 | 1056 | |
| 1132 | -/* | |
| 1133 | -** WEBPAGE: builtin | |
| 1134 | -** URL: builtin/FILENAME | |
| 1135 | -** | |
| 1136 | -** Return the built-in text given by FILENAME. This is used internally | |
| 1137 | -** by many Fossil web pages to load built-in javascript files. | |
| 1138 | -** | |
| 1139 | -** If the id= query parameter is present, then Fossil assumes that the | |
| 1140 | -** result is immutable and sets a very large cache retention time (1 year). | |
| 1141 | -*/ | |
| 1142 | -void page_builtin_text(void){ | |
| 1143 | - Blob out; | |
| 1144 | - const char *zName = P("name"); | |
| 1145 | - const char *zTxt = 0; | |
| 1146 | - const char *zId = P("id"); | |
| 1147 | - int nId; | |
| 1148 | - if( zName ) zTxt = builtin_text(zName); | |
| 1149 | - if( zTxt==0 ){ | |
| 1150 | - cgi_set_status(404, "Not Found"); | |
| 1151 | - @ File "%h(zName)" not found | |
| 1152 | - return; | |
| 1153 | - } | |
| 1154 | - if( sqlite3_strglob("*.js", zName)==0 ){ | |
| 1155 | - cgi_set_content_type("application/javascript"); | |
| 1156 | - }else{ | |
| 1157 | - cgi_set_content_type("text/plain"); | |
| 1158 | - } | |
| 1159 | - if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,MANIFEST_UUID,nId)==0 ){ | |
| 1160 | - g.isConst = 1; | |
| 1161 | - }else{ | |
| 1162 | - etag_check(0,0); | |
| 1163 | - } | |
| 1164 | - blob_init(&out, zTxt, -1); | |
| 1165 | - cgi_set_content(&out); | |
| 1166 | -} | |
| 1167 | - | |
| 1168 | 1057 | /* |
| 1169 | 1058 | ** All possible capabilities |
| 1170 | 1059 | */ |
| 1171 | 1060 | static const char allCap[] = |
| 1172 | 1061 | "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; |
| @@ -1535,35 +1424,51 @@ | ||
| 1535 | 1424 | ** bootstrap the window.fossil object, using the built-in file |
| 1536 | 1425 | ** fossil.bootstrap.js (not to be confused with bootstrap.js). |
| 1537 | 1426 | ** |
| 1538 | 1427 | ** Subsequent calls are no-ops. |
| 1539 | 1428 | ** |
| 1540 | -** If passed a true value, it emits the contents directly to the page | |
| 1541 | -** output, else it emits a script tag with a src=builtin/... to load | |
| 1542 | -** the script. It always outputs a small pre-bootstrap element in its | |
| 1543 | -** own script tag to initialize parts which need C-runtime-level | |
| 1544 | -** information, before loading the main fossil.bootstrap.js either | |
| 1545 | -** inline or via a <script src=...>, as specified by the first | |
| 1546 | -** argument. | |
| 1429 | +** It emits 2 parts: | |
| 1430 | +** | |
| 1431 | +** 1) window.fossil core object, some of which depends on C-level | |
| 1432 | +** runtime data. That part of the script is always emitted inline. If | |
| 1433 | +** addScriptTag is true then it is wrapped in its own SCRIPT tag, else | |
| 1434 | +** it is assumed that the caller already opened a tag. | |
| 1435 | +** | |
| 1436 | +** 2) Emits the static fossil.bootstrap.js using builtin_request_js(). | |
| 1547 | 1437 | */ |
| 1548 | -void style_emit_script_fossil_bootstrap(int asInline){ | |
| 1438 | +void style_emit_script_fossil_bootstrap(int addScriptTag){ | |
| 1549 | 1439 | static int once = 0; |
| 1550 | 1440 | if(0==once++){ |
| 1551 | 1441 | /* Set up the generic/app-agnostic parts of window.fossil |
| 1552 | 1442 | ** which require C-level state... */ |
| 1553 | - style_emit_script_tag(0,0); | |
| 1554 | - CX("(function(){\n" | |
| 1555 | - "if(!window.fossil) window.fossil={};\n" | |
| 1443 | + if(addScriptTag!=0){ | |
| 1444 | + style_emit_script_tag(0,0); | |
| 1445 | + } | |
| 1446 | + CX("(function(){\n"); | |
| 1447 | + CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla: | |
| 1448 | + https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill | |
| 1449 | + */ | |
| 1450 | + "if(window.NodeList && !NodeList.prototype.forEach){" | |
| 1451 | + "NodeList.prototype.forEach = Array.prototype.forEach;" | |
| 1452 | + "}\n"); | |
| 1453 | + CX("if(!window.fossil) window.fossil={};\n" | |
| 1556 | 1454 | "window.fossil.version = %!j;\n" |
| 1557 | 1455 | /* fossil.rootPath is the top-most CGI/server path, |
| 1558 | 1456 | ** including a trailing slash. */ |
| 1559 | 1457 | "window.fossil.rootPath = %!j+'/';\n", |
| 1560 | 1458 | get_version(), g.zTop); |
| 1561 | 1459 | /* fossil.config = {...various config-level options...} */ |
| 1562 | - CX("window.fossil.config = {" | |
| 1563 | - "hashDigits: %d, hashDigitsUrl: %d" | |
| 1564 | - "};\n", hash_digits(0), hash_digits(1)); | |
| 1460 | + CX("window.fossil.config = {"); | |
| 1461 | + CX("/* Length of UUID hashes for display purposes. */"); | |
| 1462 | + CX("hashDigits: %d, hashDigitsUrl: %d,\n", | |
| 1463 | + hash_digits(0), hash_digits(1)); | |
| 1464 | + CX("editStateMarkers: {" | |
| 1465 | + "/*Symbolic markers to denote certain edit states.*/" | |
| 1466 | + "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n"); | |
| 1467 | + CX("confirmerButtonTicks: 3 " | |
| 1468 | + "/*default fossil.confirmer tick count.*/\n"); | |
| 1469 | + CX("};\n"/* fossil.config */); | |
| 1565 | 1470 | #if 0 |
| 1566 | 1471 | /* Is it safe to emit the CSRF token here? Some pages add it |
| 1567 | 1472 | ** as a hidden form field. */ |
| 1568 | 1473 | if(g.zCsrfToken[0]!=0){ |
| 1569 | 1474 | CX("window.fossil.csrfToken = %!j;\n", |
| @@ -1577,19 +1482,16 @@ | ||
| 1577 | 1482 | */ |
| 1578 | 1483 | CX("window.fossil.page = {" |
| 1579 | 1484 | "name:\"%T\"" |
| 1580 | 1485 | "};\n", g.zPath); |
| 1581 | 1486 | CX("})();\n"); |
| 1582 | - /* The remaining fossil object bootstrap code is not dependent on | |
| 1583 | - ** C-runtime state... */ | |
| 1584 | - if(asInline){ | |
| 1585 | - CX("%s\n", builtin_text("fossil.bootstrap.js")); | |
| 1586 | - } | |
| 1587 | - style_emit_script_tag(1,0); | |
| 1588 | - if(asInline==0){ | |
| 1589 | - style_emit_script_builtin(0, "fossil.bootstrap.js"); | |
| 1590 | - } | |
| 1487 | + if(addScriptTag!=0){ | |
| 1488 | + style_emit_script_tag(1,0); | |
| 1489 | + } | |
| 1490 | + /* The remaining window.fossil bootstrap code is not dependent on | |
| 1491 | + ** C-runtime state... */ | |
| 1492 | + builtin_request_js("fossil.bootstrap.js"); | |
| 1591 | 1493 | } |
| 1592 | 1494 | } |
| 1593 | 1495 | |
| 1594 | 1496 | /* |
| 1595 | 1497 | ** If passed 0 as its first argument, it emits a script opener tag |
| @@ -1620,97 +1522,33 @@ | ||
| 1620 | 1522 | CX("</script>\n"); |
| 1621 | 1523 | } |
| 1622 | 1524 | } |
| 1623 | 1525 | |
| 1624 | 1526 | /* |
| 1625 | -** Emits a script tag which uses content from a builtin script file. | |
| 1626 | -** | |
| 1627 | -** If asInline is true, it is emitted directly as an opening tag, the | |
| 1628 | -** content of the zName builtin file, and a closing tag. | |
| 1629 | -** | |
| 1630 | -** If it is false, a script tag loading it via | |
| 1631 | -** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a | |
| 1632 | -** build-time-dependent cache-buster value. | |
| 1633 | -*/ | |
| 1634 | -void style_emit_script_builtin(int asInline, char const * zName){ | |
| 1635 | - if(asInline){ | |
| 1636 | - style_emit_script_tag(0,0); | |
| 1637 | - CX("%s", builtin_text(zName)); | |
| 1638 | - style_emit_script_tag(1,0); | |
| 1639 | - }else{ | |
| 1640 | - char * zFullName = mprintf("builtin/%s",zName); | |
| 1641 | - const char * zHash = fossil_exe_id(); | |
| 1642 | - CX("<script src='%R/%T?cache=%.8s'></script>\n", | |
| 1643 | - zFullName, zHash); | |
| 1644 | - fossil_free(zFullName); | |
| 1645 | - } | |
| 1646 | -} | |
| 1647 | - | |
| 1648 | -/* | |
| 1649 | -** The first time this is called it emits the JS code from the | |
| 1650 | -** built-in file fossil.fossil.js. Subsequent calls are no-ops. | |
| 1651 | -** | |
| 1652 | -** If passed a true value, it emits the contents directly | |
| 1653 | -** to the page output, else it emits a script tag with a | |
| 1654 | -** src=builtin/... to load the script. | |
| 1655 | -** | |
| 1656 | -** Note that this code relies on that loaded via | |
| 1657 | -** style_emit_script_fossil_bootstrap() but it does not call that | |
| 1658 | -** routine. | |
| 1659 | -*/ | |
| 1660 | -void style_emit_script_fetch(int asInline){ | |
| 1661 | - static int once = 0; | |
| 1662 | - if(0==once++){ | |
| 1663 | - style_emit_script_builtin(asInline, "fossil.fetch.js"); | |
| 1664 | - } | |
| 1665 | -} | |
| 1666 | - | |
| 1667 | -/* | |
| 1668 | -** The first time this is called it emits the JS code from the | |
| 1669 | -** built-in file fossil.dom.js. Subsequent calls are no-ops. | |
| 1670 | -** | |
| 1671 | -** If passed a true value, it emits the contents directly | |
| 1672 | -** to the page output, else it emits a script tag with a | |
| 1673 | -** src=builtin/... to load the script. | |
| 1674 | -** | |
| 1675 | -** Note that this code relies on that loaded via | |
| 1676 | -** style_emit_script_fossil_bootstrap(), but it does not call that | |
| 1677 | -** routine. | |
| 1678 | -*/ | |
| 1679 | -void style_emit_script_dom(int asInline){ | |
| 1680 | - static int once = 0; | |
| 1681 | - if(0==once++){ | |
| 1682 | - style_emit_script_builtin(asInline, "fossil.dom.js"); | |
| 1683 | - } | |
| 1684 | -} | |
| 1685 | - | |
| 1686 | -/* | |
| 1687 | -** The first time this is called, it calls style_emit_script_dom(), | |
| 1688 | -** passing it the given asInline value, and emits the JS code from the | |
| 1689 | -** built-in file fossil.tabs.js. Subsequent calls are no-ops. | |
| 1690 | -** | |
| 1691 | -** If passed a true value, it emits the contents directly | |
| 1692 | -** to the page output, else it emits a script tag with a | |
| 1693 | -** src=builtin/... to load the script. | |
| 1694 | -*/ | |
| 1695 | -void style_emit_script_tabs(int asInline){ | |
| 1696 | - static int once = 0; | |
| 1697 | - if(0==once++){ | |
| 1698 | - style_emit_script_dom(asInline); | |
| 1699 | - style_emit_script_builtin(asInline, "fossil.tabs.js"); | |
| 1700 | - } | |
| 1701 | -} | |
| 1702 | - | |
| 1703 | -/* | |
| 1704 | -** The first time this is called it emits the JS code from the | |
| 1705 | -** built-in file fossil.confirmer.js. Subsequent calls are no-ops. | |
| 1706 | -** | |
| 1707 | -** If passed a true value, it emits the contents directly | |
| 1708 | -** to the page output, else it emits a script tag with a | |
| 1709 | -** src=builtin/... to load the script. | |
| 1710 | -*/ | |
| 1711 | -void style_emit_script_confirmer(int asInline){ | |
| 1712 | - static int once = 0; | |
| 1713 | - if(0==once++){ | |
| 1714 | - style_emit_script_builtin(asInline, "fossil.confirmer.js"); | |
| 1715 | - } | |
| 1527 | +** Convenience wrapper which calls builtin_request_js() for a series | |
| 1528 | +** of builtin scripts named fossil.NAME.js. The first time it is | |
| 1529 | +** called, it also calls style_emit_script_fossil_bootstrap() to | |
| 1530 | +** initialize the window.fossil JS API. The first argument is a | |
| 1531 | +** no-meaning dummy required by the va_start() interface. All | |
| 1532 | +** subsequent arguments must be strings of the NAME part of | |
| 1533 | +** fossil.NAME.js, followed by a NULL argument to terminate the list. | |
| 1534 | +** | |
| 1535 | +** e.g. pass it (0, "fetch", "dom", "tabs", 0) to load those 3 | |
| 1536 | +** APIs. Do not forget the trailing 0! | |
| 1537 | +*/ | |
| 1538 | +void style_emit_fossil_js_apis( int dummy, ... ) { | |
| 1539 | + static int once = 0; | |
| 1540 | + const char *zArg; | |
| 1541 | + char * zName; | |
| 1542 | + va_list vargs; | |
| 1543 | + | |
| 1544 | + if(0==once++){ | |
| 1545 | + style_emit_script_fossil_bootstrap(1); | |
| 1546 | + } | |
| 1547 | + va_start(vargs,dummy); | |
| 1548 | + while( (zArg = va_arg (vargs, const char *))!=0 ){ | |
| 1549 | + zName = mprintf("fossil.%s.js", zArg); | |
| 1550 | + builtin_request_js(zName); | |
| 1551 | + fossil_free(zName); | |
| 1552 | + } | |
| 1553 | + va_end(vargs); | |
| 1716 | 1554 | } |
| 1717 | 1555 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -88,14 +88,10 @@ | |
| 88 | |
| 89 | /* |
| 90 | ** Flags for various javascript files needed prior to </body> |
| 91 | */ |
| 92 | static int needHrefJs = 0; /* href.js */ |
| 93 | static int needSortJs = 0; /* sorttable.js */ |
| 94 | static int needGraphJs = 0; /* graph.js */ |
| 95 | static int needCopyBtnJs = 0; /* copybtn.js */ |
| 96 | static int needAccordionJs = 0; /* accordion.js */ |
| 97 | |
| 98 | /* |
| 99 | ** Extra JS added to the end of the file. |
| 100 | */ |
| 101 | static Blob blobOnLoad = BLOB_INITIALIZER; |
| @@ -484,11 +480,11 @@ | |
| 484 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 485 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 486 | } |
| 487 | } |
| 488 | free(zText); |
| 489 | style_copybutton_control(); |
| 490 | return zResult; |
| 491 | } |
| 492 | |
| 493 | /* |
| 494 | ** Return a random nonce that is stored in static space. For a particular |
| @@ -694,66 +690,17 @@ | |
| 694 | |
| 695 | /* |
| 696 | ** Indicate that the table-sorting javascript is needed. |
| 697 | */ |
| 698 | void style_table_sorter(void){ |
| 699 | needSortJs = 1; |
| 700 | } |
| 701 | |
| 702 | /* |
| 703 | ** Indicate that the accordion javascript is needed. |
| 704 | */ |
| 705 | void style_accordion(void){ |
| 706 | needAccordionJs = 1; |
| 707 | } |
| 708 | |
| 709 | /* |
| 710 | ** Indicate that the timeline graph javascript is needed. |
| 711 | */ |
| 712 | void style_graph_generator(void){ |
| 713 | needGraphJs = 1; |
| 714 | } |
| 715 | |
| 716 | /* |
| 717 | ** Indicate that the copy button javascript is needed. |
| 718 | */ |
| 719 | void style_copybutton_control(void){ |
| 720 | needCopyBtnJs = 1; |
| 721 | } |
| 722 | |
| 723 | /* |
| 724 | ** Generate code to load a single javascript file |
| 725 | */ |
| 726 | void style_load_one_js_file(const char *zFile){ |
| 727 | @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script> |
| 728 | } |
| 729 | |
| 730 | /* |
| 731 | ** All extra JS files to load. |
| 732 | */ |
| 733 | static const char *azJsToLoad[4]; |
| 734 | static int nJsToLoad = 0; |
| 735 | |
| 736 | /* |
| 737 | ** Register a new JS file to load at the end of the document. |
| 738 | */ |
| 739 | void style_load_js(const char *zName){ |
| 740 | int i; |
| 741 | for(i=0; i<nJsToLoad; i++){ |
| 742 | if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return; |
| 743 | } |
| 744 | if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){ |
| 745 | fossil_panic("too many JS files"); |
| 746 | } |
| 747 | azJsToLoad[nJsToLoad++] = zName; |
| 748 | } |
| 749 | |
| 750 | /* |
| 751 | ** Generate code to load all required javascript files. |
| 752 | */ |
| 753 | static void style_load_all_js_files(void){ |
| 754 | int i; |
| 755 | if( needHrefJs ){ |
| 756 | int nDelay = db_get_int("auto-hyperlink-delay",0); |
| 757 | int bMouseover = db_get_boolean("auto-hyperlink-mouseover",0); |
| 758 | @ <script id='href-data' type='application/json'>\ |
| 759 | @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> |
| @@ -762,43 +709,20 @@ | |
| 762 | @ function debugMsg(msg){ |
| 763 | @ var n = document.getElementById("debugMsg"); |
| 764 | @ if(n){n.textContent=msg;} |
| 765 | @ } |
| 766 | if( needHrefJs ){ |
| 767 | cgi_append_content(builtin_text("href.js"),-1); |
| 768 | } |
| 769 | if( needSortJs ){ |
| 770 | cgi_append_content(builtin_text("sorttable.js"),-1); |
| 771 | } |
| 772 | if( needGraphJs ){ |
| 773 | cgi_append_content(builtin_text("graph.js"),-1); |
| 774 | } |
| 775 | if( needCopyBtnJs ){ |
| 776 | cgi_append_content(builtin_text("copybtn.js"),-1); |
| 777 | } |
| 778 | if( needAccordionJs ){ |
| 779 | cgi_append_content(builtin_text("accordion.js"),-1); |
| 780 | } |
| 781 | for(i=0; i<nJsToLoad; i++){ |
| 782 | cgi_append_content(builtin_text(azJsToLoad[i]),-1); |
| 783 | } |
| 784 | if( blob_size(&blobOnLoad)>0 ){ |
| 785 | @ window.onload = function(){ |
| 786 | cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad)); |
| 787 | cgi_append_content("\n}\n", -1); |
| 788 | } |
| 789 | @ </script> |
| 790 | } |
| 791 | |
| 792 | /* |
| 793 | ** Extra JS to run after all content is loaded. |
| 794 | */ |
| 795 | void style_js_onload(const char *zFormat, ...){ |
| 796 | va_list ap; |
| 797 | va_start(ap, zFormat); |
| 798 | blob_vappendf(&blobOnLoad, zFormat, ap); |
| 799 | va_end(ap); |
| 800 | } |
| 801 | |
| 802 | /* |
| 803 | ** Draw the footer at the bottom of the page. |
| 804 | */ |
| @@ -916,11 +840,11 @@ | |
| 916 | @ </div> |
| 917 | if( nSubmenuCtrl ){ |
| 918 | cgi_query_parameters_to_hidden(); |
| 919 | cgi_tag_query_parameter(0); |
| 920 | @ </form> |
| 921 | style_load_one_js_file("menu.js"); |
| 922 | } |
| 923 | } |
| 924 | |
| 925 | zAd = style_adunit_text(&mAdFlags); |
| 926 | if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ |
| @@ -1102,10 +1026,11 @@ | |
| 1102 | Blob css = empty_blob; |
| 1103 | int i; |
| 1104 | const char * zDefaults; |
| 1105 | |
| 1106 | cgi_set_content_type("text/css"); |
| 1107 | /* Emit all default rules... */ |
| 1108 | zDefaults = (const char*)builtin_file("default.css", &i); |
| 1109 | blob_append(&css, zDefaults, i); |
| 1110 | /* Page-specific CSS, if any... */ |
| 1111 | page_style_css_append_page_style(&css); |
| @@ -1127,46 +1052,10 @@ | |
| 1127 | |
| 1128 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1129 | g.isConst = 1; |
| 1130 | } |
| 1131 | |
| 1132 | /* |
| 1133 | ** WEBPAGE: builtin |
| 1134 | ** URL: builtin/FILENAME |
| 1135 | ** |
| 1136 | ** Return the built-in text given by FILENAME. This is used internally |
| 1137 | ** by many Fossil web pages to load built-in javascript files. |
| 1138 | ** |
| 1139 | ** If the id= query parameter is present, then Fossil assumes that the |
| 1140 | ** result is immutable and sets a very large cache retention time (1 year). |
| 1141 | */ |
| 1142 | void page_builtin_text(void){ |
| 1143 | Blob out; |
| 1144 | const char *zName = P("name"); |
| 1145 | const char *zTxt = 0; |
| 1146 | const char *zId = P("id"); |
| 1147 | int nId; |
| 1148 | if( zName ) zTxt = builtin_text(zName); |
| 1149 | if( zTxt==0 ){ |
| 1150 | cgi_set_status(404, "Not Found"); |
| 1151 | @ File "%h(zName)" not found |
| 1152 | return; |
| 1153 | } |
| 1154 | if( sqlite3_strglob("*.js", zName)==0 ){ |
| 1155 | cgi_set_content_type("application/javascript"); |
| 1156 | }else{ |
| 1157 | cgi_set_content_type("text/plain"); |
| 1158 | } |
| 1159 | if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,MANIFEST_UUID,nId)==0 ){ |
| 1160 | g.isConst = 1; |
| 1161 | }else{ |
| 1162 | etag_check(0,0); |
| 1163 | } |
| 1164 | blob_init(&out, zTxt, -1); |
| 1165 | cgi_set_content(&out); |
| 1166 | } |
| 1167 | |
| 1168 | /* |
| 1169 | ** All possible capabilities |
| 1170 | */ |
| 1171 | static const char allCap[] = |
| 1172 | "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; |
| @@ -1535,35 +1424,51 @@ | |
| 1535 | ** bootstrap the window.fossil object, using the built-in file |
| 1536 | ** fossil.bootstrap.js (not to be confused with bootstrap.js). |
| 1537 | ** |
| 1538 | ** Subsequent calls are no-ops. |
| 1539 | ** |
| 1540 | ** If passed a true value, it emits the contents directly to the page |
| 1541 | ** output, else it emits a script tag with a src=builtin/... to load |
| 1542 | ** the script. It always outputs a small pre-bootstrap element in its |
| 1543 | ** own script tag to initialize parts which need C-runtime-level |
| 1544 | ** information, before loading the main fossil.bootstrap.js either |
| 1545 | ** inline or via a <script src=...>, as specified by the first |
| 1546 | ** argument. |
| 1547 | */ |
| 1548 | void style_emit_script_fossil_bootstrap(int asInline){ |
| 1549 | static int once = 0; |
| 1550 | if(0==once++){ |
| 1551 | /* Set up the generic/app-agnostic parts of window.fossil |
| 1552 | ** which require C-level state... */ |
| 1553 | style_emit_script_tag(0,0); |
| 1554 | CX("(function(){\n" |
| 1555 | "if(!window.fossil) window.fossil={};\n" |
| 1556 | "window.fossil.version = %!j;\n" |
| 1557 | /* fossil.rootPath is the top-most CGI/server path, |
| 1558 | ** including a trailing slash. */ |
| 1559 | "window.fossil.rootPath = %!j+'/';\n", |
| 1560 | get_version(), g.zTop); |
| 1561 | /* fossil.config = {...various config-level options...} */ |
| 1562 | CX("window.fossil.config = {" |
| 1563 | "hashDigits: %d, hashDigitsUrl: %d" |
| 1564 | "};\n", hash_digits(0), hash_digits(1)); |
| 1565 | #if 0 |
| 1566 | /* Is it safe to emit the CSRF token here? Some pages add it |
| 1567 | ** as a hidden form field. */ |
| 1568 | if(g.zCsrfToken[0]!=0){ |
| 1569 | CX("window.fossil.csrfToken = %!j;\n", |
| @@ -1577,19 +1482,16 @@ | |
| 1577 | */ |
| 1578 | CX("window.fossil.page = {" |
| 1579 | "name:\"%T\"" |
| 1580 | "};\n", g.zPath); |
| 1581 | CX("})();\n"); |
| 1582 | /* The remaining fossil object bootstrap code is not dependent on |
| 1583 | ** C-runtime state... */ |
| 1584 | if(asInline){ |
| 1585 | CX("%s\n", builtin_text("fossil.bootstrap.js")); |
| 1586 | } |
| 1587 | style_emit_script_tag(1,0); |
| 1588 | if(asInline==0){ |
| 1589 | style_emit_script_builtin(0, "fossil.bootstrap.js"); |
| 1590 | } |
| 1591 | } |
| 1592 | } |
| 1593 | |
| 1594 | /* |
| 1595 | ** If passed 0 as its first argument, it emits a script opener tag |
| @@ -1620,97 +1522,33 @@ | |
| 1620 | CX("</script>\n"); |
| 1621 | } |
| 1622 | } |
| 1623 | |
| 1624 | /* |
| 1625 | ** Emits a script tag which uses content from a builtin script file. |
| 1626 | ** |
| 1627 | ** If asInline is true, it is emitted directly as an opening tag, the |
| 1628 | ** content of the zName builtin file, and a closing tag. |
| 1629 | ** |
| 1630 | ** If it is false, a script tag loading it via |
| 1631 | ** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a |
| 1632 | ** build-time-dependent cache-buster value. |
| 1633 | */ |
| 1634 | void style_emit_script_builtin(int asInline, char const * zName){ |
| 1635 | if(asInline){ |
| 1636 | style_emit_script_tag(0,0); |
| 1637 | CX("%s", builtin_text(zName)); |
| 1638 | style_emit_script_tag(1,0); |
| 1639 | }else{ |
| 1640 | char * zFullName = mprintf("builtin/%s",zName); |
| 1641 | const char * zHash = fossil_exe_id(); |
| 1642 | CX("<script src='%R/%T?cache=%.8s'></script>\n", |
| 1643 | zFullName, zHash); |
| 1644 | fossil_free(zFullName); |
| 1645 | } |
| 1646 | } |
| 1647 | |
| 1648 | /* |
| 1649 | ** The first time this is called it emits the JS code from the |
| 1650 | ** built-in file fossil.fossil.js. Subsequent calls are no-ops. |
| 1651 | ** |
| 1652 | ** If passed a true value, it emits the contents directly |
| 1653 | ** to the page output, else it emits a script tag with a |
| 1654 | ** src=builtin/... to load the script. |
| 1655 | ** |
| 1656 | ** Note that this code relies on that loaded via |
| 1657 | ** style_emit_script_fossil_bootstrap() but it does not call that |
| 1658 | ** routine. |
| 1659 | */ |
| 1660 | void style_emit_script_fetch(int asInline){ |
| 1661 | static int once = 0; |
| 1662 | if(0==once++){ |
| 1663 | style_emit_script_builtin(asInline, "fossil.fetch.js"); |
| 1664 | } |
| 1665 | } |
| 1666 | |
| 1667 | /* |
| 1668 | ** The first time this is called it emits the JS code from the |
| 1669 | ** built-in file fossil.dom.js. Subsequent calls are no-ops. |
| 1670 | ** |
| 1671 | ** If passed a true value, it emits the contents directly |
| 1672 | ** to the page output, else it emits a script tag with a |
| 1673 | ** src=builtin/... to load the script. |
| 1674 | ** |
| 1675 | ** Note that this code relies on that loaded via |
| 1676 | ** style_emit_script_fossil_bootstrap(), but it does not call that |
| 1677 | ** routine. |
| 1678 | */ |
| 1679 | void style_emit_script_dom(int asInline){ |
| 1680 | static int once = 0; |
| 1681 | if(0==once++){ |
| 1682 | style_emit_script_builtin(asInline, "fossil.dom.js"); |
| 1683 | } |
| 1684 | } |
| 1685 | |
| 1686 | /* |
| 1687 | ** The first time this is called, it calls style_emit_script_dom(), |
| 1688 | ** passing it the given asInline value, and emits the JS code from the |
| 1689 | ** built-in file fossil.tabs.js. Subsequent calls are no-ops. |
| 1690 | ** |
| 1691 | ** If passed a true value, it emits the contents directly |
| 1692 | ** to the page output, else it emits a script tag with a |
| 1693 | ** src=builtin/... to load the script. |
| 1694 | */ |
| 1695 | void style_emit_script_tabs(int asInline){ |
| 1696 | static int once = 0; |
| 1697 | if(0==once++){ |
| 1698 | style_emit_script_dom(asInline); |
| 1699 | style_emit_script_builtin(asInline, "fossil.tabs.js"); |
| 1700 | } |
| 1701 | } |
| 1702 | |
| 1703 | /* |
| 1704 | ** The first time this is called it emits the JS code from the |
| 1705 | ** built-in file fossil.confirmer.js. Subsequent calls are no-ops. |
| 1706 | ** |
| 1707 | ** If passed a true value, it emits the contents directly |
| 1708 | ** to the page output, else it emits a script tag with a |
| 1709 | ** src=builtin/... to load the script. |
| 1710 | */ |
| 1711 | void style_emit_script_confirmer(int asInline){ |
| 1712 | static int once = 0; |
| 1713 | if(0==once++){ |
| 1714 | style_emit_script_builtin(asInline, "fossil.confirmer.js"); |
| 1715 | } |
| 1716 | } |
| 1717 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -88,14 +88,10 @@ | |
| 88 | |
| 89 | /* |
| 90 | ** Flags for various javascript files needed prior to </body> |
| 91 | */ |
| 92 | static int needHrefJs = 0; /* href.js */ |
| 93 | |
| 94 | /* |
| 95 | ** Extra JS added to the end of the file. |
| 96 | */ |
| 97 | static Blob blobOnLoad = BLOB_INITIALIZER; |
| @@ -484,11 +480,11 @@ | |
| 480 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 481 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| 482 | } |
| 483 | } |
| 484 | free(zText); |
| 485 | builtin_request_js("copybtn.js"); |
| 486 | return zResult; |
| 487 | } |
| 488 | |
| 489 | /* |
| 490 | ** Return a random nonce that is stored in static space. For a particular |
| @@ -694,66 +690,17 @@ | |
| 690 | |
| 691 | /* |
| 692 | ** Indicate that the table-sorting javascript is needed. |
| 693 | */ |
| 694 | void style_table_sorter(void){ |
| 695 | builtin_request_js("sorttable.js"); |
| 696 | } |
| 697 | |
| 698 | /* |
| 699 | ** Generate code to load all required javascript files. |
| 700 | */ |
| 701 | static void style_load_all_js_files(void){ |
| 702 | if( needHrefJs ){ |
| 703 | int nDelay = db_get_int("auto-hyperlink-delay",0); |
| 704 | int bMouseover = db_get_boolean("auto-hyperlink-mouseover",0); |
| 705 | @ <script id='href-data' type='application/json'>\ |
| 706 | @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> |
| @@ -762,43 +709,20 @@ | |
| 709 | @ function debugMsg(msg){ |
| 710 | @ var n = document.getElementById("debugMsg"); |
| 711 | @ if(n){n.textContent=msg;} |
| 712 | @ } |
| 713 | if( needHrefJs ){ |
| 714 | @ /* href.js */ |
| 715 | cgi_append_content(builtin_text("href.js"),-1); |
| 716 | } |
| 717 | if( blob_size(&blobOnLoad)>0 ){ |
| 718 | @ window.onload = function(){ |
| 719 | cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad)); |
| 720 | cgi_append_content("\n}\n", -1); |
| 721 | } |
| 722 | @ </script> |
| 723 | builtin_fulfill_js_requests(); |
| 724 | } |
| 725 | |
| 726 | /* |
| 727 | ** Draw the footer at the bottom of the page. |
| 728 | */ |
| @@ -916,11 +840,11 @@ | |
| 840 | @ </div> |
| 841 | if( nSubmenuCtrl ){ |
| 842 | cgi_query_parameters_to_hidden(); |
| 843 | cgi_tag_query_parameter(0); |
| 844 | @ </form> |
| 845 | builtin_request_js("menu.js"); |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | zAd = style_adunit_text(&mAdFlags); |
| 850 | if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ |
| @@ -1102,10 +1026,11 @@ | |
| 1026 | Blob css = empty_blob; |
| 1027 | int i; |
| 1028 | const char * zDefaults; |
| 1029 | |
| 1030 | cgi_set_content_type("text/css"); |
| 1031 | etag_check(0, 0); |
| 1032 | /* Emit all default rules... */ |
| 1033 | zDefaults = (const char*)builtin_file("default.css", &i); |
| 1034 | blob_append(&css, zDefaults, i); |
| 1035 | /* Page-specific CSS, if any... */ |
| 1036 | page_style_css_append_page_style(&css); |
| @@ -1127,46 +1052,10 @@ | |
| 1052 | |
| 1053 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1054 | g.isConst = 1; |
| 1055 | } |
| 1056 | |
| 1057 | /* |
| 1058 | ** All possible capabilities |
| 1059 | */ |
| 1060 | static const char allCap[] = |
| 1061 | "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; |
| @@ -1535,35 +1424,51 @@ | |
| 1424 | ** bootstrap the window.fossil object, using the built-in file |
| 1425 | ** fossil.bootstrap.js (not to be confused with bootstrap.js). |
| 1426 | ** |
| 1427 | ** Subsequent calls are no-ops. |
| 1428 | ** |
| 1429 | ** It emits 2 parts: |
| 1430 | ** |
| 1431 | ** 1) window.fossil core object, some of which depends on C-level |
| 1432 | ** runtime data. That part of the script is always emitted inline. If |
| 1433 | ** addScriptTag is true then it is wrapped in its own SCRIPT tag, else |
| 1434 | ** it is assumed that the caller already opened a tag. |
| 1435 | ** |
| 1436 | ** 2) Emits the static fossil.bootstrap.js using builtin_request_js(). |
| 1437 | */ |
| 1438 | void style_emit_script_fossil_bootstrap(int addScriptTag){ |
| 1439 | static int once = 0; |
| 1440 | if(0==once++){ |
| 1441 | /* Set up the generic/app-agnostic parts of window.fossil |
| 1442 | ** which require C-level state... */ |
| 1443 | if(addScriptTag!=0){ |
| 1444 | style_emit_script_tag(0,0); |
| 1445 | } |
| 1446 | CX("(function(){\n"); |
| 1447 | CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla: |
| 1448 | https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill |
| 1449 | */ |
| 1450 | "if(window.NodeList && !NodeList.prototype.forEach){" |
| 1451 | "NodeList.prototype.forEach = Array.prototype.forEach;" |
| 1452 | "}\n"); |
| 1453 | CX("if(!window.fossil) window.fossil={};\n" |
| 1454 | "window.fossil.version = %!j;\n" |
| 1455 | /* fossil.rootPath is the top-most CGI/server path, |
| 1456 | ** including a trailing slash. */ |
| 1457 | "window.fossil.rootPath = %!j+'/';\n", |
| 1458 | get_version(), g.zTop); |
| 1459 | /* fossil.config = {...various config-level options...} */ |
| 1460 | CX("window.fossil.config = {"); |
| 1461 | CX("/* Length of UUID hashes for display purposes. */"); |
| 1462 | CX("hashDigits: %d, hashDigitsUrl: %d,\n", |
| 1463 | hash_digits(0), hash_digits(1)); |
| 1464 | CX("editStateMarkers: {" |
| 1465 | "/*Symbolic markers to denote certain edit states.*/" |
| 1466 | "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n"); |
| 1467 | CX("confirmerButtonTicks: 3 " |
| 1468 | "/*default fossil.confirmer tick count.*/\n"); |
| 1469 | CX("};\n"/* fossil.config */); |
| 1470 | #if 0 |
| 1471 | /* Is it safe to emit the CSRF token here? Some pages add it |
| 1472 | ** as a hidden form field. */ |
| 1473 | if(g.zCsrfToken[0]!=0){ |
| 1474 | CX("window.fossil.csrfToken = %!j;\n", |
| @@ -1577,19 +1482,16 @@ | |
| 1482 | */ |
| 1483 | CX("window.fossil.page = {" |
| 1484 | "name:\"%T\"" |
| 1485 | "};\n", g.zPath); |
| 1486 | CX("})();\n"); |
| 1487 | if(addScriptTag!=0){ |
| 1488 | style_emit_script_tag(1,0); |
| 1489 | } |
| 1490 | /* The remaining window.fossil bootstrap code is not dependent on |
| 1491 | ** C-runtime state... */ |
| 1492 | builtin_request_js("fossil.bootstrap.js"); |
| 1493 | } |
| 1494 | } |
| 1495 | |
| 1496 | /* |
| 1497 | ** If passed 0 as its first argument, it emits a script opener tag |
| @@ -1620,97 +1522,33 @@ | |
| 1522 | CX("</script>\n"); |
| 1523 | } |
| 1524 | } |
| 1525 | |
| 1526 | /* |
| 1527 | ** Convenience wrapper which calls builtin_request_js() for a series |
| 1528 | ** of builtin scripts named fossil.NAME.js. The first time it is |
| 1529 | ** called, it also calls style_emit_script_fossil_bootstrap() to |
| 1530 | ** initialize the window.fossil JS API. The first argument is a |
| 1531 | ** no-meaning dummy required by the va_start() interface. All |
| 1532 | ** subsequent arguments must be strings of the NAME part of |
| 1533 | ** fossil.NAME.js, followed by a NULL argument to terminate the list. |
| 1534 | ** |
| 1535 | ** e.g. pass it (0, "fetch", "dom", "tabs", 0) to load those 3 |
| 1536 | ** APIs. Do not forget the trailing 0! |
| 1537 | */ |
| 1538 | void style_emit_fossil_js_apis( int dummy, ... ) { |
| 1539 | static int once = 0; |
| 1540 | const char *zArg; |
| 1541 | char * zName; |
| 1542 | va_list vargs; |
| 1543 | |
| 1544 | if(0==once++){ |
| 1545 | style_emit_script_fossil_bootstrap(1); |
| 1546 | } |
| 1547 | va_start(vargs,dummy); |
| 1548 | while( (zArg = va_arg (vargs, const char *))!=0 ){ |
| 1549 | zName = mprintf("fossil.%s.js", zArg); |
| 1550 | builtin_request_js(zName); |
| 1551 | fossil_free(zName); |
| 1552 | } |
| 1553 | va_end(vargs); |
| 1554 | } |
| 1555 |
+47
-190
| --- src/style.fileedit.css | ||
| +++ src/style.fileedit.css | ||
| @@ -15,15 +15,16 @@ | ||
| 15 | 15 | height: initial/*undo damage from some skins*/; |
| 16 | 16 | max-width: initial /* default.css pins it at 95% */; |
| 17 | 17 | } |
| 18 | 18 | body.fileedit textarea:focus, |
| 19 | 19 | body.fileedit input:focus{ |
| 20 | - /* The sudden appearance of a border (as in the Ardoise skin) | |
| 21 | - shifts the layout in unsightly ways */ | |
| 22 | - border: initial; | |
| 20 | + /* Depending on the skin, it might be useful to add one or both of | |
| 21 | + the following... */ | |
| 22 | + /*border-width: 1px;*/ | |
| 23 | + /*border: initial; */ | |
| 23 | 24 | } |
| 24 | -body.fileedit fieldset { | |
| 25 | +body.fileedit fieldset:not(.tab-wrapper) { | |
| 25 | 26 | margin: 0.5em 0 0.5em 0; |
| 26 | 27 | padding: 0.25em 0; |
| 27 | 28 | border-radius: 0.5em; |
| 28 | 29 | border-color: inherit; |
| 29 | 30 | border-width: 1px; |
| @@ -65,11 +66,11 @@ | ||
| 65 | 66 | body.fileedit div.fileedit-preview { |
| 66 | 67 | margin: 0; |
| 67 | 68 | padding: 0; |
| 68 | 69 | } |
| 69 | 70 | body.fileedit #fileedit-tabs { |
| 70 | - margin: 1em 0 0 0; | |
| 71 | + margin: 0.5em 0 0 0; | |
| 71 | 72 | } |
| 72 | 73 | body.fileedit #fileedit-tab-preview-wrapper { |
| 73 | 74 | overflow: auto; |
| 74 | 75 | } |
| 75 | 76 | body.fileedit #fileedit-tab-fileselect > h1 { |
| @@ -114,27 +115,28 @@ | ||
| 114 | 115 | } |
| 115 | 116 | body.fileedit #fileedit-file-selector { |
| 116 | 117 | display: flex; |
| 117 | 118 | flex-direction: column; |
| 118 | 119 | align-content: flex-start; |
| 119 | - border-color: inherit; | |
| 120 | - border-width: 1px; | |
| 121 | - border-style: inset; | |
| 122 | - border-radius: 0.5em; | |
| 123 | 120 | padding: 0 0.25em; |
| 124 | 121 | margin: 0; |
| 125 | 122 | min-height: 12em; |
| 126 | 123 | } |
| 127 | 124 | body.fileedit #fileedit-file-selector select { |
| 128 | 125 | margin: 0 0 0.5em 0; |
| 129 | 126 | height: initial; |
| 130 | 127 | font-family: monospace; |
| 128 | + border: initial; | |
| 131 | 129 | } |
| 132 | 130 | body.fileedit select:focus { |
| 133 | - border: none; | |
| 131 | + border: initial; | |
| 132 | +} | |
| 133 | +body.fileedit #fileedit-file-selector select option { | |
| 134 | + margin: 0 0 0.5em 0.55em; | |
| 134 | 135 | } |
| 135 | -body.fileedit option:focus { | |
| 136 | +body.fileedit select option, | |
| 137 | +body.fileedit select option:focus { | |
| 136 | 138 | border: none; |
| 137 | 139 | } |
| 138 | 140 | body.fileedit #fileedit-file-selector > div { |
| 139 | 141 | padding: 0; |
| 140 | 142 | margin: 0; |
| @@ -184,185 +186,40 @@ | ||
| 184 | 186 | } |
| 185 | 187 | body.fileedit .sbsdiffcols div.difftxtcol pre { |
| 186 | 188 | max-width: 44em; |
| 187 | 189 | } |
| 188 | 190 | |
| 189 | -/** | |
| 190 | - Styles for fossil.tabs.js. As of this writing, currently | |
| 191 | - only used by /fileedit, but it is anticipated that these | |
| 192 | - will eventually need to migrate to default_css.txt for use | |
| 193 | - in the wiki and/or forum pages when implementing tabbed | |
| 194 | - ajax-based previews. | |
| 195 | -*/ | |
| 196 | -.tab-container { | |
| 197 | - width: 100%; | |
| 198 | - display: flex; | |
| 199 | - flex-direction: column; | |
| 200 | - align-items: stretch; | |
| 201 | -} | |
| 202 | -.tab-container > #fossil-status-bar { | |
| 203 | - margin-top: 0; | |
| 204 | -} | |
| 205 | -.tab-container > .tabs { | |
| 206 | - padding: 0.25em; | |
| 207 | - margin: 0; | |
| 208 | - display: flex; | |
| 209 | - flex-direction: column; | |
| 210 | - border-width: 1px; | |
| 211 | - border-style: outset; | |
| 212 | - border-color: inherit; | |
| 213 | -} | |
| 214 | -.tab-container > .tabs > .tab-panel { | |
| 215 | - align-self: stretch; | |
| 216 | - flex: 10 1 auto; | |
| 217 | - display: block; | |
| 218 | -} | |
| 219 | -.tab-container > .tab-bar { | |
| 220 | - display: flex; | |
| 221 | - flex-direction: row; | |
| 222 | - flex: 1 10 auto; | |
| 223 | - align-self: stretch; | |
| 224 | - flex-wrap: wrap; | |
| 225 | -} | |
| 226 | -.tab-container > .tab-bar > .tab-button { | |
| 227 | - display: inline-block; | |
| 228 | - border-radius: 0.5em 0.5em 0 0; | |
| 229 | - margin: 0 0.1em; | |
| 230 | - padding: 0.25em 0.75em; | |
| 231 | - align-self: baseline; | |
| 232 | - border-color: inherit; | |
| 233 | - border-width: 1px; | |
| 234 | - border-bottom: none; | |
| 235 | - border-top-style: inset; | |
| 236 | - border-left-style: inset; | |
| 237 | - border-right-style: inset; | |
| 238 | - cursor: pointer; | |
| 239 | - opacity: 0.6; | |
| 240 | -} | |
| 241 | -.tab-container > .tab-bar > .tab-button.selected { | |
| 242 | - text-decoration: underline; | |
| 243 | - opacity: 1.0; | |
| 244 | - border-top-style: outset; | |
| 245 | - border-left-style: outset; | |
| 246 | - border-right-style: outset; | |
| 247 | -} | |
| 248 | - | |
| 249 | -/** | |
| 250 | - Styles developed for /fileedit but which have wider | |
| 251 | - applicability... | |
| 252 | - | |
| 253 | - As of this writing, these are only used by /fileedit, but it is | |
| 254 | - anticipated that they will eventually need to be migrated over to | |
| 255 | - default_css.txt for use in other pages (specifically wiki and forum | |
| 256 | - page/post editors). | |
| 257 | -*/ | |
| 258 | -.flex-container { | |
| 259 | - display: flex; | |
| 260 | -} | |
| 261 | -.flex-container.flex-row { | |
| 262 | - flex-direction: row; | |
| 263 | - flex-wrap: wrap; | |
| 264 | - justify-content: center; | |
| 265 | - align-items: center; | |
| 266 | -} | |
| 267 | -.flex-container .flex-grow { | |
| 268 | - flex-grow: 10; | |
| 269 | - flex-shrink: 0; | |
| 270 | -} | |
| 271 | -.flex-container .flex-shrink { | |
| 272 | - flex-grow: 0; | |
| 273 | - flex-shrink: 10; | |
| 274 | -} | |
| 275 | -.flex-container.flex-row.stretch { | |
| 276 | - flex-wrap: wrap; | |
| 277 | - align-items: baseline; | |
| 278 | - justify-content: stretch; | |
| 279 | - margin: 0; | |
| 280 | -} | |
| 281 | -.flex-container.flex-column { | |
| 282 | - flex-direction: column; | |
| 283 | - flex-wrap: wrap; | |
| 284 | - justify-content: center; | |
| 285 | - align-items: center; | |
| 286 | -} | |
| 287 | -.flex-container.flex-column.stretch { | |
| 288 | - align-items: stretch; | |
| 289 | - margin: 0; | |
| 290 | -} | |
| 291 | -.flex-container.child-gap-small > * { | |
| 292 | - margin: 0.25em; | |
| 293 | -} | |
| 294 | -#fossil-status-bar { | |
| 295 | - display: block; | |
| 296 | - font-family: monospace; | |
| 297 | - border-width: 1px; | |
| 298 | - border-style: inset; | |
| 299 | - border-color: inherit; | |
| 300 | - min-height: 1.5em; | |
| 301 | - font-size: 1.2em; | |
| 302 | - padding: 0.2em; | |
| 303 | - margin: 0.25em 0; | |
| 304 | - flex: 0 0 auto; | |
| 305 | -} | |
| 306 | -.font-size-100 { | |
| 307 | - font-size: 100%; | |
| 308 | -} | |
| 309 | -.font-size-125 { | |
| 310 | - font-size: 125%; | |
| 311 | -} | |
| 312 | -.font-size-150 { | |
| 313 | - font-size: 150%; | |
| 314 | -} | |
| 315 | -.font-size-175 { | |
| 316 | - font-size: 175%; | |
| 317 | -} | |
| 318 | -.font-size-200 { | |
| 319 | - font-size: 200%; | |
| 320 | -} | |
| 321 | - | |
| 322 | -/** | |
| 323 | - .input-with-label is intended to be a wrapper element which | |
| 324 | - contain both a LABEL tag and an INPUT or SELECT control. | |
| 325 | - The wrapper is "necessary", as opposed to placing the INPUT | |
| 326 | - in the LABEL, so that we can include multiple INPUT | |
| 327 | - elements (e.g. a set of radio buttons). | |
| 328 | -*/ | |
| 329 | -.input-with-label { | |
| 330 | - border: 1px inset #808080; | |
| 331 | - border-radius: 0.5em; | |
| 332 | - padding: 0.25em 0.4em; | |
| 333 | - margin: 0 0.5em; | |
| 334 | - display: inline-block; | |
| 335 | - cursor: default; | |
| 336 | -} | |
| 337 | -.input-with-label > * { | |
| 338 | - vertical-align: middle; | |
| 339 | -} | |
| 340 | -.input-with-label > label { | |
| 341 | - display: inline; /* some skins set label display to block! */ | |
| 342 | -} | |
| 343 | -.input-with-label > input { | |
| 344 | - margin: 0; | |
| 345 | -} | |
| 346 | -.input-with-label > button { | |
| 347 | - margin: 0; | |
| 348 | -} | |
| 349 | -.input-with-label > select { | |
| 350 | - margin: 0; | |
| 351 | -} | |
| 352 | -.input-with-label > input[type=text] { | |
| 353 | - margin: 0; | |
| 354 | -} | |
| 355 | -.input-with-label > textarea { | |
| 356 | - margin: 0; | |
| 357 | -} | |
| 358 | -.input-with-label > input[type=checkbox] { | |
| 359 | - vertical-align: sub; | |
| 360 | -} | |
| 361 | -.input-with-label > input[type=radio] { | |
| 362 | - vertical-align: sub; | |
| 363 | -} | |
| 364 | -.input-with-label > label { | |
| 365 | - font-weight: initial; | |
| 366 | - margin: 0 0.25em 0 0.25em; | |
| 367 | - vertical-align: middle; | |
| 191 | +body.fileedit #fileedit-edit-status { | |
| 192 | + border-radius: 0.25em 0.25em 0 0; | |
| 193 | + margin: 0; | |
| 194 | + padding: 0; | |
| 195 | + width: 100%; | |
| 196 | + cursor: initial; | |
| 197 | + display: flex; | |
| 198 | + flex-direction: row; | |
| 199 | + flex-wrap: wrap; | |
| 200 | + justify-content: space-between; | |
| 201 | + font-family: monospace; | |
| 202 | +} | |
| 203 | +body.fileedit #fileedit-edit-status > span.name > a { | |
| 204 | + display: block; | |
| 205 | + word-break: break-word /* needed for long paths */; | |
| 206 | +} | |
| 207 | +body.fileedit #fileedit-edit-status > span.links { | |
| 208 | + display: flex; | |
| 209 | + flex-wrap: wrap; | |
| 210 | + flex-direction: row; | |
| 211 | +} | |
| 212 | +body.fileedit #fileedit-file-selector span.is-new, | |
| 213 | +body.fileedit #fileedit-file-selector span.is-modified { | |
| 214 | + font-family: monospace; | |
| 215 | +} | |
| 216 | +body.fileedit #fileedit-edit-status span.links > * { | |
| 217 | + margin: 0 0.25em; | |
| 218 | + white-space: nowrap; | |
| 219 | +} | |
| 220 | +body.fileedit #fileedit-edit-status span.links > *::before { | |
| 221 | + content: "["; | |
| 222 | +} | |
| 223 | +body.fileedit #fileedit-edit-status span.links > *::after { | |
| 224 | + content: "]"; | |
| 368 | 225 | } |
| 369 | 226 | |
| 370 | 227 | ADDED src/style.wikiedit.css |
| --- src/style.fileedit.css | |
| +++ src/style.fileedit.css | |
| @@ -15,15 +15,16 @@ | |
| 15 | height: initial/*undo damage from some skins*/; |
| 16 | max-width: initial /* default.css pins it at 95% */; |
| 17 | } |
| 18 | body.fileedit textarea:focus, |
| 19 | body.fileedit input:focus{ |
| 20 | /* The sudden appearance of a border (as in the Ardoise skin) |
| 21 | shifts the layout in unsightly ways */ |
| 22 | border: initial; |
| 23 | } |
| 24 | body.fileedit fieldset { |
| 25 | margin: 0.5em 0 0.5em 0; |
| 26 | padding: 0.25em 0; |
| 27 | border-radius: 0.5em; |
| 28 | border-color: inherit; |
| 29 | border-width: 1px; |
| @@ -65,11 +66,11 @@ | |
| 65 | body.fileedit div.fileedit-preview { |
| 66 | margin: 0; |
| 67 | padding: 0; |
| 68 | } |
| 69 | body.fileedit #fileedit-tabs { |
| 70 | margin: 1em 0 0 0; |
| 71 | } |
| 72 | body.fileedit #fileedit-tab-preview-wrapper { |
| 73 | overflow: auto; |
| 74 | } |
| 75 | body.fileedit #fileedit-tab-fileselect > h1 { |
| @@ -114,27 +115,28 @@ | |
| 114 | } |
| 115 | body.fileedit #fileedit-file-selector { |
| 116 | display: flex; |
| 117 | flex-direction: column; |
| 118 | align-content: flex-start; |
| 119 | border-color: inherit; |
| 120 | border-width: 1px; |
| 121 | border-style: inset; |
| 122 | border-radius: 0.5em; |
| 123 | padding: 0 0.25em; |
| 124 | margin: 0; |
| 125 | min-height: 12em; |
| 126 | } |
| 127 | body.fileedit #fileedit-file-selector select { |
| 128 | margin: 0 0 0.5em 0; |
| 129 | height: initial; |
| 130 | font-family: monospace; |
| 131 | } |
| 132 | body.fileedit select:focus { |
| 133 | border: none; |
| 134 | } |
| 135 | body.fileedit option:focus { |
| 136 | border: none; |
| 137 | } |
| 138 | body.fileedit #fileedit-file-selector > div { |
| 139 | padding: 0; |
| 140 | margin: 0; |
| @@ -184,185 +186,40 @@ | |
| 184 | } |
| 185 | body.fileedit .sbsdiffcols div.difftxtcol pre { |
| 186 | max-width: 44em; |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | Styles for fossil.tabs.js. As of this writing, currently |
| 191 | only used by /fileedit, but it is anticipated that these |
| 192 | will eventually need to migrate to default_css.txt for use |
| 193 | in the wiki and/or forum pages when implementing tabbed |
| 194 | ajax-based previews. |
| 195 | */ |
| 196 | .tab-container { |
| 197 | width: 100%; |
| 198 | display: flex; |
| 199 | flex-direction: column; |
| 200 | align-items: stretch; |
| 201 | } |
| 202 | .tab-container > #fossil-status-bar { |
| 203 | margin-top: 0; |
| 204 | } |
| 205 | .tab-container > .tabs { |
| 206 | padding: 0.25em; |
| 207 | margin: 0; |
| 208 | display: flex; |
| 209 | flex-direction: column; |
| 210 | border-width: 1px; |
| 211 | border-style: outset; |
| 212 | border-color: inherit; |
| 213 | } |
| 214 | .tab-container > .tabs > .tab-panel { |
| 215 | align-self: stretch; |
| 216 | flex: 10 1 auto; |
| 217 | display: block; |
| 218 | } |
| 219 | .tab-container > .tab-bar { |
| 220 | display: flex; |
| 221 | flex-direction: row; |
| 222 | flex: 1 10 auto; |
| 223 | align-self: stretch; |
| 224 | flex-wrap: wrap; |
| 225 | } |
| 226 | .tab-container > .tab-bar > .tab-button { |
| 227 | display: inline-block; |
| 228 | border-radius: 0.5em 0.5em 0 0; |
| 229 | margin: 0 0.1em; |
| 230 | padding: 0.25em 0.75em; |
| 231 | align-self: baseline; |
| 232 | border-color: inherit; |
| 233 | border-width: 1px; |
| 234 | border-bottom: none; |
| 235 | border-top-style: inset; |
| 236 | border-left-style: inset; |
| 237 | border-right-style: inset; |
| 238 | cursor: pointer; |
| 239 | opacity: 0.6; |
| 240 | } |
| 241 | .tab-container > .tab-bar > .tab-button.selected { |
| 242 | text-decoration: underline; |
| 243 | opacity: 1.0; |
| 244 | border-top-style: outset; |
| 245 | border-left-style: outset; |
| 246 | border-right-style: outset; |
| 247 | } |
| 248 | |
| 249 | /** |
| 250 | Styles developed for /fileedit but which have wider |
| 251 | applicability... |
| 252 | |
| 253 | As of this writing, these are only used by /fileedit, but it is |
| 254 | anticipated that they will eventually need to be migrated over to |
| 255 | default_css.txt for use in other pages (specifically wiki and forum |
| 256 | page/post editors). |
| 257 | */ |
| 258 | .flex-container { |
| 259 | display: flex; |
| 260 | } |
| 261 | .flex-container.flex-row { |
| 262 | flex-direction: row; |
| 263 | flex-wrap: wrap; |
| 264 | justify-content: center; |
| 265 | align-items: center; |
| 266 | } |
| 267 | .flex-container .flex-grow { |
| 268 | flex-grow: 10; |
| 269 | flex-shrink: 0; |
| 270 | } |
| 271 | .flex-container .flex-shrink { |
| 272 | flex-grow: 0; |
| 273 | flex-shrink: 10; |
| 274 | } |
| 275 | .flex-container.flex-row.stretch { |
| 276 | flex-wrap: wrap; |
| 277 | align-items: baseline; |
| 278 | justify-content: stretch; |
| 279 | margin: 0; |
| 280 | } |
| 281 | .flex-container.flex-column { |
| 282 | flex-direction: column; |
| 283 | flex-wrap: wrap; |
| 284 | justify-content: center; |
| 285 | align-items: center; |
| 286 | } |
| 287 | .flex-container.flex-column.stretch { |
| 288 | align-items: stretch; |
| 289 | margin: 0; |
| 290 | } |
| 291 | .flex-container.child-gap-small > * { |
| 292 | margin: 0.25em; |
| 293 | } |
| 294 | #fossil-status-bar { |
| 295 | display: block; |
| 296 | font-family: monospace; |
| 297 | border-width: 1px; |
| 298 | border-style: inset; |
| 299 | border-color: inherit; |
| 300 | min-height: 1.5em; |
| 301 | font-size: 1.2em; |
| 302 | padding: 0.2em; |
| 303 | margin: 0.25em 0; |
| 304 | flex: 0 0 auto; |
| 305 | } |
| 306 | .font-size-100 { |
| 307 | font-size: 100%; |
| 308 | } |
| 309 | .font-size-125 { |
| 310 | font-size: 125%; |
| 311 | } |
| 312 | .font-size-150 { |
| 313 | font-size: 150%; |
| 314 | } |
| 315 | .font-size-175 { |
| 316 | font-size: 175%; |
| 317 | } |
| 318 | .font-size-200 { |
| 319 | font-size: 200%; |
| 320 | } |
| 321 | |
| 322 | /** |
| 323 | .input-with-label is intended to be a wrapper element which |
| 324 | contain both a LABEL tag and an INPUT or SELECT control. |
| 325 | The wrapper is "necessary", as opposed to placing the INPUT |
| 326 | in the LABEL, so that we can include multiple INPUT |
| 327 | elements (e.g. a set of radio buttons). |
| 328 | */ |
| 329 | .input-with-label { |
| 330 | border: 1px inset #808080; |
| 331 | border-radius: 0.5em; |
| 332 | padding: 0.25em 0.4em; |
| 333 | margin: 0 0.5em; |
| 334 | display: inline-block; |
| 335 | cursor: default; |
| 336 | } |
| 337 | .input-with-label > * { |
| 338 | vertical-align: middle; |
| 339 | } |
| 340 | .input-with-label > label { |
| 341 | display: inline; /* some skins set label display to block! */ |
| 342 | } |
| 343 | .input-with-label > input { |
| 344 | margin: 0; |
| 345 | } |
| 346 | .input-with-label > button { |
| 347 | margin: 0; |
| 348 | } |
| 349 | .input-with-label > select { |
| 350 | margin: 0; |
| 351 | } |
| 352 | .input-with-label > input[type=text] { |
| 353 | margin: 0; |
| 354 | } |
| 355 | .input-with-label > textarea { |
| 356 | margin: 0; |
| 357 | } |
| 358 | .input-with-label > input[type=checkbox] { |
| 359 | vertical-align: sub; |
| 360 | } |
| 361 | .input-with-label > input[type=radio] { |
| 362 | vertical-align: sub; |
| 363 | } |
| 364 | .input-with-label > label { |
| 365 | font-weight: initial; |
| 366 | margin: 0 0.25em 0 0.25em; |
| 367 | vertical-align: middle; |
| 368 | } |
| 369 | |
| 370 | DDED src/style.wikiedit.css |
| --- src/style.fileedit.css | |
| +++ src/style.fileedit.css | |
| @@ -15,15 +15,16 @@ | |
| 15 | height: initial/*undo damage from some skins*/; |
| 16 | max-width: initial /* default.css pins it at 95% */; |
| 17 | } |
| 18 | body.fileedit textarea:focus, |
| 19 | body.fileedit input:focus{ |
| 20 | /* Depending on the skin, it might be useful to add one or both of |
| 21 | the following... */ |
| 22 | /*border-width: 1px;*/ |
| 23 | /*border: initial; */ |
| 24 | } |
| 25 | body.fileedit fieldset:not(.tab-wrapper) { |
| 26 | margin: 0.5em 0 0.5em 0; |
| 27 | padding: 0.25em 0; |
| 28 | border-radius: 0.5em; |
| 29 | border-color: inherit; |
| 30 | border-width: 1px; |
| @@ -65,11 +66,11 @@ | |
| 66 | body.fileedit div.fileedit-preview { |
| 67 | margin: 0; |
| 68 | padding: 0; |
| 69 | } |
| 70 | body.fileedit #fileedit-tabs { |
| 71 | margin: 0.5em 0 0 0; |
| 72 | } |
| 73 | body.fileedit #fileedit-tab-preview-wrapper { |
| 74 | overflow: auto; |
| 75 | } |
| 76 | body.fileedit #fileedit-tab-fileselect > h1 { |
| @@ -114,27 +115,28 @@ | |
| 115 | } |
| 116 | body.fileedit #fileedit-file-selector { |
| 117 | display: flex; |
| 118 | flex-direction: column; |
| 119 | align-content: flex-start; |
| 120 | padding: 0 0.25em; |
| 121 | margin: 0; |
| 122 | min-height: 12em; |
| 123 | } |
| 124 | body.fileedit #fileedit-file-selector select { |
| 125 | margin: 0 0 0.5em 0; |
| 126 | height: initial; |
| 127 | font-family: monospace; |
| 128 | border: initial; |
| 129 | } |
| 130 | body.fileedit select:focus { |
| 131 | border: initial; |
| 132 | } |
| 133 | body.fileedit #fileedit-file-selector select option { |
| 134 | margin: 0 0 0.5em 0.55em; |
| 135 | } |
| 136 | body.fileedit select option, |
| 137 | body.fileedit select option:focus { |
| 138 | border: none; |
| 139 | } |
| 140 | body.fileedit #fileedit-file-selector > div { |
| 141 | padding: 0; |
| 142 | margin: 0; |
| @@ -184,185 +186,40 @@ | |
| 186 | } |
| 187 | body.fileedit .sbsdiffcols div.difftxtcol pre { |
| 188 | max-width: 44em; |
| 189 | } |
| 190 | |
| 191 | body.fileedit #fileedit-edit-status { |
| 192 | border-radius: 0.25em 0.25em 0 0; |
| 193 | margin: 0; |
| 194 | padding: 0; |
| 195 | width: 100%; |
| 196 | cursor: initial; |
| 197 | display: flex; |
| 198 | flex-direction: row; |
| 199 | flex-wrap: wrap; |
| 200 | justify-content: space-between; |
| 201 | font-family: monospace; |
| 202 | } |
| 203 | body.fileedit #fileedit-edit-status > span.name > a { |
| 204 | display: block; |
| 205 | word-break: break-word /* needed for long paths */; |
| 206 | } |
| 207 | body.fileedit #fileedit-edit-status > span.links { |
| 208 | display: flex; |
| 209 | flex-wrap: wrap; |
| 210 | flex-direction: row; |
| 211 | } |
| 212 | body.fileedit #fileedit-file-selector span.is-new, |
| 213 | body.fileedit #fileedit-file-selector span.is-modified { |
| 214 | font-family: monospace; |
| 215 | } |
| 216 | body.fileedit #fileedit-edit-status span.links > * { |
| 217 | margin: 0 0.25em; |
| 218 | white-space: nowrap; |
| 219 | } |
| 220 | body.fileedit #fileedit-edit-status span.links > *::before { |
| 221 | content: "["; |
| 222 | } |
| 223 | body.fileedit #fileedit-edit-status span.links > *::after { |
| 224 | content: "]"; |
| 225 | } |
| 226 | |
| 227 | DDED src/style.wikiedit.css |
+185
| --- a/src/style.wikiedit.css | ||
| +++ b/src/style.wikiedit.css | ||
| @@ -0,0 +1,185 @@ | ||
| 1 | +body.wikieedit.waiting * { | |
| 2 | + /* Triggered during AJAX requests. */ | |
| 3 | + cursor: wait; | |
| 4 | +} | |
| 5 | +body.wikiedit textarea, | |
| 6 | +body.wikiedit textarea:focus, | |
| 7 | +body.wikiedit input, | |
| 8 | +body.wikiedit input:focus, | |
| 9 | +body.wikiedit select, | |
| 10 | +body.wikiedit select:focus{ | |
| 11 | + /* Depending on the skin, it might be useful to add one or both of | |
| 12 | + the following... */ | |
| 13 | + /*border-width: 1px;*/ | |
| 14 | + /*border: initial; */ | |
| 15 | +} | |
| 16 | +body.wikiedit div.wikiedit-preview { | |
| 17 | + margin: 0; | |
| 18 | + padding: 0; | |
| 19 | +} | |
| 20 | +body.wikiedit #wikiedit-tabs { | |
| 21 | + margin: 0.5em 0 0 0; | |
| 22 | +} | |
| 23 | +body.wikiedit #wikiedit-tab-preview-wrapper { | |
| 24 | + overflow: auto; | |
| 25 | +} | |
| 26 | +body.wikiedit .tab-container > .tabs > .tab{ | |
| 27 | + margin-top: 0; | |
| 28 | + border: none; | |
| 29 | + border-radius: 0; | |
| 30 | + border-bottom-width: 1px; | |
| 31 | + border-bottom-style: dotted; | |
| 32 | +} | |
| 33 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { | |
| 34 | + vertical-align: middle; | |
| 35 | + margin: 0.5em; | |
| 36 | +} | |
| 37 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { | |
| 38 | + vertical-align: middle; | |
| 39 | + margin: 0.5em; | |
| 40 | +} | |
| 41 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; | |
| 42 | + margin: 0ut-with-label { | |
| 43 | + margin: 0iting * { | |
| 44 | + /* Triggered during AJAX requests. */ | |
| 45 | + cursor: wait; | |
| 46 | +} | |
| 47 | +body.wikiedit textarea, | |
| 48 | +body.wikiedit textarea:focus, | |
| 49 | +body.wikiedit input, | |
| 50 | +body.wikiedit input:focus, | |
| 51 | +body.wikiedit select, | |
| 52 | +body.wikiedit select:focus{ | |
| 53 | + /* Depending on the skin, it might be useful to add one or both of | |
| 54 | + the following... */ | |
| 55 | + /*border-width: 1px;*/ | |
| 56 | + /*border: initial; */ | |
| 57 | +} | |
| 58 | +body.wikiedit div.wikiedit-preview { | |
| 59 | + margin: 0; | |
| 60 | + padding: 0; | |
| 61 | +} | |
| 62 | +body.wikiedit #wikiedit-tabs { | |
| 63 | + margin: 0.5em 0 0 0; | |
| 64 | +} | |
| 65 | +body.wikiedit #wikiedit-tab-preview-wrapper { | |
| 66 | + overflow: auto; | |
| 67 | +} | |
| 68 | +body.wikiedit .tab-container > .tabs > .tab{ | |
| 69 | + margin-top: 0; | |
| 70 | + border: none; | |
| 71 | + border-radius: 0; | |
| 72 | + border-bottom-width: 1px; | |
| 73 | + border-bottom-style: dotted; | |
| 74 | +} | |
| 75 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { | |
| 76 | + vertical-align: middle; | |
| 77 | + margin: 0.5em; | |
| 78 | +} | |
| 79 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { | |
| 80 | + vertical-align: middle; | |
| 81 | + margin: 0.5em; | |
| 82 | +} | |
| 83 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; | |
| 84 | + margin: 0ut-with-label { | |
| 85 | + margin: 0 0.5em 0.25em 0.5em; | |
| 86 | +} | |
| 87 | +body.wikiedit label { | |
| 88 | + display: inline; /* some skins set label display to block! */ | |
| 89 | +} | |
| 90 | +body.wikiedit .wikiedit-options > div > * { | |
| 91 | + margin: 0.25em; | |
| 92 | +} | |
| 93 | +body.wikiedit .wikiedit-options.flex-container.flex-row { | |
| 94 | + align-items: first baseline; | |
| 95 | +} | |
| 96 | +body.wikiedit .WikiList { | |
| 97 | + display: flex; | |
| 98 | + flex-direction: column; | |
| 99 | + align-items: start; | |
| 100 | +} | |
| 101 | +body.wikiedit .WikiList select { | |
| 102 | + font-size: 110%; | |
| 103 | + margin: initial; | |
| 104 | + height: initial /* some skins set these to a fixed height */; | |
| 105 | + font-family: monospace; | |
| 106 | + border: initial; | |
| 107 | +} | |
| 108 | +body.wikiedit select:focus { | |
| 109 | + border: initial; | |
| 110 | +} | |
| 111 | +body.wikiedit .WikiList select option { | |
| 112 | + margin: 0 0 0.5em 0.55em; | |
| 113 | +} | |
| 114 | +body.wikiedit select option, | |
| 115 | +body.wikiedit select option:focus { | |
| 116 | + border: none; | |
| 117 | +} | |
| 118 | +body.wikiedit .WikiList select option.stashed, | |
| 119 | +body.wikiedit .WikiList select option.stashed-new, | |
| 120 | +body.wikiedit .WikiList select option.deleted { | |
| 121 | + marginbody.wikieedit.waiting * { | |
| 122 | + /* Triggered during AJAX requests. */ | |
| 123 | + cursor: wait; | |
| 124 | +} | |
| 125 | +body.wikiedit textarea, | |
| 126 | +body.wikiedit textarea:focus, | |
| 127 | +body.wikiedit input, | |
| 128 | +body.wikiedit input:focus, | |
| 129 | +body.wikiedit select, | |
| 130 | +body.wikiedit select:focus{ | |
| 131 | + /* Depending on the skin, it might be useful to add one or both of | |
| 132 | + the following... */ | |
| 133 | + /*border-width: 1px;*/ | |
| 134 | + /*border: initial; */ | |
| 135 | +} | |
| 136 | +body.wikiedit div.wikiedit-preview { | |
| 137 | + margin: 0; | |
| 138 | + padding: 0; | |
| 139 | +} | |
| 140 | +body.wikiedit #wikiedit-tabs { | |
| 141 | + margin: 0.5em 0 0 0; | |
| 142 | +} | |
| 143 | +body.wikiedit #wikiedit-tab-preview-wrapper { | |
| 144 | + overflow: auto; | |
| 145 | +} | |
| 146 | +body.wikiedit .tab-container > .tabs > .tab{ | |
| 147 | + margin-top: 0; | |
| 148 | + border: none; | |
| 149 | + border-radius: 0; | |
| 150 | + border-bottom-width: 1px; | |
| 151 | + border-bottom-style: dotted; | |
| 152 | +} | |
| 153 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { | |
| 154 | + vertical-align: middle; | |
| 155 | + margin: 0.5em; | |
| 156 | +} | |
| 157 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { | |
| 158 | + vertical-align: middle; | |
| 159 | + margin: 0.5em; | |
| 160 | +} | |
| 161 | +body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; | |
| 162 | + margin: 0ut-with-label { | |
| 163 | + margin: 0 0.5em 0.25em 0.5em; | |
| 164 | +} | |
| 165 | +body.wikiedit label { | |
| 166 | + display: inline; /* some skins set label display to block! */ | |
| 167 | +} | |
| 168 | +body.wikiedit .wikiedit-options > div > * { | |
| 169 | + margin: 0.25em; | |
| 170 | +} | |
| 171 | +body.wikiedit .wikiedit-options.flex-container.flex-row { | |
| 172 | + align-items: first baseline; | |
| 173 | +} | |
| 174 | +body.wikiedit .WikiList { | |
| 175 | + display: flex; | |
| 176 | + flex-direction: column; | |
| 177 | + align-items: start; | |
| 178 | +} | |
| 179 | +body.wikiedit .WikiList select { | |
| 180 | + font-size: 110%; | |
| 181 | + margin: initial; | |
| 182 | +edit #wikiedit-tabsns > input { | |
| 183 | + vertical-alig flex: 10 1 auto; | |
| 184 | +} | |
| 185 | + |
| --- a/src/style.wikiedit.css | |
| +++ b/src/style.wikiedit.css | |
| @@ -0,0 +1,185 @@ | |
| --- a/src/style.wikiedit.css | |
| +++ b/src/style.wikiedit.css | |
| @@ -0,0 +1,185 @@ | |
| 1 | body.wikieedit.waiting * { |
| 2 | /* Triggered during AJAX requests. */ |
| 3 | cursor: wait; |
| 4 | } |
| 5 | body.wikiedit textarea, |
| 6 | body.wikiedit textarea:focus, |
| 7 | body.wikiedit input, |
| 8 | body.wikiedit input:focus, |
| 9 | body.wikiedit select, |
| 10 | body.wikiedit select:focus{ |
| 11 | /* Depending on the skin, it might be useful to add one or both of |
| 12 | the following... */ |
| 13 | /*border-width: 1px;*/ |
| 14 | /*border: initial; */ |
| 15 | } |
| 16 | body.wikiedit div.wikiedit-preview { |
| 17 | margin: 0; |
| 18 | padding: 0; |
| 19 | } |
| 20 | body.wikiedit #wikiedit-tabs { |
| 21 | margin: 0.5em 0 0 0; |
| 22 | } |
| 23 | body.wikiedit #wikiedit-tab-preview-wrapper { |
| 24 | overflow: auto; |
| 25 | } |
| 26 | body.wikiedit .tab-container > .tabs > .tab{ |
| 27 | margin-top: 0; |
| 28 | border: none; |
| 29 | border-radius: 0; |
| 30 | border-bottom-width: 1px; |
| 31 | border-bottom-style: dotted; |
| 32 | } |
| 33 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { |
| 34 | vertical-align: middle; |
| 35 | margin: 0.5em; |
| 36 | } |
| 37 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { |
| 38 | vertical-align: middle; |
| 39 | margin: 0.5em; |
| 40 | } |
| 41 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; |
| 42 | margin: 0ut-with-label { |
| 43 | margin: 0iting * { |
| 44 | /* Triggered during AJAX requests. */ |
| 45 | cursor: wait; |
| 46 | } |
| 47 | body.wikiedit textarea, |
| 48 | body.wikiedit textarea:focus, |
| 49 | body.wikiedit input, |
| 50 | body.wikiedit input:focus, |
| 51 | body.wikiedit select, |
| 52 | body.wikiedit select:focus{ |
| 53 | /* Depending on the skin, it might be useful to add one or both of |
| 54 | the following... */ |
| 55 | /*border-width: 1px;*/ |
| 56 | /*border: initial; */ |
| 57 | } |
| 58 | body.wikiedit div.wikiedit-preview { |
| 59 | margin: 0; |
| 60 | padding: 0; |
| 61 | } |
| 62 | body.wikiedit #wikiedit-tabs { |
| 63 | margin: 0.5em 0 0 0; |
| 64 | } |
| 65 | body.wikiedit #wikiedit-tab-preview-wrapper { |
| 66 | overflow: auto; |
| 67 | } |
| 68 | body.wikiedit .tab-container > .tabs > .tab{ |
| 69 | margin-top: 0; |
| 70 | border: none; |
| 71 | border-radius: 0; |
| 72 | border-bottom-width: 1px; |
| 73 | border-bottom-style: dotted; |
| 74 | } |
| 75 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { |
| 76 | vertical-align: middle; |
| 77 | margin: 0.5em; |
| 78 | } |
| 79 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { |
| 80 | vertical-align: middle; |
| 81 | margin: 0.5em; |
| 82 | } |
| 83 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; |
| 84 | margin: 0ut-with-label { |
| 85 | margin: 0 0.5em 0.25em 0.5em; |
| 86 | } |
| 87 | body.wikiedit label { |
| 88 | display: inline; /* some skins set label display to block! */ |
| 89 | } |
| 90 | body.wikiedit .wikiedit-options > div > * { |
| 91 | margin: 0.25em; |
| 92 | } |
| 93 | body.wikiedit .wikiedit-options.flex-container.flex-row { |
| 94 | align-items: first baseline; |
| 95 | } |
| 96 | body.wikiedit .WikiList { |
| 97 | display: flex; |
| 98 | flex-direction: column; |
| 99 | align-items: start; |
| 100 | } |
| 101 | body.wikiedit .WikiList select { |
| 102 | font-size: 110%; |
| 103 | margin: initial; |
| 104 | height: initial /* some skins set these to a fixed height */; |
| 105 | font-family: monospace; |
| 106 | border: initial; |
| 107 | } |
| 108 | body.wikiedit select:focus { |
| 109 | border: initial; |
| 110 | } |
| 111 | body.wikiedit .WikiList select option { |
| 112 | margin: 0 0 0.5em 0.55em; |
| 113 | } |
| 114 | body.wikiedit select option, |
| 115 | body.wikiedit select option:focus { |
| 116 | border: none; |
| 117 | } |
| 118 | body.wikiedit .WikiList select option.stashed, |
| 119 | body.wikiedit .WikiList select option.stashed-new, |
| 120 | body.wikiedit .WikiList select option.deleted { |
| 121 | marginbody.wikieedit.waiting * { |
| 122 | /* Triggered during AJAX requests. */ |
| 123 | cursor: wait; |
| 124 | } |
| 125 | body.wikiedit textarea, |
| 126 | body.wikiedit textarea:focus, |
| 127 | body.wikiedit input, |
| 128 | body.wikiedit input:focus, |
| 129 | body.wikiedit select, |
| 130 | body.wikiedit select:focus{ |
| 131 | /* Depending on the skin, it might be useful to add one or both of |
| 132 | the following... */ |
| 133 | /*border-width: 1px;*/ |
| 134 | /*border: initial; */ |
| 135 | } |
| 136 | body.wikiedit div.wikiedit-preview { |
| 137 | margin: 0; |
| 138 | padding: 0; |
| 139 | } |
| 140 | body.wikiedit #wikiedit-tabs { |
| 141 | margin: 0.5em 0 0 0; |
| 142 | } |
| 143 | body.wikiedit #wikiedit-tab-preview-wrapper { |
| 144 | overflow: auto; |
| 145 | } |
| 146 | body.wikiedit .tab-container > .tabs > .tab{ |
| 147 | margin-top: 0; |
| 148 | border: none; |
| 149 | border-radius: 0; |
| 150 | border-bottom-width: 1px; |
| 151 | border-bottom-style: dotted; |
| 152 | } |
| 153 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > button { |
| 154 | vertical-align: middle; |
| 155 | margin: 0.5em; |
| 156 | } |
| 157 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input { |
| 158 | vertical-align: middle; |
| 159 | margin: 0.5em; |
| 160 | } |
| 161 | body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-optiovertical-align: middle; |
| 162 | margin: 0ut-with-label { |
| 163 | margin: 0 0.5em 0.25em 0.5em; |
| 164 | } |
| 165 | body.wikiedit label { |
| 166 | display: inline; /* some skins set label display to block! */ |
| 167 | } |
| 168 | body.wikiedit .wikiedit-options > div > * { |
| 169 | margin: 0.25em; |
| 170 | } |
| 171 | body.wikiedit .wikiedit-options.flex-container.flex-row { |
| 172 | align-items: first baseline; |
| 173 | } |
| 174 | body.wikiedit .WikiList { |
| 175 | display: flex; |
| 176 | flex-direction: column; |
| 177 | align-items: start; |
| 178 | } |
| 179 | body.wikiedit .WikiList select { |
| 180 | font-size: 110%; |
| 181 | margin: initial; |
| 182 | edit #wikiedit-tabsns > input { |
| 183 | vertical-alig flex: 10 1 auto; |
| 184 | } |
| 185 |
+5
-4
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -217,11 +217,11 @@ | ||
| 217 | 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | 218 | ** -v|--verbose Additional (debugging) output |
| 219 | 219 | ** --verily Exchange extra information with the remote |
| 220 | 220 | ** to ensure no content is overlooked |
| 221 | 221 | ** |
| 222 | -** See also: clone, config pull, push, remote-url, sync | |
| 222 | +** See also: [[clone]], [[config]], [[push]], [[remote-url]], [[sync]] | |
| 223 | 223 | */ |
| 224 | 224 | void pull_cmd(void){ |
| 225 | 225 | unsigned configFlags = 0; |
| 226 | 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | ||
| 266 | 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | 267 | ** -v|--verbose Additional (debugging) output |
| 268 | 268 | ** --verily Exchange extra information with the remote |
| 269 | 269 | ** to ensure no content is overlooked |
| 270 | 270 | ** |
| 271 | -** See also: clone, config push, pull, remote-url, sync | |
| 271 | +** See also: [[clone]], [[config]], [[pull]], [[remote-url]], [[sync]] | |
| 272 | 272 | */ |
| 273 | 273 | void push_cmd(void){ |
| 274 | 274 | unsigned configFlags = 0; |
| 275 | 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | ||
| 312 | 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | 313 | ** -v|--verbose Additional (debugging) output |
| 314 | 314 | ** --verily Exchange extra information with the remote |
| 315 | 315 | ** to ensure no content is overlooked |
| 316 | 316 | ** |
| 317 | -** See also: clone, pull, push, remote-url | |
| 317 | +** See also: [[clone]], [[pull]], [[push]], [[remote-url]] | |
| 318 | 318 | */ |
| 319 | 319 | void sync_cmd(void){ |
| 320 | 320 | unsigned configFlags = 0; |
| 321 | 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | ||
| 345 | 345 | verify_all_options(); |
| 346 | 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | 347 | } |
| 348 | 348 | |
| 349 | 349 | /* |
| 350 | -** COMMAND: remote-url | |
| 350 | +** COMMAND: remote | |
| 351 | +** COMMAND: remote-url* | |
| 351 | 352 | ** |
| 352 | 353 | ** Usage: %fossil remote-url ?URL|off? |
| 353 | 354 | ** |
| 354 | 355 | ** Query and/or change the default server URL used by the "pull", "push", |
| 355 | 356 | ** and "sync" commands. |
| 356 | 357 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | ** -v|--verbose Additional (debugging) output |
| 219 | ** --verily Exchange extra information with the remote |
| 220 | ** to ensure no content is overlooked |
| 221 | ** |
| 222 | ** See also: clone, config pull, push, remote-url, sync |
| 223 | */ |
| 224 | void pull_cmd(void){ |
| 225 | unsigned configFlags = 0; |
| 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | |
| 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | ** -v|--verbose Additional (debugging) output |
| 268 | ** --verily Exchange extra information with the remote |
| 269 | ** to ensure no content is overlooked |
| 270 | ** |
| 271 | ** See also: clone, config push, pull, remote-url, sync |
| 272 | */ |
| 273 | void push_cmd(void){ |
| 274 | unsigned configFlags = 0; |
| 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | |
| 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | ** -v|--verbose Additional (debugging) output |
| 314 | ** --verily Exchange extra information with the remote |
| 315 | ** to ensure no content is overlooked |
| 316 | ** |
| 317 | ** See also: clone, pull, push, remote-url |
| 318 | */ |
| 319 | void sync_cmd(void){ |
| 320 | unsigned configFlags = 0; |
| 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | |
| 345 | verify_all_options(); |
| 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | ** COMMAND: remote-url |
| 351 | ** |
| 352 | ** Usage: %fossil remote-url ?URL|off? |
| 353 | ** |
| 354 | ** Query and/or change the default server URL used by the "pull", "push", |
| 355 | ** and "sync" commands. |
| 356 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | ** -v|--verbose Additional (debugging) output |
| 219 | ** --verily Exchange extra information with the remote |
| 220 | ** to ensure no content is overlooked |
| 221 | ** |
| 222 | ** See also: [[clone]], [[config]], [[push]], [[remote-url]], [[sync]] |
| 223 | */ |
| 224 | void pull_cmd(void){ |
| 225 | unsigned configFlags = 0; |
| 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | |
| 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | ** -v|--verbose Additional (debugging) output |
| 268 | ** --verily Exchange extra information with the remote |
| 269 | ** to ensure no content is overlooked |
| 270 | ** |
| 271 | ** See also: [[clone]], [[config]], [[pull]], [[remote-url]], [[sync]] |
| 272 | */ |
| 273 | void push_cmd(void){ |
| 274 | unsigned configFlags = 0; |
| 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | |
| 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | ** -v|--verbose Additional (debugging) output |
| 314 | ** --verily Exchange extra information with the remote |
| 315 | ** to ensure no content is overlooked |
| 316 | ** |
| 317 | ** See also: [[clone]], [[pull]], [[push]], [[remote-url]] |
| 318 | */ |
| 319 | void sync_cmd(void){ |
| 320 | unsigned configFlags = 0; |
| 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | |
| 345 | verify_all_options(); |
| 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | ** COMMAND: remote |
| 351 | ** COMMAND: remote-url* |
| 352 | ** |
| 353 | ** Usage: %fossil remote-url ?URL|off? |
| 354 | ** |
| 355 | ** Query and/or change the default server URL used by the "pull", "push", |
| 356 | ** and "sync" commands. |
| 357 |
+5
-4
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -217,11 +217,11 @@ | ||
| 217 | 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | 218 | ** -v|--verbose Additional (debugging) output |
| 219 | 219 | ** --verily Exchange extra information with the remote |
| 220 | 220 | ** to ensure no content is overlooked |
| 221 | 221 | ** |
| 222 | -** See also: clone, config pull, push, remote-url, sync | |
| 222 | +** See also: [[clone]], [[config]], [[push]], [[remote-url]], [[sync]] | |
| 223 | 223 | */ |
| 224 | 224 | void pull_cmd(void){ |
| 225 | 225 | unsigned configFlags = 0; |
| 226 | 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | ||
| 266 | 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | 267 | ** -v|--verbose Additional (debugging) output |
| 268 | 268 | ** --verily Exchange extra information with the remote |
| 269 | 269 | ** to ensure no content is overlooked |
| 270 | 270 | ** |
| 271 | -** See also: clone, config push, pull, remote-url, sync | |
| 271 | +** See also: [[clone]], [[config]], [[pull]], [[remote-url]], [[sync]] | |
| 272 | 272 | */ |
| 273 | 273 | void push_cmd(void){ |
| 274 | 274 | unsigned configFlags = 0; |
| 275 | 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | ||
| 312 | 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | 313 | ** -v|--verbose Additional (debugging) output |
| 314 | 314 | ** --verily Exchange extra information with the remote |
| 315 | 315 | ** to ensure no content is overlooked |
| 316 | 316 | ** |
| 317 | -** See also: clone, pull, push, remote-url | |
| 317 | +** See also: [[clone]], [[pull]], [[push]], [[remote-url]] | |
| 318 | 318 | */ |
| 319 | 319 | void sync_cmd(void){ |
| 320 | 320 | unsigned configFlags = 0; |
| 321 | 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | ||
| 345 | 345 | verify_all_options(); |
| 346 | 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | 347 | } |
| 348 | 348 | |
| 349 | 349 | /* |
| 350 | -** COMMAND: remote-url | |
| 350 | +** COMMAND: remote | |
| 351 | +** COMMAND: remote-url* | |
| 351 | 352 | ** |
| 352 | 353 | ** Usage: %fossil remote-url ?URL|off? |
| 353 | 354 | ** |
| 354 | 355 | ** Query and/or change the default server URL used by the "pull", "push", |
| 355 | 356 | ** and "sync" commands. |
| 356 | 357 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | ** -v|--verbose Additional (debugging) output |
| 219 | ** --verily Exchange extra information with the remote |
| 220 | ** to ensure no content is overlooked |
| 221 | ** |
| 222 | ** See also: clone, config pull, push, remote-url, sync |
| 223 | */ |
| 224 | void pull_cmd(void){ |
| 225 | unsigned configFlags = 0; |
| 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | |
| 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | ** -v|--verbose Additional (debugging) output |
| 268 | ** --verily Exchange extra information with the remote |
| 269 | ** to ensure no content is overlooked |
| 270 | ** |
| 271 | ** See also: clone, config push, pull, remote-url, sync |
| 272 | */ |
| 273 | void push_cmd(void){ |
| 274 | unsigned configFlags = 0; |
| 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | |
| 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | ** -v|--verbose Additional (debugging) output |
| 314 | ** --verily Exchange extra information with the remote |
| 315 | ** to ensure no content is overlooked |
| 316 | ** |
| 317 | ** See also: clone, pull, push, remote-url |
| 318 | */ |
| 319 | void sync_cmd(void){ |
| 320 | unsigned configFlags = 0; |
| 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | |
| 345 | verify_all_options(); |
| 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | ** COMMAND: remote-url |
| 351 | ** |
| 352 | ** Usage: %fossil remote-url ?URL|off? |
| 353 | ** |
| 354 | ** Query and/or change the default server URL used by the "pull", "push", |
| 355 | ** and "sync" commands. |
| 356 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 218 | ** -v|--verbose Additional (debugging) output |
| 219 | ** --verily Exchange extra information with the remote |
| 220 | ** to ensure no content is overlooked |
| 221 | ** |
| 222 | ** See also: [[clone]], [[config]], [[push]], [[remote-url]], [[sync]] |
| 223 | */ |
| 224 | void pull_cmd(void){ |
| 225 | unsigned configFlags = 0; |
| 226 | unsigned syncFlags = SYNC_PULL; |
| 227 | unsigned urlOmitFlags = 0; |
| @@ -266,11 +266,11 @@ | |
| 266 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 267 | ** -v|--verbose Additional (debugging) output |
| 268 | ** --verily Exchange extra information with the remote |
| 269 | ** to ensure no content is overlooked |
| 270 | ** |
| 271 | ** See also: [[clone]], [[config]], [[pull]], [[remote-url]], [[sync]] |
| 272 | */ |
| 273 | void push_cmd(void){ |
| 274 | unsigned configFlags = 0; |
| 275 | unsigned syncFlags = SYNC_PUSH; |
| 276 | process_sync_args(&configFlags, &syncFlags, 0, 0); |
| @@ -312,11 +312,11 @@ | |
| 312 | ** -u|--unversioned Also sync unversioned content |
| 313 | ** -v|--verbose Additional (debugging) output |
| 314 | ** --verily Exchange extra information with the remote |
| 315 | ** to ensure no content is overlooked |
| 316 | ** |
| 317 | ** See also: [[clone]], [[pull]], [[push]], [[remote-url]] |
| 318 | */ |
| 319 | void sync_cmd(void){ |
| 320 | unsigned configFlags = 0; |
| 321 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 322 | if( find_option("unversioned","u",0)!=0 ){ |
| @@ -345,11 +345,12 @@ | |
| 345 | verify_all_options(); |
| 346 | client_sync(syncFlags, 0, 0, 0); |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | ** COMMAND: remote |
| 351 | ** COMMAND: remote-url* |
| 352 | ** |
| 353 | ** Usage: %fossil remote-url ?URL|off? |
| 354 | ** |
| 355 | ** Query and/or change the default server URL used by the "pull", "push", |
| 356 | ** and "sync" commands. |
| 357 |
+6
-2
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1083,12 +1083,12 @@ | ||
| 1083 | 1083 | cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : ""); |
| 1084 | 1084 | cgi_printf("\"h\":\"%!S\"}%s", |
| 1085 | 1085 | pRow->zUuid, pRow->pNext ? ",\n" : "]\n"); |
| 1086 | 1086 | } |
| 1087 | 1087 | @ }</script> |
| 1088 | - style_graph_generator(); | |
| 1089 | - style_copybutton_control(); /* Dependency: graph.js requires copybtn.js. */ | |
| 1088 | + builtin_request_js("graph.js"); | |
| 1089 | + builtin_request_js("copybtn.js"); /* Required by graph.js */ | |
| 1090 | 1090 | graph_free(pGraph); |
| 1091 | 1091 | } |
| 1092 | 1092 | } |
| 1093 | 1093 | |
| 1094 | 1094 | /* |
| @@ -1741,10 +1741,14 @@ | ||
| 1741 | 1741 | } |
| 1742 | 1742 | }else{ |
| 1743 | 1743 | z = "50"; |
| 1744 | 1744 | nEntry = 50; |
| 1745 | 1745 | } |
| 1746 | + | |
| 1747 | + /* Undocumented query parameter to set JS mode */ | |
| 1748 | + builtin_set_js_delivery_mode(P("jsmode"),1); | |
| 1749 | + | |
| 1746 | 1750 | secondaryRid = name_to_typed_rid(P("sel2"),"ci"); |
| 1747 | 1751 | selectedRid = name_to_typed_rid(P("sel1"),"ci"); |
| 1748 | 1752 | cgi_replace_query_parameter("n",z); |
| 1749 | 1753 | cookie_write_parameter("n","n",0); |
| 1750 | 1754 | tmFlags |= timeline_ss_submenu(); |
| 1751 | 1755 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1083,12 +1083,12 @@ | |
| 1083 | cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : ""); |
| 1084 | cgi_printf("\"h\":\"%!S\"}%s", |
| 1085 | pRow->zUuid, pRow->pNext ? ",\n" : "]\n"); |
| 1086 | } |
| 1087 | @ }</script> |
| 1088 | style_graph_generator(); |
| 1089 | style_copybutton_control(); /* Dependency: graph.js requires copybtn.js. */ |
| 1090 | graph_free(pGraph); |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | /* |
| @@ -1741,10 +1741,14 @@ | |
| 1741 | } |
| 1742 | }else{ |
| 1743 | z = "50"; |
| 1744 | nEntry = 50; |
| 1745 | } |
| 1746 | secondaryRid = name_to_typed_rid(P("sel2"),"ci"); |
| 1747 | selectedRid = name_to_typed_rid(P("sel1"),"ci"); |
| 1748 | cgi_replace_query_parameter("n",z); |
| 1749 | cookie_write_parameter("n","n",0); |
| 1750 | tmFlags |= timeline_ss_submenu(); |
| 1751 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1083,12 +1083,12 @@ | |
| 1083 | cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : ""); |
| 1084 | cgi_printf("\"h\":\"%!S\"}%s", |
| 1085 | pRow->zUuid, pRow->pNext ? ",\n" : "]\n"); |
| 1086 | } |
| 1087 | @ }</script> |
| 1088 | builtin_request_js("graph.js"); |
| 1089 | builtin_request_js("copybtn.js"); /* Required by graph.js */ |
| 1090 | graph_free(pGraph); |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | /* |
| @@ -1741,10 +1741,14 @@ | |
| 1741 | } |
| 1742 | }else{ |
| 1743 | z = "50"; |
| 1744 | nEntry = 50; |
| 1745 | } |
| 1746 | |
| 1747 | /* Undocumented query parameter to set JS mode */ |
| 1748 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1749 | |
| 1750 | secondaryRid = name_to_typed_rid(P("sel2"),"ci"); |
| 1751 | selectedRid = name_to_typed_rid(P("sel1"),"ci"); |
| 1752 | cgi_replace_query_parameter("n",z); |
| 1753 | cookie_write_parameter("n","n",0); |
| 1754 | tmFlags |= timeline_ss_submenu(); |
| 1755 |
+1
-1
| --- src/undo.c | ||
| +++ src/undo.c | ||
| @@ -462,11 +462,11 @@ | ||
| 462 | 462 | ** that are undoable. |
| 463 | 463 | ** |
| 464 | 464 | ** Options: |
| 465 | 465 | ** -n|--dry-run do not make changes but show what would be done |
| 466 | 466 | ** |
| 467 | -** See also: commit, status | |
| 467 | +** See also: [[commit]], [[status]] | |
| 468 | 468 | */ |
| 469 | 469 | void undo_cmd(void){ |
| 470 | 470 | int isRedo = g.argv[1][0]=='r'; |
| 471 | 471 | int undo_available; |
| 472 | 472 | int dryRunFlag = find_option("dry-run", "n", 0)!=0; |
| 473 | 473 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -462,11 +462,11 @@ | |
| 462 | ** that are undoable. |
| 463 | ** |
| 464 | ** Options: |
| 465 | ** -n|--dry-run do not make changes but show what would be done |
| 466 | ** |
| 467 | ** See also: commit, status |
| 468 | */ |
| 469 | void undo_cmd(void){ |
| 470 | int isRedo = g.argv[1][0]=='r'; |
| 471 | int undo_available; |
| 472 | int dryRunFlag = find_option("dry-run", "n", 0)!=0; |
| 473 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -462,11 +462,11 @@ | |
| 462 | ** that are undoable. |
| 463 | ** |
| 464 | ** Options: |
| 465 | ** -n|--dry-run do not make changes but show what would be done |
| 466 | ** |
| 467 | ** See also: [[commit]], [[status]] |
| 468 | */ |
| 469 | void undo_cmd(void){ |
| 470 | int isRedo = g.argv[1][0]=='r'; |
| 471 | int undo_available; |
| 472 | int dryRunFlag = find_option("dry-run", "n", 0)!=0; |
| 473 |
+2
-2
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -108,11 +108,11 @@ | ||
| 108 | 108 | ** checkin which modified them). |
| 109 | 109 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 110 | 110 | ** used for merging, named *-baseline, *-original, |
| 111 | 111 | ** and *-merge. |
| 112 | 112 | ** |
| 113 | -** See also: revert | |
| 113 | +** See also: [[revert]] | |
| 114 | 114 | */ |
| 115 | 115 | void update_cmd(void){ |
| 116 | 116 | int vid; /* Current version */ |
| 117 | 117 | int tid=0; /* Target version - version we are changing to */ |
| 118 | 118 | Stmt q; |
| @@ -786,11 +786,11 @@ | ||
| 786 | 786 | ** |
| 787 | 787 | ** Options: |
| 788 | 788 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 789 | 789 | ** VERSION |
| 790 | 790 | ** |
| 791 | -** See also: redo, undo, checkout, update | |
| 791 | +** See also: [[redo]], [[undo]], [[checkout]], [[update]] | |
| 792 | 792 | */ |
| 793 | 793 | void revert_cmd(void){ |
| 794 | 794 | Manifest *pCoManifest; /* Manifest of current checkout */ |
| 795 | 795 | Manifest *pRvManifest; /* Manifest of selected revert version */ |
| 796 | 796 | ManifestFile *pCoFile; /* File within current checkout manifest */ |
| 797 | 797 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -108,11 +108,11 @@ | |
| 108 | ** checkin which modified them). |
| 109 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 110 | ** used for merging, named *-baseline, *-original, |
| 111 | ** and *-merge. |
| 112 | ** |
| 113 | ** See also: revert |
| 114 | */ |
| 115 | void update_cmd(void){ |
| 116 | int vid; /* Current version */ |
| 117 | int tid=0; /* Target version - version we are changing to */ |
| 118 | Stmt q; |
| @@ -786,11 +786,11 @@ | |
| 786 | ** |
| 787 | ** Options: |
| 788 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 789 | ** VERSION |
| 790 | ** |
| 791 | ** See also: redo, undo, checkout, update |
| 792 | */ |
| 793 | void revert_cmd(void){ |
| 794 | Manifest *pCoManifest; /* Manifest of current checkout */ |
| 795 | Manifest *pRvManifest; /* Manifest of selected revert version */ |
| 796 | ManifestFile *pCoFile; /* File within current checkout manifest */ |
| 797 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -108,11 +108,11 @@ | |
| 108 | ** checkin which modified them). |
| 109 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 110 | ** used for merging, named *-baseline, *-original, |
| 111 | ** and *-merge. |
| 112 | ** |
| 113 | ** See also: [[revert]] |
| 114 | */ |
| 115 | void update_cmd(void){ |
| 116 | int vid; /* Current version */ |
| 117 | int tid=0; /* Target version - version we are changing to */ |
| 118 | Stmt q; |
| @@ -786,11 +786,11 @@ | |
| 786 | ** |
| 787 | ** Options: |
| 788 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 789 | ** VERSION |
| 790 | ** |
| 791 | ** See also: [[redo]], [[undo]], [[checkout]], [[update]] |
| 792 | */ |
| 793 | void revert_cmd(void){ |
| 794 | Manifest *pCoManifest; /* Manifest of current checkout */ |
| 795 | Manifest *pRvManifest; /* Manifest of selected revert version */ |
| 796 | ManifestFile *pCoFile; /* File within current checkout manifest */ |
| 797 |
+716
-236
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -102,11 +102,10 @@ | ||
| 102 | 102 | " WHERE tagid=%d AND mtime<%.16g" |
| 103 | 103 | " ORDER BY mtime DESC LIMIT 1", |
| 104 | 104 | tagid, mtime); |
| 105 | 105 | } |
| 106 | 106 | |
| 107 | - | |
| 108 | 107 | /* |
| 109 | 108 | ** WEBPAGE: home |
| 110 | 109 | ** WEBPAGE: index |
| 111 | 110 | ** WEBPAGE: not_found |
| 112 | 111 | ** |
| @@ -398,10 +397,24 @@ | ||
| 398 | 397 | if( sqlite3_strglob("tag/*", zPageName)==0 ){ |
| 399 | 398 | return WIKITYPE_TAG; |
| 400 | 399 | } |
| 401 | 400 | return WIKITYPE_NORMAL; |
| 402 | 401 | } |
| 402 | + | |
| 403 | +/* | |
| 404 | +** Returns a JSON-friendly string form of the integer value returned | |
| 405 | +** by wiki_page_type(zPageName). | |
| 406 | +*/ | |
| 407 | +const char * wiki_page_type_name(const char *zPageName){ | |
| 408 | + switch(wiki_page_type(zPageName)){ | |
| 409 | + case WIKITYPE_CHECKIN: return "checkin"; | |
| 410 | + case WIKITYPE_BRANCH: return "branch"; | |
| 411 | + case WIKITYPE_TAG: return "tag"; | |
| 412 | + case WIKITYPE_NORMAL: | |
| 413 | + default: return "normal"; | |
| 414 | + } | |
| 415 | +} | |
| 403 | 416 | |
| 404 | 417 | /* |
| 405 | 418 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 406 | 419 | ** for zPageName. zExtra is an empty string for /wiki but has the text |
| 407 | 420 | ** "Edit: " for /wikiedit. |
| @@ -541,16 +554,11 @@ | ||
| 541 | 554 | zMimetype = wiki_filter_mimetypes(zMimetype); |
| 542 | 555 | if( !g.isHome && !noSubmenu ){ |
| 543 | 556 | if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki)) |
| 544 | 557 | && wiki_special_permission(zPageName) |
| 545 | 558 | ){ |
| 546 | - if( db_get_boolean("wysiwyg-wiki", 0) ){ | |
| 547 | - style_submenu_element("Edit", "%R/wikiedit?name=%T&wysiwyg=1", | |
| 548 | - zPageName); | |
| 549 | - }else{ | |
| 550 | - style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName); | |
| 551 | - } | |
| 559 | + style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName); | |
| 552 | 560 | }else if( rid && g.perm.ApndWiki ){ |
| 553 | 561 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 554 | 562 | } |
| 555 | 563 | if( g.perm.Hyperlink ){ |
| 556 | 564 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| @@ -620,220 +628,692 @@ | ||
| 620 | 628 | return azStyles[i+1]; |
| 621 | 629 | } |
| 622 | 630 | } |
| 623 | 631 | return azStyles[1]; |
| 624 | 632 | } |
| 633 | + | |
| 634 | +/* | |
| 635 | + ** Tries to fetch a wiki page for the given name. If found, it | |
| 636 | + ** returns true, else false. | |
| 637 | + ** | |
| 638 | + ** versionsBack specifies how many versions back in the history to | |
| 639 | + ** fetch. Use 0 for the latest version, 1 for its parent, etc. | |
| 640 | + ** | |
| 641 | + ** If pRid is not NULL then if a result is found *pRid is set to its | |
| 642 | + ** RID. If ppWiki is not NULL then if found *ppWiki is set to the | |
| 643 | + ** loaded wiki object, which the caller is responsible for passing to | |
| 644 | + ** manifest_destroy(). | |
| 645 | + */ | |
| 646 | +static int wiki_fetch_by_name( const char *zPageName, | |
| 647 | + unsigned int versionsBack, | |
| 648 | + int * pRid, Manifest **ppWiki ){ | |
| 649 | + Manifest *pWiki = 0; | |
| 650 | + char *zTag = mprintf("wiki-%s", zPageName); | |
| 651 | + Stmt q = empty_Stmt; | |
| 652 | + int rid = 0; | |
| 653 | + | |
| 654 | + db_prepare(&q, "SELECT rid FROM tagxref" | |
| 655 | + " WHERE tagid=(SELECT tagid FROM tag WHERE" | |
| 656 | + " tagname=%Q) " | |
| 657 | + " ORDER BY mtime DESC LIMIT -1 OFFSET %u", zTag, | |
| 658 | + versionsBack); | |
| 659 | + fossil_free(zTag); | |
| 660 | + if(SQLITE_ROW == db_step(&q)){ | |
| 661 | + rid = db_column_int(&q, 0); | |
| 662 | + } | |
| 663 | + db_finalize(&q); | |
| 664 | + if( rid == 0 ){ | |
| 665 | + return 0; | |
| 666 | + } | |
| 667 | + else if(pRid){ | |
| 668 | + *pRid = rid; | |
| 669 | + } | |
| 670 | + if(ppWiki){ | |
| 671 | + pWiki = manifest_get(rid, CFTYPE_WIKI, 0); | |
| 672 | + if( pWiki==0 ){ | |
| 673 | + /* "Cannot happen." */ | |
| 674 | + return 0; | |
| 675 | + } | |
| 676 | + *ppWiki = pWiki; | |
| 677 | + } | |
| 678 | + return 1; | |
| 679 | +} | |
| 680 | + | |
| 681 | +/* | |
| 682 | +** Determines whether the wiki page with the given name can be edited | |
| 683 | +** or created by the current user. If not, an AJAX error is queued and | |
| 684 | +** false is returned, else true is returned. A NULL, empty, or | |
| 685 | +** malformed name is considered non-writable, regardless of the user. | |
| 686 | +** | |
| 687 | +** If pRid is not NULL then this function writes the page's rid to | |
| 688 | +** *pRid (whether or not access is granted). On error or if the page | |
| 689 | +** does not yet exist, *pRid will be set to 0. | |
| 690 | +** | |
| 691 | +** Note that the sandbox is a special case: it is a pseudo-page with | |
| 692 | +** no rid and the /wikiajax API does not allow anyone to actually save | |
| 693 | +** a sandbox page, but it is reported as writable here (with rid 0). | |
| 694 | +*/ | |
| 695 | +static int wiki_ajax_can_write(const char *zPageName, int * pRid){ | |
| 696 | + int rid = 0; | |
| 697 | + const char * zErr = 0; | |
| 698 | + | |
| 699 | + if(pRid) *pRid = 0; | |
| 700 | + if(!zPageName || !*zPageName | |
| 701 | + || !wiki_name_is_wellformed((unsigned const char *)zPageName)){ | |
| 702 | + zErr = "Invalid page name."; | |
| 703 | + }else if(is_sandbox(zPageName)){ | |
| 704 | + return 1; | |
| 705 | + }else{ | |
| 706 | + wiki_fetch_by_name(zPageName, 0, &rid, 0); | |
| 707 | + if(pRid) *pRid = rid; | |
| 708 | + if(!wiki_special_permission(zPageName)){ | |
| 709 | + zErr = "Editing this page requires non-wiki write permissions."; | |
| 710 | + }else if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){ | |
| 711 | + return 3; | |
| 712 | + }else if(rid && !g.perm.WrWiki){ | |
| 713 | + zErr = "Requires wiki-write permissions."; | |
| 714 | + }else if(!rid && !g.perm.NewWiki){ | |
| 715 | + zErr = "Requires new-wiki permissions."; | |
| 716 | + }else{ | |
| 717 | + zErr = "Cannot happen! Please report this as a bug."; | |
| 718 | + } | |
| 719 | + } | |
| 720 | + ajax_route_error(403, "%s", zErr); | |
| 721 | + return 0; | |
| 722 | +} | |
| 723 | + | |
| 724 | +/* | |
| 725 | +** Loads the given wiki page, sets the response type to | |
| 726 | +** application/json, and emits it as a JSON object. If zPageName is a | |
| 727 | +** sandbox page then a "fake" object is emitted, as the wikiajax API | |
| 728 | +** does not permit saving the sandbox. | |
| 729 | +** | |
| 730 | +** Returns true on success, false on error, and on error it | |
| 731 | +** queues up a JSON-format error response. | |
| 732 | +** | |
| 733 | +** Output JSON format: | |
| 734 | +** | |
| 735 | +** { name: "page name", | |
| 736 | +** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", | |
| 737 | +** mimetype: "mime type", | |
| 738 | +** version: UUID string or null for a sandbox page, | |
| 739 | +** parent: "parent uuid" or null if no parent, | |
| 740 | +** isDeleted: true if the page has no content (is "deleted") | |
| 741 | +** else not set (making it "falsy" in JS), | |
| 742 | +** content: "page content" (only if includeContent is true) | |
| 743 | +** } | |
| 744 | +** | |
| 745 | +** If includeContent is false then the content member is elided. | |
| 746 | +*/ | |
| 747 | +static int wiki_ajax_emit_page_object(const char *zPageName, | |
| 748 | + int includeContent){ | |
| 749 | + Manifest * pWiki = 0; | |
| 750 | + char * zUuid; | |
| 751 | + | |
| 752 | + if( is_sandbox(zPageName) ){ | |
| 753 | + char * zMimetype = | |
| 754 | + db_get("sandbox-mimetype","text/x-fossil-wiki"); | |
| 755 | + char * zBody = db_get("sandbox",""); | |
| 756 | + CX("{\"name\": %!j, \"type\": \"sandbox\", " | |
| 757 | + "\"mimetype\": %!j, \"version\": null, \"parent\": null", | |
| 758 | + zPageName, zMimetype); | |
| 759 | + if(includeContent){ | |
| 760 | + CX(", \"content\": %!j", | |
| 761 | + zBody); | |
| 762 | + } | |
| 763 | + CX("}"); | |
| 764 | + fossil_free(zMimetype); | |
| 765 | + fossil_free(zBody); | |
| 766 | + return 1; | |
| 767 | + }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ | |
| 768 | + ajax_route_error(404, "Wiki page could not be loaded: %s", | |
| 769 | + zPageName); | |
| 770 | + return 0; | |
| 771 | + }else{ | |
| 772 | + zUuid = rid_to_uuid(pWiki->rid); | |
| 773 | + CX("{\"name\": %!j, \"type\": %!j, " | |
| 774 | + "\"version\": %!j, " | |
| 775 | + "\"mimetype\": %!j, ", | |
| 776 | + pWiki->zWikiTitle, | |
| 777 | + wiki_page_type_name(pWiki->zWikiTitle), | |
| 778 | + zUuid, | |
| 779 | + pWiki->zMimetype ? pWiki->zMimetype : "text/x-fossil-wiki"); | |
| 780 | + CX("\"parent\": "); | |
| 781 | + if(pWiki->nParent){ | |
| 782 | + CX("%!j", pWiki->azParent[0]); | |
| 783 | + }else{ | |
| 784 | + CX("null"); | |
| 785 | + } | |
| 786 | + if(!pWiki->zWiki || !pWiki->zWiki[0]){ | |
| 787 | + CX(", \"isEmpty\": true"); | |
| 788 | + } | |
| 789 | + if(includeContent){ | |
| 790 | + CX(", \"content\": %!j", pWiki->zWiki); | |
| 791 | + } | |
| 792 | + CX("}"); | |
| 793 | + fossil_free(zUuid); | |
| 794 | + manifest_destroy(pWiki); | |
| 795 | + return 2; | |
| 796 | + } | |
| 797 | +} | |
| 798 | + | |
| 799 | +/* | |
| 800 | +** Ajax route handler for /wikiajax/save. | |
| 801 | +** | |
| 802 | +** URL params: | |
| 803 | +** | |
| 804 | +** page = the wiki page name. | |
| 805 | +** mimetype = content mime type. | |
| 806 | +** content = page content. Fossil considers an empty page to | |
| 807 | +** be "deleted". | |
| 808 | +** isnew = 1 if the page is to be newly-created, else 0 or | |
| 809 | +** not send. | |
| 810 | +** | |
| 811 | +** Responds with JSON. On error, an object in the form documented by | |
| 812 | +** ajax_route_error(). On success, an object in the form documented | |
| 813 | +** for wiki_ajax_emit_page_object(). | |
| 814 | +** | |
| 815 | +** The wikiajax API disallows saving of a sandbox pseudo-page, and | |
| 816 | +** will respond with an error if asked to save one. Should we want to | |
| 817 | +** enable it, it's implemented like this for any saved page for which | |
| 818 | +** is_sandbox(zPageName) is true: | |
| 819 | +** | |
| 820 | +** db_set("sandbox",zBody,0); | |
| 821 | +** db_set("sandbox-mimetype",zMimetype,0); | |
| 822 | +** | |
| 823 | +*/ | |
| 824 | +static void wiki_ajax_route_save(void){ | |
| 825 | + const char *zPageName = P("page"); | |
| 826 | + const char *zMimetype = P("mimetype"); | |
| 827 | + const char *zContent = P("content"); | |
| 828 | + const int isNew = ajax_p_bool("isnew"); | |
| 829 | + Blob content = empty_blob; | |
| 830 | + int parentRid = 0; | |
| 831 | + int rollback = 0; | |
| 832 | + | |
| 833 | + if(!wiki_ajax_can_write(zPageName, &parentRid)){ | |
| 834 | + return; | |
| 835 | + }else if(is_sandbox(zPageName)){ | |
| 836 | + ajax_route_error(403,"Saving a sandbox page is prohibited."); | |
| 837 | + return; | |
| 838 | + } | |
| 839 | + /* These isNew checks are just me being pedantic. We could just as | |
| 840 | + easily derive isNew based on whether or not the page already | |
| 841 | + exists. */ | |
| 842 | + if(isNew){ | |
| 843 | + if(parentRid>0){ | |
| 844 | + ajax_route_error(403,"Requested a new page, " | |
| 845 | + "but it already exists with RID %d: %s", | |
| 846 | + parentRid, zPageName); | |
| 847 | + return; | |
| 848 | + } | |
| 849 | + }else if(parentRid==0){ | |
| 850 | + ajax_route_error(403,"Creating new page [%s] requires passing " | |
| 851 | + "isnew=1.", zPageName); | |
| 852 | + return; | |
| 853 | + } | |
| 854 | + blob_init(&content, zContent ? zContent : "", -1); | |
| 855 | + cgi_set_content_type("application/json"); | |
| 856 | + db_begin_transaction(); | |
| 857 | + wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); | |
| 858 | + rollback = wiki_ajax_emit_page_object(zPageName, 1) ? 0 : 1; | |
| 859 | + db_end_transaction(rollback); | |
| 860 | +} | |
| 861 | + | |
| 862 | +/* | |
| 863 | +** Ajax route handler for /wikiajax/fetch. | |
| 864 | +** | |
| 865 | +** URL params: | |
| 866 | +** | |
| 867 | +** page = the wiki page name | |
| 868 | +** | |
| 869 | +** Responds with JSON. On error, an object in the form documented by | |
| 870 | +** ajax_route_error(). On success, an object in the form documented | |
| 871 | +** for wiki_ajax_emit_page_object(). | |
| 872 | +*/ | |
| 873 | +static void wiki_ajax_route_fetch(void){ | |
| 874 | + const char * zPageName = P("page"); | |
| 875 | + | |
| 876 | + if( zPageName==0 || zPageName[0]==0 ){ | |
| 877 | + ajax_route_error(400,"Missing page name."); | |
| 878 | + return; | |
| 879 | + } | |
| 880 | + cgi_set_content_type("application/json"); | |
| 881 | + wiki_ajax_emit_page_object(zPageName, 1); | |
| 882 | +} | |
| 883 | + | |
| 884 | +/* | |
| 885 | +** Ajax route handler for /wikiajax/diff. | |
| 886 | +** | |
| 887 | +** URL params: | |
| 888 | +** | |
| 889 | +** page = the wiki page name | |
| 890 | +** content = the new/edited wiki page content | |
| 891 | +** | |
| 892 | +** Requires that the user have write access solely to avoid some | |
| 893 | +** potential abuse cases. It does not actually write anything. | |
| 894 | +*/ | |
| 895 | +static void wiki_ajax_route_diff(void){ | |
| 896 | + const char * zPageName = P("page"); | |
| 897 | + Blob contentNew = empty_blob, contentOrig = empty_blob; | |
| 898 | + Manifest * pParent = 0; | |
| 899 | + const char * zContent = P("content"); | |
| 900 | + u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR; | |
| 901 | + | |
| 902 | + if( zPageName==0 || zPageName[0]==0 ){ | |
| 903 | + ajax_route_error(400,"Missing page name."); | |
| 904 | + return; | |
| 905 | + }else if(!wiki_ajax_can_write(zPageName, 0)){ | |
| 906 | + return; | |
| 907 | + } | |
| 908 | + switch(atoi(PD("sbs","0"))){ | |
| 909 | + case 0: diffFlags |= DIFF_LINENO; break; | |
| 910 | + default: diffFlags |= DIFF_SIDEBYSIDE; | |
| 911 | + } | |
| 912 | + switch(atoi(PD("ws","2"))){ | |
| 913 | + case 1: diffFlags |= DIFF_IGNORE_EOLWS; break; | |
| 914 | + case 2: diffFlags |= DIFF_IGNORE_ALLWS; break; | |
| 915 | + default: break; | |
| 916 | + } | |
| 917 | + wiki_fetch_by_name( zPageName, 0, 0, &pParent ); | |
| 918 | + if( pParent && pParent->zWiki && *pParent->zWiki ){ | |
| 919 | + blob_init(&contentOrig, pParent->zWiki, -1); | |
| 920 | + }else{ | |
| 921 | + blob_init(&contentOrig, "", 0); | |
| 922 | + } | |
| 923 | + blob_init(&contentNew, zContent ? zContent : "", -1); | |
| 924 | + cgi_set_content_type("text/html"); | |
| 925 | + ajax_render_diff(&contentOrig, &contentNew, diffFlags); | |
| 926 | + blob_reset(&contentNew); | |
| 927 | + blob_reset(&contentOrig); | |
| 928 | + manifest_destroy(pParent); | |
| 929 | +} | |
| 930 | + | |
| 931 | +/* | |
| 932 | +** Ajax route handler for /wikiajax/preview. | |
| 933 | +** | |
| 934 | +** URL params: | |
| 935 | +** | |
| 936 | +** page = wiki page name. This is only needed for authorization | |
| 937 | +** checking. | |
| 938 | +** mimetype = the wiki page mimetype (determines rendering style) | |
| 939 | +** content = the wiki page content | |
| 940 | +*/ | |
| 941 | +static void wiki_ajax_route_preview(void){ | |
| 942 | + const char * zPageName = P("page"); | |
| 943 | + const char * zContent = P("content"); | |
| 944 | + | |
| 945 | + if(!wiki_ajax_can_write(zPageName, 0)){ | |
| 946 | + return; | |
| 947 | + }else if( zContent==0 ){ | |
| 948 | + ajax_route_error(400,"Missing content to preview."); | |
| 949 | + return; | |
| 950 | + }else{ | |
| 951 | + Blob content = empty_blob; | |
| 952 | + const char * zMimetype = PD("mimetype","text/x-fossil-wiki"); | |
| 953 | + | |
| 954 | + blob_init(&content, zContent, -1); | |
| 955 | + cgi_set_content_type("text/html"); | |
| 956 | + wiki_render_by_mimetype(&content, zMimetype); | |
| 957 | + blob_reset(&content); | |
| 958 | + } | |
| 959 | +} | |
| 960 | + | |
| 961 | +/* | |
| 962 | +** Outputs the wiki page list in JSON form. If verbose is false then | |
| 963 | +** it emits an array of strings (page names). If verbose is true it outputs | |
| 964 | +** an array of objects in this form: | |
| 965 | +** | |
| 966 | +** { name: string, version: string or null of sandbox box, | |
| 967 | +** parent: uuid or null for first version or sandbox, | |
| 968 | +** mimetype: string, | |
| 969 | +** type: string (normal, branch, tag, checkin, or sandbox) | |
| 970 | +** } | |
| 971 | +** | |
| 972 | +** If includeContent is true, the object contains a "content" member | |
| 973 | +** with the raw page content. includeContent is ignored if verbose is | |
| 974 | +** false. | |
| 975 | +** | |
| 976 | +*/ | |
| 977 | +static void wiki_render_page_list_json(int verbose, int includeContent){ | |
| 978 | + Stmt q = empty_Stmt; | |
| 979 | + int n = 0; | |
| 980 | + db_begin_transaction(); | |
| 981 | + db_prepare(&q, "SELECT" | |
| 982 | + " substr(tagname,6) AS name" | |
| 983 | + " FROM tag WHERE tagname GLOB 'wiki-*'" | |
| 984 | + " UNION SELECT 'Sandbox' AS name" | |
| 985 | + " ORDER BY name COLLATE NOCASE"); | |
| 986 | + CX("["); | |
| 987 | + while( SQLITE_ROW==db_step(&q) ){ | |
| 988 | + char const * zName = db_column_text(&q,0); | |
| 989 | + if(n++){ | |
| 990 | + CX(","); | |
| 991 | + } | |
| 992 | + if(verbose==0){ | |
| 993 | + CX("%!j", zName); | |
| 994 | + }else{ | |
| 995 | + wiki_ajax_emit_page_object(zName, includeContent); | |
| 996 | + } | |
| 997 | + } | |
| 998 | + CX("]"); | |
| 999 | + db_finalize(&q); | |
| 1000 | + db_end_transaction(0); | |
| 1001 | +} | |
| 1002 | + | |
| 1003 | +/* | |
| 1004 | +** Ajax route handler for /wikiajax/list. | |
| 1005 | +** | |
| 1006 | +** Optional parameters: verbose, includeContent (see below). | |
| 1007 | +** | |
| 1008 | +** Responds with JSON. On error, an object in the form documented by | |
| 1009 | +** ajax_route_error(). | |
| 1010 | +** | |
| 1011 | +** On success, it emits an array of strings (page names) sorted | |
| 1012 | +** case-insensitively. If the "verbose" parameter is passed in then | |
| 1013 | +** the result list contains objects in the format documented for | |
| 1014 | +** wiki_ajax_emit_page_object(). The content of each object is elided | |
| 1015 | +** unless the "includeContent" parameter is passed on with a | |
| 1016 | +** "non-false" value.. | |
| 1017 | +** | |
| 1018 | +** The result list always contains an entry named "Sandbox" which | |
| 1019 | +** represents the sandbox pseudo-page. | |
| 1020 | +*/ | |
| 1021 | +static void wiki_ajax_route_list(void){ | |
| 1022 | + const int verbose = ajax_p_bool("verbose"); | |
| 1023 | + const int includeContent = ajax_p_bool("includeContent"); | |
| 1024 | + | |
| 1025 | + cgi_set_content_type("application/json"); | |
| 1026 | + wiki_render_page_list_json(verbose, includeContent); | |
| 1027 | +} | |
| 1028 | + | |
| 1029 | +/* | |
| 1030 | +** WEBPAGE: wikiajax | |
| 1031 | +** | |
| 1032 | +** An internal dispatcher for wiki AJAX operations. Not for direct | |
| 1033 | +** client use. All routes defined by this interface are app-internal, | |
| 1034 | +** subject to change | |
| 1035 | +*/ | |
| 1036 | +void wiki_ajax_page(void){ | |
| 1037 | + const char * zName = P("name"); | |
| 1038 | + AjaxRoute routeName = {0,0,0,0}; | |
| 1039 | + const AjaxRoute * pRoute = 0; | |
| 1040 | + const AjaxRoute routes[] = { | |
| 1041 | + /* Keep these sorted by zName (for bsearch()) */ | |
| 1042 | + {"diff", wiki_ajax_route_diff, 1, 1}, | |
| 1043 | + {"fetch", wiki_ajax_route_fetch, 0, 0}, | |
| 1044 | + {"list", wiki_ajax_route_list, 0, 0}, | |
| 1045 | + {"preview", wiki_ajax_route_preview, 0, 1} | |
| 1046 | + /* preview access mode: whether or not wiki-write mode is needed | |
| 1047 | + really depends on multiple factors. e.g. the sandbox page does | |
| 1048 | + not normally require more than anonymous access. We set its | |
| 1049 | + write-mode to false and do those checks manually in that route's | |
| 1050 | + handler. | |
| 1051 | + */, | |
| 1052 | + {"save", wiki_ajax_route_save, 1, 1} | |
| 1053 | + }; | |
| 1054 | + | |
| 1055 | + if(zName==0 || zName[0]==0){ | |
| 1056 | + ajax_route_error(400,"Missing required [route] 'name' parameter."); | |
| 1057 | + return; | |
| 1058 | + } | |
| 1059 | + routeName.zName = zName; | |
| 1060 | + pRoute = (const AjaxRoute *)bsearch(&routeName, routes, | |
| 1061 | + count(routes), sizeof routes[0], | |
| 1062 | + cmp_ajax_route_name); | |
| 1063 | + if(pRoute==0){ | |
| 1064 | + ajax_route_error(404,"Ajax route not found."); | |
| 1065 | + return; | |
| 1066 | + } | |
| 1067 | + login_check_credentials(); | |
| 1068 | + if( pRoute->bWriteMode!=0 && g.perm.WrWiki==0 ){ | |
| 1069 | + ajax_route_error(403,"Write permissions required."); | |
| 1070 | + return; | |
| 1071 | + }else if(0==cgi_csrf_safe(pRoute->bPost)){ | |
| 1072 | + ajax_route_error(403, | |
| 1073 | + "CSRF violation (make sure sending of HTTP " | |
| 1074 | + "Referer headers is enabled for XHR " | |
| 1075 | + "connections)."); | |
| 1076 | + return; | |
| 1077 | + } | |
| 1078 | + pRoute->xCallback(); | |
| 1079 | +} | |
| 625 | 1080 | |
| 626 | 1081 | /* |
| 627 | 1082 | ** WEBPAGE: wikiedit |
| 628 | -** URL: /wikiedit?name=PAGENAME | |
| 1083 | +** URL: /wikedit?name=PAGENAME | |
| 1084 | +** | |
| 1085 | +** The main front-end for the Ajax-based wiki editor app. Passing | |
| 1086 | +** in the name of an unknown page will trigger the creation | |
| 1087 | +** of a new page (which is not actually created in the database | |
| 1088 | +** until the user explicitly saves it). If passed no page name, | |
| 1089 | +** the user may select a page from the list on the first UI tab. | |
| 629 | 1090 | ** |
| 630 | -** Edit a wiki page. | |
| 1091 | +** When creating a new page, the mimetype URL parameter may optionally | |
| 1092 | +** be used to set its mimetype to one of text/x-fossil-wiki, | |
| 1093 | +** text/x-markdown, or text/plain, defauling to the former. | |
| 631 | 1094 | */ |
| 632 | 1095 | void wikiedit_page(void){ |
| 633 | - char *zTag; | |
| 634 | - int rid = 0; | |
| 1096 | + const char *zPageName; | |
| 1097 | + const char * zMimetype = P("mimetype"); | |
| 635 | 1098 | int isSandbox; |
| 636 | - Blob wiki; | |
| 637 | - Manifest *pWiki = 0; | |
| 638 | - const char *zPageName; | |
| 639 | - int n; | |
| 640 | - const char *z; | |
| 641 | - char *zBody = (char*)P("w"); | |
| 642 | - const char *zMimetype = wiki_filter_mimetypes(P("mimetype")); | |
| 643 | - int isWysiwyg = P("wysiwyg")!=0; | |
| 644 | - int goodCaptcha = 1; | |
| 645 | - int eType = WIKITYPE_UNKNOWN; | |
| 646 | - int havePreview = 0; | |
| 647 | - | |
| 648 | - if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; } | |
| 649 | - if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; } | |
| 650 | - if( zBody ){ | |
| 651 | - if( isWysiwyg ){ | |
| 652 | - Blob body; | |
| 653 | - blob_zero(&body); | |
| 654 | - htmlTidy(zBody, &body); | |
| 655 | - zBody = blob_str(&body); | |
| 656 | - }else{ | |
| 657 | - zBody = mprintf("%s", zBody); | |
| 658 | - } | |
| 659 | - } | |
| 1099 | + int found = 0; | |
| 1100 | + | |
| 660 | 1101 | login_check_credentials(); |
| 661 | 1102 | zPageName = PD("name",""); |
| 662 | - if( check_name(zPageName) ) return; | |
| 1103 | + if(zPageName && *zPageName){ | |
| 1104 | + if( check_name(zPageName) ) return; | |
| 1105 | + } | |
| 663 | 1106 | isSandbox = is_sandbox(zPageName); |
| 664 | 1107 | if( isSandbox ){ |
| 665 | 1108 | if( !g.perm.WrWiki ){ |
| 666 | 1109 | login_needed(g.anon.WrWiki); |
| 667 | 1110 | return; |
| 668 | 1111 | } |
| 669 | - if( zBody==0 ){ | |
| 670 | - zBody = db_get("sandbox",""); | |
| 671 | - zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); | |
| 672 | - } | |
| 673 | - }else{ | |
| 674 | - zTag = mprintf("wiki-%s", zPageName); | |
| 675 | - rid = db_int(0, | |
| 676 | - "SELECT rid FROM tagxref" | |
| 677 | - " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" | |
| 678 | - " ORDER BY mtime DESC", zTag | |
| 679 | - ); | |
| 680 | - free(zTag); | |
| 1112 | + found = 1; | |
| 1113 | + }else if( zPageName!=0 ){ | |
| 1114 | + int rid = 0; | |
| 681 | 1115 | if( !wiki_special_permission(zPageName) ){ |
| 682 | 1116 | login_needed(0); |
| 683 | 1117 | return; |
| 684 | 1118 | } |
| 1119 | + found = wiki_fetch_by_name(zPageName, 0, &rid, 0); | |
| 685 | 1120 | if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){ |
| 686 | 1121 | login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki); |
| 687 | 1122 | return; |
| 688 | 1123 | } |
| 689 | - if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ | |
| 690 | - zBody = pWiki->zWiki; | |
| 691 | - zMimetype = pWiki->zMimetype; | |
| 692 | - } | |
| 693 | - } | |
| 694 | - if( P("submit")!=0 && zBody!=0 | |
| 695 | - && (goodCaptcha = captcha_is_correct(0)) | |
| 696 | - ){ | |
| 697 | - char *zDate; | |
| 698 | - Blob cksum; | |
| 699 | - blob_zero(&wiki); | |
| 700 | - db_begin_transaction(); | |
| 701 | - if( isSandbox ){ | |
| 702 | - db_set("sandbox",zBody,0); | |
| 703 | - db_set("sandbox-mimetype",zMimetype,0); | |
| 704 | - }else{ | |
| 705 | - login_verify_csrf_secret(); | |
| 706 | - zDate = date_in_standard_format("now"); | |
| 707 | - blob_appendf(&wiki, "D %s\n", zDate); | |
| 708 | - free(zDate); | |
| 709 | - blob_appendf(&wiki, "L %F\n", zPageName); | |
| 710 | - if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")!=0 ){ | |
| 711 | - blob_appendf(&wiki, "N %s\n", zMimetype); | |
| 712 | - } | |
| 713 | - if( rid ){ | |
| 714 | - char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 715 | - blob_appendf(&wiki, "P %s\n", zUuid); | |
| 716 | - free(zUuid); | |
| 717 | - } | |
| 718 | - if( !login_is_nobody() ){ | |
| 719 | - blob_appendf(&wiki, "U %F\n", login_name()); | |
| 720 | - } | |
| 721 | - blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody); | |
| 722 | - md5sum_blob(&wiki, &cksum); | |
| 723 | - blob_appendf(&wiki, "Z %b\n", &cksum); | |
| 724 | - blob_reset(&cksum); | |
| 725 | - wiki_put(&wiki, 0, wiki_need_moderation(0)); | |
| 726 | - } | |
| 727 | - db_end_transaction(0); | |
| 728 | - cgi_redirectf("wiki?name=%T", zPageName); | |
| 729 | - } | |
| 730 | - if( P("cancel")!=0 ){ | |
| 731 | - cgi_redirectf("wiki?name=%T", zPageName); | |
| 732 | - return; | |
| 733 | - } | |
| 734 | - if( zBody==0 ){ | |
| 735 | - zBody = mprintf(""); | |
| 736 | - } | |
| 737 | - style_set_current_page("%T?name=%T", g.zPath, zPageName); | |
| 738 | - eType = wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "Edit: "); | |
| 739 | - if( rid && !isSandbox && g.perm.ApndWiki ){ | |
| 740 | - if( g.perm.Attach ){ | |
| 741 | - style_submenu_element("Attach", | |
| 742 | - "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T", | |
| 743 | - g.zTop, zPageName, g.zTop, zPageName); | |
| 744 | - } | |
| 745 | - } | |
| 746 | - if( !goodCaptcha ){ | |
| 747 | - @ <p class="generalError">Error: Incorrect security code.</p> | |
| 748 | - } | |
| 749 | - blob_zero(&wiki); | |
| 750 | - while( fossil_isspace(zBody[0]) ) zBody++; | |
| 751 | - blob_append(&wiki, zBody, -1); | |
| 752 | - if( P("preview")!=0 ){ | |
| 753 | - havePreview = 1; | |
| 754 | - if( zBody[0] ){ | |
| 755 | - @ Preview:<hr /> | |
| 756 | - safe_html_context(DOCSRC_WIKI); | |
| 757 | - wiki_render_by_mimetype(&wiki, zMimetype); | |
| 758 | - @ <hr /> | |
| 759 | - blob_reset(&wiki); | |
| 760 | - } | |
| 761 | - } | |
| 762 | - for(n=2, z=zBody; z[0]; z++){ | |
| 763 | - if( z[0]=='\n' ) n++; | |
| 764 | - } | |
| 765 | - if( n<20 ) n = 20; | |
| 766 | - if( n>30 ) n = 30; | |
| 767 | - if( !isWysiwyg ){ | |
| 768 | - /* Traditional markup-only editing */ | |
| 769 | - char *zPlaceholder = 0; | |
| 770 | - switch( eType ){ | |
| 771 | - case WIKITYPE_NORMAL: { | |
| 772 | - zPlaceholder = mprintf("Enter text for wiki page %s", zPageName); | |
| 773 | - break; | |
| 774 | - } | |
| 775 | - case WIKITYPE_BRANCH: { | |
| 776 | - zPlaceholder = mprintf("Enter notes about branch %s", zPageName+7); | |
| 777 | - break; | |
| 778 | - } | |
| 779 | - case WIKITYPE_CHECKIN: { | |
| 780 | - zPlaceholder = mprintf("Enter notes about check-in %.20s", zPageName+8); | |
| 781 | - break; | |
| 782 | - } | |
| 783 | - case WIKITYPE_TAG: { | |
| 784 | - zPlaceholder = mprintf("Enter notes about tag %s", zPageName+4); | |
| 785 | - break; | |
| 786 | - } | |
| 787 | - } | |
| 788 | - form_begin(0, "%R/wikiedit"); | |
| 789 | - @ <div>%z(href("%R/markup_help"))Markup style</a>: | |
| 790 | - mimetype_option_menu(zMimetype); | |
| 791 | - @ <br /><textarea name="w" class="wikiedit" cols="80" \ | |
| 792 | - @ rows="%d(n)" wrap="virtual" placeholder="%h(zPlaceholder)">\ | |
| 793 | - @ %h(zBody)</textarea> | |
| 794 | - @ <br /> | |
| 795 | - fossil_free(zPlaceholder); | |
| 796 | - if( db_get_boolean("wysiwyg-wiki", 0) ){ | |
| 797 | - @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor" | |
| 798 | - @ onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' /> | |
| 799 | - } | |
| 800 | - @ <input type="submit" name="preview" value="Preview Your Changes" /> | |
| 801 | - }else{ | |
| 802 | - /* Wysiwyg editing */ | |
| 803 | - Blob html, temp; | |
| 804 | - havePreview = 1; | |
| 805 | - form_begin("", "%R/wikiedit"); | |
| 806 | - @ <div> | |
| 807 | - @ <input type="hidden" name="wysiwyg" value="1" /> | |
| 808 | - blob_zero(&temp); | |
| 809 | - wiki_convert(&wiki, &temp, 0); | |
| 810 | - blob_zero(&html); | |
| 811 | - htmlTidy(blob_str(&temp), &html); | |
| 812 | - blob_reset(&temp); | |
| 813 | - wysiwygEditor("w", blob_str(&html), 60, n); | |
| 814 | - blob_reset(&html); | |
| 815 | - @ <br /> | |
| 816 | - @ <input type="submit" name="edit-markup" value="Markup Editor" | |
| 817 | - @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' /> | |
| 818 | - } | |
| 819 | - login_insert_csrf_secret(); | |
| 820 | - if( havePreview ){ | |
| 821 | - if( isWysiwyg || zBody[0] ){ | |
| 822 | - @ <input type="submit" name="submit" value="Apply These Changes" /> | |
| 823 | - }else{ | |
| 824 | - @ <input type="submit" name="submit" value="Delete This Wiki Page" /> | |
| 825 | - } | |
| 826 | - } | |
| 827 | - @ <input type="hidden" name="name" value="%h(zPageName)" /> | |
| 828 | - @ <input type="submit" name="cancel" value="Cancel" | |
| 829 | - @ onclick='confirm("Abandon your changes?")' /> | |
| 830 | - @ </div> | |
| 831 | - captcha_generate(0); | |
| 832 | - @ </form> | |
| 833 | - manifest_destroy(pWiki); | |
| 834 | - blob_reset(&wiki); | |
| 1124 | + } | |
| 1125 | + style_header("Wiki Editor"); | |
| 1126 | + | |
| 1127 | + /* Status bar */ | |
| 1128 | + CX("<div id='fossil-status-bar' " | |
| 1129 | + "title='Status message area. Double-click to clear them.'>" | |
| 1130 | + "Status messages will go here.</div>\n" | |
| 1131 | + /* will be moved into the tab container via JS */); | |
| 1132 | + | |
| 1133 | + CX("<div id='wikiedit-edit-status''>" | |
| 1134 | + "<span class='name'></span>" | |
| 1135 | + "<span class='links'></span>" | |
| 1136 | + "</div>"); | |
| 1137 | + | |
| 1138 | + /* Main tab container... */ | |
| 1139 | + CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>"); | |
| 1140 | + /* The .hidden class on the following tab elements is to help lessen | |
| 1141 | + the FOUC effect of the tabs before JS re-assembles them. */ | |
| 1142 | + | |
| 1143 | + /******* Page list *******/ | |
| 1144 | + { | |
| 1145 | + CX("<div id='wikiedit-tab-pages' " | |
| 1146 | + "data-tab-parent='wikiedit-tabs' " | |
| 1147 | + "data-tab-label='Wiki Page List' " | |
| 1148 | + "class='hidden'" | |
| 1149 | + ">"); | |
| 1150 | + CX("<div>Loading wiki pages list...</div>"); | |
| 1151 | + CX("</div>"/*#wikiedit-tab-pages*/); | |
| 1152 | + } | |
| 1153 | + | |
| 1154 | + /******* Content tab *******/ | |
| 1155 | + { | |
| 1156 | + CX("<div id='wikiedit-tab-content' " | |
| 1157 | + "data-tab-parent='wikiedit-tabs' " | |
| 1158 | + "data-tab-label='Editor' " | |
| 1159 | + "class='hidden'" | |
| 1160 | + ">"); | |
| 1161 | + CX("<div class='flex-container flex-row child-gap-small'>"); | |
| 1162 | + CX("<span class='input-with-label'>" | |
| 1163 | + "<label>Mime type</label>"); | |
| 1164 | + mimetype_option_menu(0); | |
| 1165 | + CX("</span>"); | |
| 1166 | + style_select_list_int("select-font-size", | |
| 1167 | + "editor_font_size", "Editor font size", | |
| 1168 | + NULL/*tooltip*/, | |
| 1169 | + 100, | |
| 1170 | + "100%", 100, "125%", 125, | |
| 1171 | + "150%", 150, "175%", 175, | |
| 1172 | + "200%", 200, NULL); | |
| 1173 | + CX("<button class='wikiedit-save'>" | |
| 1174 | + "Save</button>" | |
| 1175 | + /*will get moved around dynamically*/); | |
| 1176 | + CX("<button class='wikiedit-save-close'>" | |
| 1177 | + "Save & Close</button>"/*will get moved around dynamically*/); | |
| 1178 | + CX("<span class='save-button-slot'></span>"); | |
| 1179 | + CX("<button class='wikiedit-content-reload' " | |
| 1180 | + "title='Reload the file from the server, discarding " | |
| 1181 | + "any local edits. To help avoid accidental loss of " | |
| 1182 | + "edits, it requires confirmation (a second click) within " | |
| 1183 | + "a few seconds or it will not reload.'" | |
| 1184 | + ">Discard & Reload</button>"); | |
| 1185 | + CX("</div>"); | |
| 1186 | + CX("<div class='flex-container flex-column stretch'>"); | |
| 1187 | + CX("<textarea name='content' id='wikiedit-content-editor' " | |
| 1188 | + "class='wikiedit' rows='25'>"); | |
| 1189 | + CX("</textarea>"); | |
| 1190 | + CX("</div>"/*textarea wrapper*/); | |
| 1191 | + CX("</div>"/*#tab-file-content*/); | |
| 1192 | + } | |
| 1193 | + /****** Preview tab ******/ | |
| 1194 | + { | |
| 1195 | + CX("<div id='wikiedit-tab-preview' " | |
| 1196 | + "data-tab-parent='wikiedit-tabs' " | |
| 1197 | + "data-tab-label='Preview' " | |
| 1198 | + "class='hidden'" | |
| 1199 | + ">"); | |
| 1200 | + CX("<div class='wikiedit-options flex-container " | |
| 1201 | + "flex-row child-gap-small'>"); | |
| 1202 | + CX("<button id='btn-preview-refresh' " | |
| 1203 | + "data-f-preview-from='wikiContent' " | |
| 1204 | + /* ^^^ fossil.page[methodName]() OR text source elem ID, | |
| 1205 | + ** but we need a method in order to support clients swapping out | |
| 1206 | + ** the text editor with their own. */ | |
| 1207 | + "data-f-preview-via='_postPreview' " | |
| 1208 | + /* ^^^ fossil.page[methodName](content, callback) */ | |
| 1209 | + "data-f-preview-to='#wikiedit-tab-preview-wrapper' " | |
| 1210 | + /* ^^^ dest elem ID */ | |
| 1211 | + ">Refresh</button>"); | |
| 1212 | + /* Toggle auto-update of preview when the Preview tab is selected. */ | |
| 1213 | + style_labeled_checkbox("cb-preview-autoupdate", | |
| 1214 | + NULL, | |
| 1215 | + "Auto-refresh?", | |
| 1216 | + "1", 1, | |
| 1217 | + "If on, the preview will automatically " | |
| 1218 | + "refresh when this tab is selected."); | |
| 1219 | + CX("<span class='save-button-slot'></span>"); | |
| 1220 | + CX("</div>"/*.wikiedit-options*/); | |
| 1221 | + CX("<div id='wikiedit-tab-preview-wrapper'></div>"); | |
| 1222 | + CX("</div>"/*#wikiedit-tab-preview*/); | |
| 1223 | + } | |
| 1224 | + | |
| 1225 | + /****** Diff tab ******/ | |
| 1226 | + { | |
| 1227 | + CX("<div id='wikiedit-tab-diff' " | |
| 1228 | + "data-tab-parent='wikiedit-tabs' " | |
| 1229 | + "data-tab-label='Diff' " | |
| 1230 | + "class='hidden'" | |
| 1231 | + ">"); | |
| 1232 | + | |
| 1233 | + CX("<div class='wikiedit-options flex-container " | |
| 1234 | + "flex-row child-gap-small' " | |
| 1235 | + "id='wikiedit-tab-diff-buttons'>"); | |
| 1236 | + CX("<button class='sbs'>Side-by-side</button>" | |
| 1237 | + "<button class='unified'>Unified</button>"); | |
| 1238 | + CX("<span class='save-button-slot'></span>"); | |
| 1239 | + CX("</div>"); | |
| 1240 | + CX("<div id='wikiedit-tab-diff-wrapper'>" | |
| 1241 | + "Diffs will be shown here." | |
| 1242 | + "</div>"); | |
| 1243 | + CX("</div>"/*#wikiedit-tab-diff*/); | |
| 1244 | + } | |
| 1245 | + | |
| 1246 | + /****** The obligatory "Misc" tab ******/ | |
| 1247 | + { | |
| 1248 | + CX("<div id='wikiedit-tab-misc' " | |
| 1249 | + "data-tab-parent='wikiedit-tabs' " | |
| 1250 | + "data-tab-label='Help' " | |
| 1251 | + "class='hidden'" | |
| 1252 | + ">"); | |
| 1253 | + CX("<h2>Wiki formatting rules</h2>"); | |
| 1254 | + CX("<ul>"); | |
| 1255 | + CX("<li><a href='%R/wiki_rules'>Fossil wiki format</a></li>"); | |
| 1256 | + CX("<li><a href='%R/md_rules'>Markdown format</a></li>"); | |
| 1257 | + CX("<li>Plain-text pages use no special formatting.</li>"); | |
| 1258 | + CX("</ul>"); | |
| 1259 | + CX("<h2>The \"Sandbox\" Page</h2>"); | |
| 1260 | + CX("<p>The page named \"Sandbox\" is not a real wiki page. " | |
| 1261 | + "It provides a place where users may test out wiki syntax " | |
| 1262 | + "without having to actually save anything, nor pollute " | |
| 1263 | + "the repo with endless test runs. Any attempt to save the " | |
| 1264 | + "sandbox page will fail.</p>"); | |
| 1265 | + CX("<h2>Wiki Name Rules</h2>"); | |
| 1266 | + well_formed_wiki_name_rules(); | |
| 1267 | + CX("</div>"/*#wikiedit-tab-save*/); | |
| 1268 | + } | |
| 1269 | + | |
| 1270 | + builtin_request_js("sbsdiff.js"); | |
| 1271 | + style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", | |
| 1272 | + "storage", "page.wikiedit", 0); | |
| 1273 | + builtin_fulfill_js_requests(); | |
| 1274 | + /* Dynamically populate the editor... */ | |
| 1275 | + style_emit_script_tag(0,0); | |
| 1276 | + { | |
| 1277 | + /* Render the current page list to save us an XHR request | |
| 1278 | + during page initialization. This must be OUTSIDE of | |
| 1279 | + an onPageLoad() handler or else it does not get applied | |
| 1280 | + until after the wiki list widget is initialized. Similarly, | |
| 1281 | + it must come *after* window.fossil is initialized. */ | |
| 1282 | + CX("\nfossil.page.initialPageList = "); | |
| 1283 | + wiki_render_page_list_json(1, 0); | |
| 1284 | + CX(";\n"); | |
| 1285 | + } | |
| 1286 | + CX("fossil.onPageLoad(function(){\n"); | |
| 1287 | + CX("const P = fossil.page;\n" | |
| 1288 | + "try{\n"); | |
| 1289 | + if(!found && zPageName && *zPageName){ | |
| 1290 | + /* For a new page, stick a dummy entry in the JS-side stash | |
| 1291 | + and "load" it from there. */ | |
| 1292 | + CX("const winfo = {" | |
| 1293 | + "\"name\": %!j, \"mimetype\": %!j, " | |
| 1294 | + "\"type\": %!j, " | |
| 1295 | + "\"parent\": null, \"version\": null" | |
| 1296 | + "};\n", | |
| 1297 | + zPageName, | |
| 1298 | + zMimetype ? zMimetype : "text/x-fossil-wiki", | |
| 1299 | + wiki_page_type_name(zPageName)); | |
| 1300 | + /* If the JS-side stash already has this page, load that | |
| 1301 | + copy from the stash, otherwise inject a new stash entry | |
| 1302 | + for it and load *that* one... */ | |
| 1303 | + CX("if(!P.$stash.getWinfo(winfo)){" | |
| 1304 | + "P.$stash.updateWinfo(winfo,'');" | |
| 1305 | + "}\n"); | |
| 1306 | + } | |
| 1307 | + if(zPageName && *zPageName){ | |
| 1308 | + CX("P.loadPage(%!j);\n", zPageName); | |
| 1309 | + } | |
| 1310 | + CX("}catch(e){" | |
| 1311 | + "fossil.error(e); console.error('Exception:',e);" | |
| 1312 | + "}\n"); | |
| 1313 | + CX("});\n"/*fossil.onPageLoad()*/); | |
| 1314 | + style_emit_script_tag(1,0); | |
| 835 | 1315 | style_footer(); |
| 836 | 1316 | } |
| 837 | 1317 | |
| 838 | 1318 | /* |
| 839 | 1319 | ** WEBPAGE: wikinew |
| @@ -851,17 +1331,11 @@ | ||
| 851 | 1331 | return; |
| 852 | 1332 | } |
| 853 | 1333 | zName = PD("name",""); |
| 854 | 1334 | zMimetype = wiki_filter_mimetypes(P("mimetype")); |
| 855 | 1335 | if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ |
| 856 | - if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 | |
| 857 | - && db_get_boolean("wysiwyg-wiki", 0) | |
| 858 | - ){ | |
| 859 | - cgi_redirectf("wikiedit?name=%T&wysiwyg=1", zName); | |
| 860 | - }else{ | |
| 861 | - cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); | |
| 862 | - } | |
| 1336 | + cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); | |
| 863 | 1337 | } |
| 864 | 1338 | style_header("Create A New Wiki Page"); |
| 865 | 1339 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 866 | 1340 | @ <p>Rules for wiki page names:</p> |
| 867 | 1341 | well_formed_wiki_name_rules(); |
| @@ -1441,11 +1915,11 @@ | ||
| 1441 | 1915 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1442 | 1916 | ** Defaults to the type used by |
| 1443 | 1917 | ** the previous version of the |
| 1444 | 1918 | ** page, or text/x-fossil-wiki. |
| 1445 | 1919 | ** Valid values are: text/x-fossil-wiki, |
| 1446 | -** text/markdown and text/plain. fossil, | |
| 1920 | +** text/x-markdown and text/plain. fossil, | |
| 1447 | 1921 | ** markdown or plain can be specified as |
| 1448 | 1922 | ** synonyms of these values. |
| 1449 | 1923 | ** -t|--technote DATETIME Specifies the timestamp of |
| 1450 | 1924 | ** the technote to be created or |
| 1451 | 1925 | ** updated. When updating a tech note |
| @@ -1479,13 +1953,18 @@ | ||
| 1479 | 1953 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 1480 | 1954 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 1481 | 1955 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 1482 | 1956 | ** means UTC. |
| 1483 | 1957 | ** |
| 1958 | +** The "Sandbox" wiki pseudo-page is a special case. Its name is | |
| 1959 | +** checked case-insensitively and either "create" or "commit" may be | |
| 1960 | +** used to update its contents. | |
| 1484 | 1961 | */ |
| 1485 | 1962 | void wiki_cmd(void){ |
| 1486 | 1963 | int n; |
| 1964 | + int isSandbox = 0; /* true if dealing with sandbox pseudo-page */ | |
| 1965 | + | |
| 1487 | 1966 | db_find_and_open_repository(0, 0); |
| 1488 | 1967 | if( g.argc<3 ){ |
| 1489 | 1968 | goto wiki_cmd_usage; |
| 1490 | 1969 | } |
| 1491 | 1970 | n = strlen(g.argv[2]); |
| @@ -1492,14 +1971,14 @@ | ||
| 1492 | 1971 | if( n==0 ){ |
| 1493 | 1972 | goto wiki_cmd_usage; |
| 1494 | 1973 | } |
| 1495 | 1974 | |
| 1496 | 1975 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 1497 | - const char *zPageName; /* Name of the wiki page to export */ | |
| 1976 | + const char *zPageName = 0; /* Name of the wiki page to export */ | |
| 1498 | 1977 | const char *zFile; /* Name of the output file (0=stdout) */ |
| 1499 | 1978 | const char *zETime; /* The name of the technote to export */ |
| 1500 | - int rid; /* Artifact ID of the wiki page */ | |
| 1979 | + int rid = 0; /* Artifact ID of the wiki page */ | |
| 1501 | 1980 | int i; /* Loop counter */ |
| 1502 | 1981 | char *zBody = 0; /* Wiki page content */ |
| 1503 | 1982 | Blob body = empty_blob; /* Wiki page content */ |
| 1504 | 1983 | Manifest *pWiki = 0; /* Parsed wiki page content */ |
| 1505 | 1984 | int fHtml = 0; /* Export in HTML form */ |
| @@ -1517,17 +1996,18 @@ | ||
| 1517 | 1996 | if( !zETime ){ |
| 1518 | 1997 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 1519 | 1998 | usage("export ?-html? PAGENAME ?FILE?"); |
| 1520 | 1999 | } |
| 1521 | 2000 | zPageName = g.argv[3]; |
| 1522 | - rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" | |
| 1523 | - " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" | |
| 1524 | - " ORDER BY x.mtime DESC LIMIT 1", | |
| 1525 | - zPageName | |
| 1526 | - ); | |
| 1527 | - if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ | |
| 1528 | - zBody = pWiki->zWiki; | |
| 2001 | + isSandbox = is_sandbox(zPageName); | |
| 2002 | + if(isSandbox){ | |
| 2003 | + zBody = db_get("sandbox", 0); | |
| 2004 | + }else{ | |
| 2005 | + wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); | |
| 2006 | + if(pWiki){ | |
| 2007 | + zBody = pWiki->zWiki; | |
| 2008 | + } | |
| 1529 | 2009 | } |
| 1530 | 2010 | if( zBody==0 ){ |
| 1531 | 2011 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 1532 | 2012 | } |
| 1533 | 2013 | zFile = (g.argc==4) ? "-" : g.argv[4]; |
| @@ -1553,30 +2033,24 @@ | ||
| 1553 | 2033 | blob_init(&body, zBody, -1); |
| 1554 | 2034 | if(fHtml==0){ |
| 1555 | 2035 | blob_append(&body, "\n", 1); |
| 1556 | 2036 | }else{ |
| 1557 | 2037 | Blob html = empty_blob; /* HTML-ized content */ |
| 1558 | - const char * zMimetype = wiki_filter_mimetypes(pWiki->zMimetype); | |
| 2038 | + const char * zMimetype = isSandbox | |
| 2039 | + ? db_get("sandbox-mimetype", "text/x-fossil-wiki") | |
| 2040 | + : wiki_filter_mimetypes(pWiki->zMimetype); | |
| 1559 | 2041 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1560 | 2042 | wiki_convert(&body,&html,0); |
| 1561 | 2043 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1562 | - markdown_to_html(&body,0,&html) | |
| 1563 | - /* TODO: add -HTML|-H flag to work like -html|-h but also | |
| 1564 | - ** add <html><body> tag wrappers around the output. The | |
| 1565 | - ** hurdle here is that the markdown converter resets its | |
| 1566 | - ** input blob before appending the output, which is | |
| 1567 | - ** different from wiki_convert() and htmlize_to_blob(), and | |
| 1568 | - ** precludes us simply appending the opening <html><body> | |
| 1569 | - ** part to the body | |
| 1570 | - */; | |
| 2044 | + markdown_to_html(&body,0,&html); | |
| 1571 | 2045 | safe_html_context(DOCSRC_WIKI); |
| 1572 | 2046 | safe_html(&html); |
| 1573 | 2047 | }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ |
| 1574 | 2048 | htmlize_to_blob(&html,zBody,i); |
| 1575 | 2049 | }else{ |
| 1576 | 2050 | fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", |
| 1577 | - zMimetype, pWiki->zWikiTitle ); | |
| 2051 | + zMimetype, pWiki ? pWiki->zWikiTitle : zPageName ); | |
| 1578 | 2052 | } |
| 1579 | 2053 | blob_reset(&body); |
| 1580 | 2054 | body = html /* transfer memory */; |
| 1581 | 2055 | } |
| 1582 | 2056 | pFile = fossil_fopen_for_output(zFile); |
| @@ -1618,28 +2092,28 @@ | ||
| 1618 | 2092 | if( g.argc==4 ){ |
| 1619 | 2093 | blob_read_from_channel(&content, stdin, -1); |
| 1620 | 2094 | }else{ |
| 1621 | 2095 | blob_read_from_file(&content, g.argv[4], ExtFILE); |
| 1622 | 2096 | } |
| 2097 | + isSandbox = is_sandbox(zPageName); | |
| 1623 | 2098 | if ( !zETime ){ |
| 1624 | - rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" | |
| 1625 | - " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" | |
| 1626 | - " ORDER BY x.mtime DESC LIMIT 1", | |
| 1627 | - zPageName | |
| 1628 | - ); | |
| 1629 | - if( rid>0 ){ | |
| 1630 | - pWiki = manifest_get(rid, CFTYPE_WIKI, 0); | |
| 2099 | + if( !isSandbox ){ | |
| 2100 | + wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); | |
| 1631 | 2101 | } |
| 1632 | 2102 | }else{ |
| 1633 | 2103 | rid = wiki_technote_to_rid(zETime); |
| 1634 | 2104 | if( rid>0 ){ |
| 1635 | 2105 | pWiki = manifest_get(rid, CFTYPE_EVENT, 0); |
| 1636 | 2106 | } |
| 1637 | 2107 | } |
| 1638 | 2108 | if( !zMimeType || !*zMimeType ){ |
| 1639 | 2109 | /* Try to deduce the mime type based on the prior version. */ |
| 1640 | - if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ | |
| 2110 | + if(isSandbox){ | |
| 2111 | + zMimeType = | |
| 2112 | + wiki_filter_mimetypes(db_get("sandbox-mimetype", | |
| 2113 | + "text/x-fossil-wiki")); | |
| 2114 | + }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ | |
| 1641 | 2115 | zMimeType = pWiki->zMimetype; |
| 1642 | 2116 | } |
| 1643 | 2117 | }else{ |
| 1644 | 2118 | zMimeType = wiki_filter_mimetypes(zMimeType); |
| 1645 | 2119 | } |
| @@ -1649,24 +2123,30 @@ | ||
| 1649 | 2123 | }else{ |
| 1650 | 2124 | /* Creating a tech note with same timestamp is permitted |
| 1651 | 2125 | and should create a new tech note */ |
| 1652 | 2126 | rid = 0; |
| 1653 | 2127 | } |
| 1654 | - }else if( !isCreate && rid == 0 ){ | |
| 2128 | + }else if( !isCreate && rid==0 && isSandbox==0 ){ | |
| 1655 | 2129 | if ( !zETime ){ |
| 1656 | 2130 | fossil_fatal("no such wiki page: %s", zPageName); |
| 1657 | 2131 | }else{ |
| 1658 | 2132 | fossil_fatal("no such tech note: %s", zETime); |
| 1659 | 2133 | } |
| 1660 | 2134 | } |
| 1661 | 2135 | |
| 1662 | 2136 | if( !zETime ){ |
| 1663 | - wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1); | |
| 1664 | - if( g.argv[2][1]=='r' ){ | |
| 1665 | - fossil_print("Created new wiki page %s.\n", zPageName); | |
| 2137 | + if(isSandbox){ | |
| 2138 | + db_set("sandbox",blob_str(&content),0); | |
| 2139 | + db_set("sandbox-mimetype",zMimeType,0); | |
| 2140 | + fossil_print("Updated sandbox pseudo-page.\n"); | |
| 1666 | 2141 | }else{ |
| 1667 | - fossil_print("Updated wiki page %s.\n", zPageName); | |
| 2142 | + wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1); | |
| 2143 | + if( g.argv[2][1]=='r' ){ | |
| 2144 | + fossil_print("Created new wiki page %s.\n", zPageName); | |
| 2145 | + }else{ | |
| 2146 | + fossil_print("Updated wiki page %s.\n", zPageName); | |
| 2147 | + } | |
| 1668 | 2148 | } |
| 1669 | 2149 | }else{ |
| 1670 | 2150 | if( rid != -1 ){ |
| 1671 | 2151 | char *zMETime; /* Normalized, mutable version of zETime */ |
| 1672 | 2152 | zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", |
| @@ -1844,8 +2324,8 @@ | ||
| 1844 | 2324 | blob_reset(&tail); |
| 1845 | 2325 | blob_reset(&title); |
| 1846 | 2326 | blob_reset(&wiki); |
| 1847 | 2327 | } |
| 1848 | 2328 | manifest_destroy(pWiki); |
| 1849 | - style_accordion(); | |
| 2329 | + builtin_request_js("accordion.js"); | |
| 1850 | 2330 | return 1; |
| 1851 | 2331 | } |
| 1852 | 2332 | |
| 1853 | 2333 | DELETED src/wysiwyg.c |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -102,11 +102,10 @@ | |
| 102 | " WHERE tagid=%d AND mtime<%.16g" |
| 103 | " ORDER BY mtime DESC LIMIT 1", |
| 104 | tagid, mtime); |
| 105 | } |
| 106 | |
| 107 | |
| 108 | /* |
| 109 | ** WEBPAGE: home |
| 110 | ** WEBPAGE: index |
| 111 | ** WEBPAGE: not_found |
| 112 | ** |
| @@ -398,10 +397,24 @@ | |
| 398 | if( sqlite3_strglob("tag/*", zPageName)==0 ){ |
| 399 | return WIKITYPE_TAG; |
| 400 | } |
| 401 | return WIKITYPE_NORMAL; |
| 402 | } |
| 403 | |
| 404 | /* |
| 405 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 406 | ** for zPageName. zExtra is an empty string for /wiki but has the text |
| 407 | ** "Edit: " for /wikiedit. |
| @@ -541,16 +554,11 @@ | |
| 541 | zMimetype = wiki_filter_mimetypes(zMimetype); |
| 542 | if( !g.isHome && !noSubmenu ){ |
| 543 | if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki)) |
| 544 | && wiki_special_permission(zPageName) |
| 545 | ){ |
| 546 | if( db_get_boolean("wysiwyg-wiki", 0) ){ |
| 547 | style_submenu_element("Edit", "%R/wikiedit?name=%T&wysiwyg=1", |
| 548 | zPageName); |
| 549 | }else{ |
| 550 | style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName); |
| 551 | } |
| 552 | }else if( rid && g.perm.ApndWiki ){ |
| 553 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 554 | } |
| 555 | if( g.perm.Hyperlink ){ |
| 556 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| @@ -620,220 +628,692 @@ | |
| 620 | return azStyles[i+1]; |
| 621 | } |
| 622 | } |
| 623 | return azStyles[1]; |
| 624 | } |
| 625 | |
| 626 | /* |
| 627 | ** WEBPAGE: wikiedit |
| 628 | ** URL: /wikiedit?name=PAGENAME |
| 629 | ** |
| 630 | ** Edit a wiki page. |
| 631 | */ |
| 632 | void wikiedit_page(void){ |
| 633 | char *zTag; |
| 634 | int rid = 0; |
| 635 | int isSandbox; |
| 636 | Blob wiki; |
| 637 | Manifest *pWiki = 0; |
| 638 | const char *zPageName; |
| 639 | int n; |
| 640 | const char *z; |
| 641 | char *zBody = (char*)P("w"); |
| 642 | const char *zMimetype = wiki_filter_mimetypes(P("mimetype")); |
| 643 | int isWysiwyg = P("wysiwyg")!=0; |
| 644 | int goodCaptcha = 1; |
| 645 | int eType = WIKITYPE_UNKNOWN; |
| 646 | int havePreview = 0; |
| 647 | |
| 648 | if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; } |
| 649 | if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; } |
| 650 | if( zBody ){ |
| 651 | if( isWysiwyg ){ |
| 652 | Blob body; |
| 653 | blob_zero(&body); |
| 654 | htmlTidy(zBody, &body); |
| 655 | zBody = blob_str(&body); |
| 656 | }else{ |
| 657 | zBody = mprintf("%s", zBody); |
| 658 | } |
| 659 | } |
| 660 | login_check_credentials(); |
| 661 | zPageName = PD("name",""); |
| 662 | if( check_name(zPageName) ) return; |
| 663 | isSandbox = is_sandbox(zPageName); |
| 664 | if( isSandbox ){ |
| 665 | if( !g.perm.WrWiki ){ |
| 666 | login_needed(g.anon.WrWiki); |
| 667 | return; |
| 668 | } |
| 669 | if( zBody==0 ){ |
| 670 | zBody = db_get("sandbox",""); |
| 671 | zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 672 | } |
| 673 | }else{ |
| 674 | zTag = mprintf("wiki-%s", zPageName); |
| 675 | rid = db_int(0, |
| 676 | "SELECT rid FROM tagxref" |
| 677 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 678 | " ORDER BY mtime DESC", zTag |
| 679 | ); |
| 680 | free(zTag); |
| 681 | if( !wiki_special_permission(zPageName) ){ |
| 682 | login_needed(0); |
| 683 | return; |
| 684 | } |
| 685 | if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){ |
| 686 | login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki); |
| 687 | return; |
| 688 | } |
| 689 | if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ |
| 690 | zBody = pWiki->zWiki; |
| 691 | zMimetype = pWiki->zMimetype; |
| 692 | } |
| 693 | } |
| 694 | if( P("submit")!=0 && zBody!=0 |
| 695 | && (goodCaptcha = captcha_is_correct(0)) |
| 696 | ){ |
| 697 | char *zDate; |
| 698 | Blob cksum; |
| 699 | blob_zero(&wiki); |
| 700 | db_begin_transaction(); |
| 701 | if( isSandbox ){ |
| 702 | db_set("sandbox",zBody,0); |
| 703 | db_set("sandbox-mimetype",zMimetype,0); |
| 704 | }else{ |
| 705 | login_verify_csrf_secret(); |
| 706 | zDate = date_in_standard_format("now"); |
| 707 | blob_appendf(&wiki, "D %s\n", zDate); |
| 708 | free(zDate); |
| 709 | blob_appendf(&wiki, "L %F\n", zPageName); |
| 710 | if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")!=0 ){ |
| 711 | blob_appendf(&wiki, "N %s\n", zMimetype); |
| 712 | } |
| 713 | if( rid ){ |
| 714 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 715 | blob_appendf(&wiki, "P %s\n", zUuid); |
| 716 | free(zUuid); |
| 717 | } |
| 718 | if( !login_is_nobody() ){ |
| 719 | blob_appendf(&wiki, "U %F\n", login_name()); |
| 720 | } |
| 721 | blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody); |
| 722 | md5sum_blob(&wiki, &cksum); |
| 723 | blob_appendf(&wiki, "Z %b\n", &cksum); |
| 724 | blob_reset(&cksum); |
| 725 | wiki_put(&wiki, 0, wiki_need_moderation(0)); |
| 726 | } |
| 727 | db_end_transaction(0); |
| 728 | cgi_redirectf("wiki?name=%T", zPageName); |
| 729 | } |
| 730 | if( P("cancel")!=0 ){ |
| 731 | cgi_redirectf("wiki?name=%T", zPageName); |
| 732 | return; |
| 733 | } |
| 734 | if( zBody==0 ){ |
| 735 | zBody = mprintf(""); |
| 736 | } |
| 737 | style_set_current_page("%T?name=%T", g.zPath, zPageName); |
| 738 | eType = wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "Edit: "); |
| 739 | if( rid && !isSandbox && g.perm.ApndWiki ){ |
| 740 | if( g.perm.Attach ){ |
| 741 | style_submenu_element("Attach", |
| 742 | "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T", |
| 743 | g.zTop, zPageName, g.zTop, zPageName); |
| 744 | } |
| 745 | } |
| 746 | if( !goodCaptcha ){ |
| 747 | @ <p class="generalError">Error: Incorrect security code.</p> |
| 748 | } |
| 749 | blob_zero(&wiki); |
| 750 | while( fossil_isspace(zBody[0]) ) zBody++; |
| 751 | blob_append(&wiki, zBody, -1); |
| 752 | if( P("preview")!=0 ){ |
| 753 | havePreview = 1; |
| 754 | if( zBody[0] ){ |
| 755 | @ Preview:<hr /> |
| 756 | safe_html_context(DOCSRC_WIKI); |
| 757 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 758 | @ <hr /> |
| 759 | blob_reset(&wiki); |
| 760 | } |
| 761 | } |
| 762 | for(n=2, z=zBody; z[0]; z++){ |
| 763 | if( z[0]=='\n' ) n++; |
| 764 | } |
| 765 | if( n<20 ) n = 20; |
| 766 | if( n>30 ) n = 30; |
| 767 | if( !isWysiwyg ){ |
| 768 | /* Traditional markup-only editing */ |
| 769 | char *zPlaceholder = 0; |
| 770 | switch( eType ){ |
| 771 | case WIKITYPE_NORMAL: { |
| 772 | zPlaceholder = mprintf("Enter text for wiki page %s", zPageName); |
| 773 | break; |
| 774 | } |
| 775 | case WIKITYPE_BRANCH: { |
| 776 | zPlaceholder = mprintf("Enter notes about branch %s", zPageName+7); |
| 777 | break; |
| 778 | } |
| 779 | case WIKITYPE_CHECKIN: { |
| 780 | zPlaceholder = mprintf("Enter notes about check-in %.20s", zPageName+8); |
| 781 | break; |
| 782 | } |
| 783 | case WIKITYPE_TAG: { |
| 784 | zPlaceholder = mprintf("Enter notes about tag %s", zPageName+4); |
| 785 | break; |
| 786 | } |
| 787 | } |
| 788 | form_begin(0, "%R/wikiedit"); |
| 789 | @ <div>%z(href("%R/markup_help"))Markup style</a>: |
| 790 | mimetype_option_menu(zMimetype); |
| 791 | @ <br /><textarea name="w" class="wikiedit" cols="80" \ |
| 792 | @ rows="%d(n)" wrap="virtual" placeholder="%h(zPlaceholder)">\ |
| 793 | @ %h(zBody)</textarea> |
| 794 | @ <br /> |
| 795 | fossil_free(zPlaceholder); |
| 796 | if( db_get_boolean("wysiwyg-wiki", 0) ){ |
| 797 | @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor" |
| 798 | @ onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' /> |
| 799 | } |
| 800 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 801 | }else{ |
| 802 | /* Wysiwyg editing */ |
| 803 | Blob html, temp; |
| 804 | havePreview = 1; |
| 805 | form_begin("", "%R/wikiedit"); |
| 806 | @ <div> |
| 807 | @ <input type="hidden" name="wysiwyg" value="1" /> |
| 808 | blob_zero(&temp); |
| 809 | wiki_convert(&wiki, &temp, 0); |
| 810 | blob_zero(&html); |
| 811 | htmlTidy(blob_str(&temp), &html); |
| 812 | blob_reset(&temp); |
| 813 | wysiwygEditor("w", blob_str(&html), 60, n); |
| 814 | blob_reset(&html); |
| 815 | @ <br /> |
| 816 | @ <input type="submit" name="edit-markup" value="Markup Editor" |
| 817 | @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' /> |
| 818 | } |
| 819 | login_insert_csrf_secret(); |
| 820 | if( havePreview ){ |
| 821 | if( isWysiwyg || zBody[0] ){ |
| 822 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 823 | }else{ |
| 824 | @ <input type="submit" name="submit" value="Delete This Wiki Page" /> |
| 825 | } |
| 826 | } |
| 827 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 828 | @ <input type="submit" name="cancel" value="Cancel" |
| 829 | @ onclick='confirm("Abandon your changes?")' /> |
| 830 | @ </div> |
| 831 | captcha_generate(0); |
| 832 | @ </form> |
| 833 | manifest_destroy(pWiki); |
| 834 | blob_reset(&wiki); |
| 835 | style_footer(); |
| 836 | } |
| 837 | |
| 838 | /* |
| 839 | ** WEBPAGE: wikinew |
| @@ -851,17 +1331,11 @@ | |
| 851 | return; |
| 852 | } |
| 853 | zName = PD("name",""); |
| 854 | zMimetype = wiki_filter_mimetypes(P("mimetype")); |
| 855 | if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ |
| 856 | if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 |
| 857 | && db_get_boolean("wysiwyg-wiki", 0) |
| 858 | ){ |
| 859 | cgi_redirectf("wikiedit?name=%T&wysiwyg=1", zName); |
| 860 | }else{ |
| 861 | cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); |
| 862 | } |
| 863 | } |
| 864 | style_header("Create A New Wiki Page"); |
| 865 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 866 | @ <p>Rules for wiki page names:</p> |
| 867 | well_formed_wiki_name_rules(); |
| @@ -1441,11 +1915,11 @@ | |
| 1441 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1442 | ** Defaults to the type used by |
| 1443 | ** the previous version of the |
| 1444 | ** page, or text/x-fossil-wiki. |
| 1445 | ** Valid values are: text/x-fossil-wiki, |
| 1446 | ** text/markdown and text/plain. fossil, |
| 1447 | ** markdown or plain can be specified as |
| 1448 | ** synonyms of these values. |
| 1449 | ** -t|--technote DATETIME Specifies the timestamp of |
| 1450 | ** the technote to be created or |
| 1451 | ** updated. When updating a tech note |
| @@ -1479,13 +1953,18 @@ | |
| 1479 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 1480 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 1481 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 1482 | ** means UTC. |
| 1483 | ** |
| 1484 | */ |
| 1485 | void wiki_cmd(void){ |
| 1486 | int n; |
| 1487 | db_find_and_open_repository(0, 0); |
| 1488 | if( g.argc<3 ){ |
| 1489 | goto wiki_cmd_usage; |
| 1490 | } |
| 1491 | n = strlen(g.argv[2]); |
| @@ -1492,14 +1971,14 @@ | |
| 1492 | if( n==0 ){ |
| 1493 | goto wiki_cmd_usage; |
| 1494 | } |
| 1495 | |
| 1496 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 1497 | const char *zPageName; /* Name of the wiki page to export */ |
| 1498 | const char *zFile; /* Name of the output file (0=stdout) */ |
| 1499 | const char *zETime; /* The name of the technote to export */ |
| 1500 | int rid; /* Artifact ID of the wiki page */ |
| 1501 | int i; /* Loop counter */ |
| 1502 | char *zBody = 0; /* Wiki page content */ |
| 1503 | Blob body = empty_blob; /* Wiki page content */ |
| 1504 | Manifest *pWiki = 0; /* Parsed wiki page content */ |
| 1505 | int fHtml = 0; /* Export in HTML form */ |
| @@ -1517,17 +1996,18 @@ | |
| 1517 | if( !zETime ){ |
| 1518 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 1519 | usage("export ?-html? PAGENAME ?FILE?"); |
| 1520 | } |
| 1521 | zPageName = g.argv[3]; |
| 1522 | rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" |
| 1523 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 1524 | " ORDER BY x.mtime DESC LIMIT 1", |
| 1525 | zPageName |
| 1526 | ); |
| 1527 | if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ |
| 1528 | zBody = pWiki->zWiki; |
| 1529 | } |
| 1530 | if( zBody==0 ){ |
| 1531 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 1532 | } |
| 1533 | zFile = (g.argc==4) ? "-" : g.argv[4]; |
| @@ -1553,30 +2033,24 @@ | |
| 1553 | blob_init(&body, zBody, -1); |
| 1554 | if(fHtml==0){ |
| 1555 | blob_append(&body, "\n", 1); |
| 1556 | }else{ |
| 1557 | Blob html = empty_blob; /* HTML-ized content */ |
| 1558 | const char * zMimetype = wiki_filter_mimetypes(pWiki->zMimetype); |
| 1559 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1560 | wiki_convert(&body,&html,0); |
| 1561 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1562 | markdown_to_html(&body,0,&html) |
| 1563 | /* TODO: add -HTML|-H flag to work like -html|-h but also |
| 1564 | ** add <html><body> tag wrappers around the output. The |
| 1565 | ** hurdle here is that the markdown converter resets its |
| 1566 | ** input blob before appending the output, which is |
| 1567 | ** different from wiki_convert() and htmlize_to_blob(), and |
| 1568 | ** precludes us simply appending the opening <html><body> |
| 1569 | ** part to the body |
| 1570 | */; |
| 1571 | safe_html_context(DOCSRC_WIKI); |
| 1572 | safe_html(&html); |
| 1573 | }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ |
| 1574 | htmlize_to_blob(&html,zBody,i); |
| 1575 | }else{ |
| 1576 | fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", |
| 1577 | zMimetype, pWiki->zWikiTitle ); |
| 1578 | } |
| 1579 | blob_reset(&body); |
| 1580 | body = html /* transfer memory */; |
| 1581 | } |
| 1582 | pFile = fossil_fopen_for_output(zFile); |
| @@ -1618,28 +2092,28 @@ | |
| 1618 | if( g.argc==4 ){ |
| 1619 | blob_read_from_channel(&content, stdin, -1); |
| 1620 | }else{ |
| 1621 | blob_read_from_file(&content, g.argv[4], ExtFILE); |
| 1622 | } |
| 1623 | if ( !zETime ){ |
| 1624 | rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" |
| 1625 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 1626 | " ORDER BY x.mtime DESC LIMIT 1", |
| 1627 | zPageName |
| 1628 | ); |
| 1629 | if( rid>0 ){ |
| 1630 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 1631 | } |
| 1632 | }else{ |
| 1633 | rid = wiki_technote_to_rid(zETime); |
| 1634 | if( rid>0 ){ |
| 1635 | pWiki = manifest_get(rid, CFTYPE_EVENT, 0); |
| 1636 | } |
| 1637 | } |
| 1638 | if( !zMimeType || !*zMimeType ){ |
| 1639 | /* Try to deduce the mime type based on the prior version. */ |
| 1640 | if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ |
| 1641 | zMimeType = pWiki->zMimetype; |
| 1642 | } |
| 1643 | }else{ |
| 1644 | zMimeType = wiki_filter_mimetypes(zMimeType); |
| 1645 | } |
| @@ -1649,24 +2123,30 @@ | |
| 1649 | }else{ |
| 1650 | /* Creating a tech note with same timestamp is permitted |
| 1651 | and should create a new tech note */ |
| 1652 | rid = 0; |
| 1653 | } |
| 1654 | }else if( !isCreate && rid == 0 ){ |
| 1655 | if ( !zETime ){ |
| 1656 | fossil_fatal("no such wiki page: %s", zPageName); |
| 1657 | }else{ |
| 1658 | fossil_fatal("no such tech note: %s", zETime); |
| 1659 | } |
| 1660 | } |
| 1661 | |
| 1662 | if( !zETime ){ |
| 1663 | wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1); |
| 1664 | if( g.argv[2][1]=='r' ){ |
| 1665 | fossil_print("Created new wiki page %s.\n", zPageName); |
| 1666 | }else{ |
| 1667 | fossil_print("Updated wiki page %s.\n", zPageName); |
| 1668 | } |
| 1669 | }else{ |
| 1670 | if( rid != -1 ){ |
| 1671 | char *zMETime; /* Normalized, mutable version of zETime */ |
| 1672 | zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", |
| @@ -1844,8 +2324,8 @@ | |
| 1844 | blob_reset(&tail); |
| 1845 | blob_reset(&title); |
| 1846 | blob_reset(&wiki); |
| 1847 | } |
| 1848 | manifest_destroy(pWiki); |
| 1849 | style_accordion(); |
| 1850 | return 1; |
| 1851 | } |
| 1852 | |
| 1853 | ELETED src/wysiwyg.c |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -102,11 +102,10 @@ | |
| 102 | " WHERE tagid=%d AND mtime<%.16g" |
| 103 | " ORDER BY mtime DESC LIMIT 1", |
| 104 | tagid, mtime); |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | ** WEBPAGE: home |
| 109 | ** WEBPAGE: index |
| 110 | ** WEBPAGE: not_found |
| 111 | ** |
| @@ -398,10 +397,24 @@ | |
| 397 | if( sqlite3_strglob("tag/*", zPageName)==0 ){ |
| 398 | return WIKITYPE_TAG; |
| 399 | } |
| 400 | return WIKITYPE_NORMAL; |
| 401 | } |
| 402 | |
| 403 | /* |
| 404 | ** Returns a JSON-friendly string form of the integer value returned |
| 405 | ** by wiki_page_type(zPageName). |
| 406 | */ |
| 407 | const char * wiki_page_type_name(const char *zPageName){ |
| 408 | switch(wiki_page_type(zPageName)){ |
| 409 | case WIKITYPE_CHECKIN: return "checkin"; |
| 410 | case WIKITYPE_BRANCH: return "branch"; |
| 411 | case WIKITYPE_TAG: return "tag"; |
| 412 | case WIKITYPE_NORMAL: |
| 413 | default: return "normal"; |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** Add an appropriate style_header() for either the /wiki or /wikiedit page |
| 419 | ** for zPageName. zExtra is an empty string for /wiki but has the text |
| 420 | ** "Edit: " for /wikiedit. |
| @@ -541,16 +554,11 @@ | |
| 554 | zMimetype = wiki_filter_mimetypes(zMimetype); |
| 555 | if( !g.isHome && !noSubmenu ){ |
| 556 | if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki)) |
| 557 | && wiki_special_permission(zPageName) |
| 558 | ){ |
| 559 | style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName); |
| 560 | }else if( rid && g.perm.ApndWiki ){ |
| 561 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 562 | } |
| 563 | if( g.perm.Hyperlink ){ |
| 564 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| @@ -620,220 +628,692 @@ | |
| 628 | return azStyles[i+1]; |
| 629 | } |
| 630 | } |
| 631 | return azStyles[1]; |
| 632 | } |
| 633 | |
| 634 | /* |
| 635 | ** Tries to fetch a wiki page for the given name. If found, it |
| 636 | ** returns true, else false. |
| 637 | ** |
| 638 | ** versionsBack specifies how many versions back in the history to |
| 639 | ** fetch. Use 0 for the latest version, 1 for its parent, etc. |
| 640 | ** |
| 641 | ** If pRid is not NULL then if a result is found *pRid is set to its |
| 642 | ** RID. If ppWiki is not NULL then if found *ppWiki is set to the |
| 643 | ** loaded wiki object, which the caller is responsible for passing to |
| 644 | ** manifest_destroy(). |
| 645 | */ |
| 646 | static int wiki_fetch_by_name( const char *zPageName, |
| 647 | unsigned int versionsBack, |
| 648 | int * pRid, Manifest **ppWiki ){ |
| 649 | Manifest *pWiki = 0; |
| 650 | char *zTag = mprintf("wiki-%s", zPageName); |
| 651 | Stmt q = empty_Stmt; |
| 652 | int rid = 0; |
| 653 | |
| 654 | db_prepare(&q, "SELECT rid FROM tagxref" |
| 655 | " WHERE tagid=(SELECT tagid FROM tag WHERE" |
| 656 | " tagname=%Q) " |
| 657 | " ORDER BY mtime DESC LIMIT -1 OFFSET %u", zTag, |
| 658 | versionsBack); |
| 659 | fossil_free(zTag); |
| 660 | if(SQLITE_ROW == db_step(&q)){ |
| 661 | rid = db_column_int(&q, 0); |
| 662 | } |
| 663 | db_finalize(&q); |
| 664 | if( rid == 0 ){ |
| 665 | return 0; |
| 666 | } |
| 667 | else if(pRid){ |
| 668 | *pRid = rid; |
| 669 | } |
| 670 | if(ppWiki){ |
| 671 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 672 | if( pWiki==0 ){ |
| 673 | /* "Cannot happen." */ |
| 674 | return 0; |
| 675 | } |
| 676 | *ppWiki = pWiki; |
| 677 | } |
| 678 | return 1; |
| 679 | } |
| 680 | |
| 681 | /* |
| 682 | ** Determines whether the wiki page with the given name can be edited |
| 683 | ** or created by the current user. If not, an AJAX error is queued and |
| 684 | ** false is returned, else true is returned. A NULL, empty, or |
| 685 | ** malformed name is considered non-writable, regardless of the user. |
| 686 | ** |
| 687 | ** If pRid is not NULL then this function writes the page's rid to |
| 688 | ** *pRid (whether or not access is granted). On error or if the page |
| 689 | ** does not yet exist, *pRid will be set to 0. |
| 690 | ** |
| 691 | ** Note that the sandbox is a special case: it is a pseudo-page with |
| 692 | ** no rid and the /wikiajax API does not allow anyone to actually save |
| 693 | ** a sandbox page, but it is reported as writable here (with rid 0). |
| 694 | */ |
| 695 | static int wiki_ajax_can_write(const char *zPageName, int * pRid){ |
| 696 | int rid = 0; |
| 697 | const char * zErr = 0; |
| 698 | |
| 699 | if(pRid) *pRid = 0; |
| 700 | if(!zPageName || !*zPageName |
| 701 | || !wiki_name_is_wellformed((unsigned const char *)zPageName)){ |
| 702 | zErr = "Invalid page name."; |
| 703 | }else if(is_sandbox(zPageName)){ |
| 704 | return 1; |
| 705 | }else{ |
| 706 | wiki_fetch_by_name(zPageName, 0, &rid, 0); |
| 707 | if(pRid) *pRid = rid; |
| 708 | if(!wiki_special_permission(zPageName)){ |
| 709 | zErr = "Editing this page requires non-wiki write permissions."; |
| 710 | }else if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){ |
| 711 | return 3; |
| 712 | }else if(rid && !g.perm.WrWiki){ |
| 713 | zErr = "Requires wiki-write permissions."; |
| 714 | }else if(!rid && !g.perm.NewWiki){ |
| 715 | zErr = "Requires new-wiki permissions."; |
| 716 | }else{ |
| 717 | zErr = "Cannot happen! Please report this as a bug."; |
| 718 | } |
| 719 | } |
| 720 | ajax_route_error(403, "%s", zErr); |
| 721 | return 0; |
| 722 | } |
| 723 | |
| 724 | /* |
| 725 | ** Loads the given wiki page, sets the response type to |
| 726 | ** application/json, and emits it as a JSON object. If zPageName is a |
| 727 | ** sandbox page then a "fake" object is emitted, as the wikiajax API |
| 728 | ** does not permit saving the sandbox. |
| 729 | ** |
| 730 | ** Returns true on success, false on error, and on error it |
| 731 | ** queues up a JSON-format error response. |
| 732 | ** |
| 733 | ** Output JSON format: |
| 734 | ** |
| 735 | ** { name: "page name", |
| 736 | ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| 737 | ** mimetype: "mime type", |
| 738 | ** version: UUID string or null for a sandbox page, |
| 739 | ** parent: "parent uuid" or null if no parent, |
| 740 | ** isDeleted: true if the page has no content (is "deleted") |
| 741 | ** else not set (making it "falsy" in JS), |
| 742 | ** content: "page content" (only if includeContent is true) |
| 743 | ** } |
| 744 | ** |
| 745 | ** If includeContent is false then the content member is elided. |
| 746 | */ |
| 747 | static int wiki_ajax_emit_page_object(const char *zPageName, |
| 748 | int includeContent){ |
| 749 | Manifest * pWiki = 0; |
| 750 | char * zUuid; |
| 751 | |
| 752 | if( is_sandbox(zPageName) ){ |
| 753 | char * zMimetype = |
| 754 | db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 755 | char * zBody = db_get("sandbox",""); |
| 756 | CX("{\"name\": %!j, \"type\": \"sandbox\", " |
| 757 | "\"mimetype\": %!j, \"version\": null, \"parent\": null", |
| 758 | zPageName, zMimetype); |
| 759 | if(includeContent){ |
| 760 | CX(", \"content\": %!j", |
| 761 | zBody); |
| 762 | } |
| 763 | CX("}"); |
| 764 | fossil_free(zMimetype); |
| 765 | fossil_free(zBody); |
| 766 | return 1; |
| 767 | }else if( !wiki_fetch_by_name(zPageName, 0, 0, &pWiki) ){ |
| 768 | ajax_route_error(404, "Wiki page could not be loaded: %s", |
| 769 | zPageName); |
| 770 | return 0; |
| 771 | }else{ |
| 772 | zUuid = rid_to_uuid(pWiki->rid); |
| 773 | CX("{\"name\": %!j, \"type\": %!j, " |
| 774 | "\"version\": %!j, " |
| 775 | "\"mimetype\": %!j, ", |
| 776 | pWiki->zWikiTitle, |
| 777 | wiki_page_type_name(pWiki->zWikiTitle), |
| 778 | zUuid, |
| 779 | pWiki->zMimetype ? pWiki->zMimetype : "text/x-fossil-wiki"); |
| 780 | CX("\"parent\": "); |
| 781 | if(pWiki->nParent){ |
| 782 | CX("%!j", pWiki->azParent[0]); |
| 783 | }else{ |
| 784 | CX("null"); |
| 785 | } |
| 786 | if(!pWiki->zWiki || !pWiki->zWiki[0]){ |
| 787 | CX(", \"isEmpty\": true"); |
| 788 | } |
| 789 | if(includeContent){ |
| 790 | CX(", \"content\": %!j", pWiki->zWiki); |
| 791 | } |
| 792 | CX("}"); |
| 793 | fossil_free(zUuid); |
| 794 | manifest_destroy(pWiki); |
| 795 | return 2; |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | /* |
| 800 | ** Ajax route handler for /wikiajax/save. |
| 801 | ** |
| 802 | ** URL params: |
| 803 | ** |
| 804 | ** page = the wiki page name. |
| 805 | ** mimetype = content mime type. |
| 806 | ** content = page content. Fossil considers an empty page to |
| 807 | ** be "deleted". |
| 808 | ** isnew = 1 if the page is to be newly-created, else 0 or |
| 809 | ** not send. |
| 810 | ** |
| 811 | ** Responds with JSON. On error, an object in the form documented by |
| 812 | ** ajax_route_error(). On success, an object in the form documented |
| 813 | ** for wiki_ajax_emit_page_object(). |
| 814 | ** |
| 815 | ** The wikiajax API disallows saving of a sandbox pseudo-page, and |
| 816 | ** will respond with an error if asked to save one. Should we want to |
| 817 | ** enable it, it's implemented like this for any saved page for which |
| 818 | ** is_sandbox(zPageName) is true: |
| 819 | ** |
| 820 | ** db_set("sandbox",zBody,0); |
| 821 | ** db_set("sandbox-mimetype",zMimetype,0); |
| 822 | ** |
| 823 | */ |
| 824 | static void wiki_ajax_route_save(void){ |
| 825 | const char *zPageName = P("page"); |
| 826 | const char *zMimetype = P("mimetype"); |
| 827 | const char *zContent = P("content"); |
| 828 | const int isNew = ajax_p_bool("isnew"); |
| 829 | Blob content = empty_blob; |
| 830 | int parentRid = 0; |
| 831 | int rollback = 0; |
| 832 | |
| 833 | if(!wiki_ajax_can_write(zPageName, &parentRid)){ |
| 834 | return; |
| 835 | }else if(is_sandbox(zPageName)){ |
| 836 | ajax_route_error(403,"Saving a sandbox page is prohibited."); |
| 837 | return; |
| 838 | } |
| 839 | /* These isNew checks are just me being pedantic. We could just as |
| 840 | easily derive isNew based on whether or not the page already |
| 841 | exists. */ |
| 842 | if(isNew){ |
| 843 | if(parentRid>0){ |
| 844 | ajax_route_error(403,"Requested a new page, " |
| 845 | "but it already exists with RID %d: %s", |
| 846 | parentRid, zPageName); |
| 847 | return; |
| 848 | } |
| 849 | }else if(parentRid==0){ |
| 850 | ajax_route_error(403,"Creating new page [%s] requires passing " |
| 851 | "isnew=1.", zPageName); |
| 852 | return; |
| 853 | } |
| 854 | blob_init(&content, zContent ? zContent : "", -1); |
| 855 | cgi_set_content_type("application/json"); |
| 856 | db_begin_transaction(); |
| 857 | wiki_cmd_commit(zPageName, parentRid, &content, zMimetype, 0); |
| 858 | rollback = wiki_ajax_emit_page_object(zPageName, 1) ? 0 : 1; |
| 859 | db_end_transaction(rollback); |
| 860 | } |
| 861 | |
| 862 | /* |
| 863 | ** Ajax route handler for /wikiajax/fetch. |
| 864 | ** |
| 865 | ** URL params: |
| 866 | ** |
| 867 | ** page = the wiki page name |
| 868 | ** |
| 869 | ** Responds with JSON. On error, an object in the form documented by |
| 870 | ** ajax_route_error(). On success, an object in the form documented |
| 871 | ** for wiki_ajax_emit_page_object(). |
| 872 | */ |
| 873 | static void wiki_ajax_route_fetch(void){ |
| 874 | const char * zPageName = P("page"); |
| 875 | |
| 876 | if( zPageName==0 || zPageName[0]==0 ){ |
| 877 | ajax_route_error(400,"Missing page name."); |
| 878 | return; |
| 879 | } |
| 880 | cgi_set_content_type("application/json"); |
| 881 | wiki_ajax_emit_page_object(zPageName, 1); |
| 882 | } |
| 883 | |
| 884 | /* |
| 885 | ** Ajax route handler for /wikiajax/diff. |
| 886 | ** |
| 887 | ** URL params: |
| 888 | ** |
| 889 | ** page = the wiki page name |
| 890 | ** content = the new/edited wiki page content |
| 891 | ** |
| 892 | ** Requires that the user have write access solely to avoid some |
| 893 | ** potential abuse cases. It does not actually write anything. |
| 894 | */ |
| 895 | static void wiki_ajax_route_diff(void){ |
| 896 | const char * zPageName = P("page"); |
| 897 | Blob contentNew = empty_blob, contentOrig = empty_blob; |
| 898 | Manifest * pParent = 0; |
| 899 | const char * zContent = P("content"); |
| 900 | u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR; |
| 901 | |
| 902 | if( zPageName==0 || zPageName[0]==0 ){ |
| 903 | ajax_route_error(400,"Missing page name."); |
| 904 | return; |
| 905 | }else if(!wiki_ajax_can_write(zPageName, 0)){ |
| 906 | return; |
| 907 | } |
| 908 | switch(atoi(PD("sbs","0"))){ |
| 909 | case 0: diffFlags |= DIFF_LINENO; break; |
| 910 | default: diffFlags |= DIFF_SIDEBYSIDE; |
| 911 | } |
| 912 | switch(atoi(PD("ws","2"))){ |
| 913 | case 1: diffFlags |= DIFF_IGNORE_EOLWS; break; |
| 914 | case 2: diffFlags |= DIFF_IGNORE_ALLWS; break; |
| 915 | default: break; |
| 916 | } |
| 917 | wiki_fetch_by_name( zPageName, 0, 0, &pParent ); |
| 918 | if( pParent && pParent->zWiki && *pParent->zWiki ){ |
| 919 | blob_init(&contentOrig, pParent->zWiki, -1); |
| 920 | }else{ |
| 921 | blob_init(&contentOrig, "", 0); |
| 922 | } |
| 923 | blob_init(&contentNew, zContent ? zContent : "", -1); |
| 924 | cgi_set_content_type("text/html"); |
| 925 | ajax_render_diff(&contentOrig, &contentNew, diffFlags); |
| 926 | blob_reset(&contentNew); |
| 927 | blob_reset(&contentOrig); |
| 928 | manifest_destroy(pParent); |
| 929 | } |
| 930 | |
| 931 | /* |
| 932 | ** Ajax route handler for /wikiajax/preview. |
| 933 | ** |
| 934 | ** URL params: |
| 935 | ** |
| 936 | ** page = wiki page name. This is only needed for authorization |
| 937 | ** checking. |
| 938 | ** mimetype = the wiki page mimetype (determines rendering style) |
| 939 | ** content = the wiki page content |
| 940 | */ |
| 941 | static void wiki_ajax_route_preview(void){ |
| 942 | const char * zPageName = P("page"); |
| 943 | const char * zContent = P("content"); |
| 944 | |
| 945 | if(!wiki_ajax_can_write(zPageName, 0)){ |
| 946 | return; |
| 947 | }else if( zContent==0 ){ |
| 948 | ajax_route_error(400,"Missing content to preview."); |
| 949 | return; |
| 950 | }else{ |
| 951 | Blob content = empty_blob; |
| 952 | const char * zMimetype = PD("mimetype","text/x-fossil-wiki"); |
| 953 | |
| 954 | blob_init(&content, zContent, -1); |
| 955 | cgi_set_content_type("text/html"); |
| 956 | wiki_render_by_mimetype(&content, zMimetype); |
| 957 | blob_reset(&content); |
| 958 | } |
| 959 | } |
| 960 | |
| 961 | /* |
| 962 | ** Outputs the wiki page list in JSON form. If verbose is false then |
| 963 | ** it emits an array of strings (page names). If verbose is true it outputs |
| 964 | ** an array of objects in this form: |
| 965 | ** |
| 966 | ** { name: string, version: string or null of sandbox box, |
| 967 | ** parent: uuid or null for first version or sandbox, |
| 968 | ** mimetype: string, |
| 969 | ** type: string (normal, branch, tag, checkin, or sandbox) |
| 970 | ** } |
| 971 | ** |
| 972 | ** If includeContent is true, the object contains a "content" member |
| 973 | ** with the raw page content. includeContent is ignored if verbose is |
| 974 | ** false. |
| 975 | ** |
| 976 | */ |
| 977 | static void wiki_render_page_list_json(int verbose, int includeContent){ |
| 978 | Stmt q = empty_Stmt; |
| 979 | int n = 0; |
| 980 | db_begin_transaction(); |
| 981 | db_prepare(&q, "SELECT" |
| 982 | " substr(tagname,6) AS name" |
| 983 | " FROM tag WHERE tagname GLOB 'wiki-*'" |
| 984 | " UNION SELECT 'Sandbox' AS name" |
| 985 | " ORDER BY name COLLATE NOCASE"); |
| 986 | CX("["); |
| 987 | while( SQLITE_ROW==db_step(&q) ){ |
| 988 | char const * zName = db_column_text(&q,0); |
| 989 | if(n++){ |
| 990 | CX(","); |
| 991 | } |
| 992 | if(verbose==0){ |
| 993 | CX("%!j", zName); |
| 994 | }else{ |
| 995 | wiki_ajax_emit_page_object(zName, includeContent); |
| 996 | } |
| 997 | } |
| 998 | CX("]"); |
| 999 | db_finalize(&q); |
| 1000 | db_end_transaction(0); |
| 1001 | } |
| 1002 | |
| 1003 | /* |
| 1004 | ** Ajax route handler for /wikiajax/list. |
| 1005 | ** |
| 1006 | ** Optional parameters: verbose, includeContent (see below). |
| 1007 | ** |
| 1008 | ** Responds with JSON. On error, an object in the form documented by |
| 1009 | ** ajax_route_error(). |
| 1010 | ** |
| 1011 | ** On success, it emits an array of strings (page names) sorted |
| 1012 | ** case-insensitively. If the "verbose" parameter is passed in then |
| 1013 | ** the result list contains objects in the format documented for |
| 1014 | ** wiki_ajax_emit_page_object(). The content of each object is elided |
| 1015 | ** unless the "includeContent" parameter is passed on with a |
| 1016 | ** "non-false" value.. |
| 1017 | ** |
| 1018 | ** The result list always contains an entry named "Sandbox" which |
| 1019 | ** represents the sandbox pseudo-page. |
| 1020 | */ |
| 1021 | static void wiki_ajax_route_list(void){ |
| 1022 | const int verbose = ajax_p_bool("verbose"); |
| 1023 | const int includeContent = ajax_p_bool("includeContent"); |
| 1024 | |
| 1025 | cgi_set_content_type("application/json"); |
| 1026 | wiki_render_page_list_json(verbose, includeContent); |
| 1027 | } |
| 1028 | |
| 1029 | /* |
| 1030 | ** WEBPAGE: wikiajax |
| 1031 | ** |
| 1032 | ** An internal dispatcher for wiki AJAX operations. Not for direct |
| 1033 | ** client use. All routes defined by this interface are app-internal, |
| 1034 | ** subject to change |
| 1035 | */ |
| 1036 | void wiki_ajax_page(void){ |
| 1037 | const char * zName = P("name"); |
| 1038 | AjaxRoute routeName = {0,0,0,0}; |
| 1039 | const AjaxRoute * pRoute = 0; |
| 1040 | const AjaxRoute routes[] = { |
| 1041 | /* Keep these sorted by zName (for bsearch()) */ |
| 1042 | {"diff", wiki_ajax_route_diff, 1, 1}, |
| 1043 | {"fetch", wiki_ajax_route_fetch, 0, 0}, |
| 1044 | {"list", wiki_ajax_route_list, 0, 0}, |
| 1045 | {"preview", wiki_ajax_route_preview, 0, 1} |
| 1046 | /* preview access mode: whether or not wiki-write mode is needed |
| 1047 | really depends on multiple factors. e.g. the sandbox page does |
| 1048 | not normally require more than anonymous access. We set its |
| 1049 | write-mode to false and do those checks manually in that route's |
| 1050 | handler. |
| 1051 | */, |
| 1052 | {"save", wiki_ajax_route_save, 1, 1} |
| 1053 | }; |
| 1054 | |
| 1055 | if(zName==0 || zName[0]==0){ |
| 1056 | ajax_route_error(400,"Missing required [route] 'name' parameter."); |
| 1057 | return; |
| 1058 | } |
| 1059 | routeName.zName = zName; |
| 1060 | pRoute = (const AjaxRoute *)bsearch(&routeName, routes, |
| 1061 | count(routes), sizeof routes[0], |
| 1062 | cmp_ajax_route_name); |
| 1063 | if(pRoute==0){ |
| 1064 | ajax_route_error(404,"Ajax route not found."); |
| 1065 | return; |
| 1066 | } |
| 1067 | login_check_credentials(); |
| 1068 | if( pRoute->bWriteMode!=0 && g.perm.WrWiki==0 ){ |
| 1069 | ajax_route_error(403,"Write permissions required."); |
| 1070 | return; |
| 1071 | }else if(0==cgi_csrf_safe(pRoute->bPost)){ |
| 1072 | ajax_route_error(403, |
| 1073 | "CSRF violation (make sure sending of HTTP " |
| 1074 | "Referer headers is enabled for XHR " |
| 1075 | "connections)."); |
| 1076 | return; |
| 1077 | } |
| 1078 | pRoute->xCallback(); |
| 1079 | } |
| 1080 | |
| 1081 | /* |
| 1082 | ** WEBPAGE: wikiedit |
| 1083 | ** URL: /wikedit?name=PAGENAME |
| 1084 | ** |
| 1085 | ** The main front-end for the Ajax-based wiki editor app. Passing |
| 1086 | ** in the name of an unknown page will trigger the creation |
| 1087 | ** of a new page (which is not actually created in the database |
| 1088 | ** until the user explicitly saves it). If passed no page name, |
| 1089 | ** the user may select a page from the list on the first UI tab. |
| 1090 | ** |
| 1091 | ** When creating a new page, the mimetype URL parameter may optionally |
| 1092 | ** be used to set its mimetype to one of text/x-fossil-wiki, |
| 1093 | ** text/x-markdown, or text/plain, defauling to the former. |
| 1094 | */ |
| 1095 | void wikiedit_page(void){ |
| 1096 | const char *zPageName; |
| 1097 | const char * zMimetype = P("mimetype"); |
| 1098 | int isSandbox; |
| 1099 | int found = 0; |
| 1100 | |
| 1101 | login_check_credentials(); |
| 1102 | zPageName = PD("name",""); |
| 1103 | if(zPageName && *zPageName){ |
| 1104 | if( check_name(zPageName) ) return; |
| 1105 | } |
| 1106 | isSandbox = is_sandbox(zPageName); |
| 1107 | if( isSandbox ){ |
| 1108 | if( !g.perm.WrWiki ){ |
| 1109 | login_needed(g.anon.WrWiki); |
| 1110 | return; |
| 1111 | } |
| 1112 | found = 1; |
| 1113 | }else if( zPageName!=0 ){ |
| 1114 | int rid = 0; |
| 1115 | if( !wiki_special_permission(zPageName) ){ |
| 1116 | login_needed(0); |
| 1117 | return; |
| 1118 | } |
| 1119 | found = wiki_fetch_by_name(zPageName, 0, &rid, 0); |
| 1120 | if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){ |
| 1121 | login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki); |
| 1122 | return; |
| 1123 | } |
| 1124 | } |
| 1125 | style_header("Wiki Editor"); |
| 1126 | |
| 1127 | /* Status bar */ |
| 1128 | CX("<div id='fossil-status-bar' " |
| 1129 | "title='Status message area. Double-click to clear them.'>" |
| 1130 | "Status messages will go here.</div>\n" |
| 1131 | /* will be moved into the tab container via JS */); |
| 1132 | |
| 1133 | CX("<div id='wikiedit-edit-status''>" |
| 1134 | "<span class='name'></span>" |
| 1135 | "<span class='links'></span>" |
| 1136 | "</div>"); |
| 1137 | |
| 1138 | /* Main tab container... */ |
| 1139 | CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>"); |
| 1140 | /* The .hidden class on the following tab elements is to help lessen |
| 1141 | the FOUC effect of the tabs before JS re-assembles them. */ |
| 1142 | |
| 1143 | /******* Page list *******/ |
| 1144 | { |
| 1145 | CX("<div id='wikiedit-tab-pages' " |
| 1146 | "data-tab-parent='wikiedit-tabs' " |
| 1147 | "data-tab-label='Wiki Page List' " |
| 1148 | "class='hidden'" |
| 1149 | ">"); |
| 1150 | CX("<div>Loading wiki pages list...</div>"); |
| 1151 | CX("</div>"/*#wikiedit-tab-pages*/); |
| 1152 | } |
| 1153 | |
| 1154 | /******* Content tab *******/ |
| 1155 | { |
| 1156 | CX("<div id='wikiedit-tab-content' " |
| 1157 | "data-tab-parent='wikiedit-tabs' " |
| 1158 | "data-tab-label='Editor' " |
| 1159 | "class='hidden'" |
| 1160 | ">"); |
| 1161 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1162 | CX("<span class='input-with-label'>" |
| 1163 | "<label>Mime type</label>"); |
| 1164 | mimetype_option_menu(0); |
| 1165 | CX("</span>"); |
| 1166 | style_select_list_int("select-font-size", |
| 1167 | "editor_font_size", "Editor font size", |
| 1168 | NULL/*tooltip*/, |
| 1169 | 100, |
| 1170 | "100%", 100, "125%", 125, |
| 1171 | "150%", 150, "175%", 175, |
| 1172 | "200%", 200, NULL); |
| 1173 | CX("<button class='wikiedit-save'>" |
| 1174 | "Save</button>" |
| 1175 | /*will get moved around dynamically*/); |
| 1176 | CX("<button class='wikiedit-save-close'>" |
| 1177 | "Save & Close</button>"/*will get moved around dynamically*/); |
| 1178 | CX("<span class='save-button-slot'></span>"); |
| 1179 | CX("<button class='wikiedit-content-reload' " |
| 1180 | "title='Reload the file from the server, discarding " |
| 1181 | "any local edits. To help avoid accidental loss of " |
| 1182 | "edits, it requires confirmation (a second click) within " |
| 1183 | "a few seconds or it will not reload.'" |
| 1184 | ">Discard & Reload</button>"); |
| 1185 | CX("</div>"); |
| 1186 | CX("<div class='flex-container flex-column stretch'>"); |
| 1187 | CX("<textarea name='content' id='wikiedit-content-editor' " |
| 1188 | "class='wikiedit' rows='25'>"); |
| 1189 | CX("</textarea>"); |
| 1190 | CX("</div>"/*textarea wrapper*/); |
| 1191 | CX("</div>"/*#tab-file-content*/); |
| 1192 | } |
| 1193 | /****** Preview tab ******/ |
| 1194 | { |
| 1195 | CX("<div id='wikiedit-tab-preview' " |
| 1196 | "data-tab-parent='wikiedit-tabs' " |
| 1197 | "data-tab-label='Preview' " |
| 1198 | "class='hidden'" |
| 1199 | ">"); |
| 1200 | CX("<div class='wikiedit-options flex-container " |
| 1201 | "flex-row child-gap-small'>"); |
| 1202 | CX("<button id='btn-preview-refresh' " |
| 1203 | "data-f-preview-from='wikiContent' " |
| 1204 | /* ^^^ fossil.page[methodName]() OR text source elem ID, |
| 1205 | ** but we need a method in order to support clients swapping out |
| 1206 | ** the text editor with their own. */ |
| 1207 | "data-f-preview-via='_postPreview' " |
| 1208 | /* ^^^ fossil.page[methodName](content, callback) */ |
| 1209 | "data-f-preview-to='#wikiedit-tab-preview-wrapper' " |
| 1210 | /* ^^^ dest elem ID */ |
| 1211 | ">Refresh</button>"); |
| 1212 | /* Toggle auto-update of preview when the Preview tab is selected. */ |
| 1213 | style_labeled_checkbox("cb-preview-autoupdate", |
| 1214 | NULL, |
| 1215 | "Auto-refresh?", |
| 1216 | "1", 1, |
| 1217 | "If on, the preview will automatically " |
| 1218 | "refresh when this tab is selected."); |
| 1219 | CX("<span class='save-button-slot'></span>"); |
| 1220 | CX("</div>"/*.wikiedit-options*/); |
| 1221 | CX("<div id='wikiedit-tab-preview-wrapper'></div>"); |
| 1222 | CX("</div>"/*#wikiedit-tab-preview*/); |
| 1223 | } |
| 1224 | |
| 1225 | /****** Diff tab ******/ |
| 1226 | { |
| 1227 | CX("<div id='wikiedit-tab-diff' " |
| 1228 | "data-tab-parent='wikiedit-tabs' " |
| 1229 | "data-tab-label='Diff' " |
| 1230 | "class='hidden'" |
| 1231 | ">"); |
| 1232 | |
| 1233 | CX("<div class='wikiedit-options flex-container " |
| 1234 | "flex-row child-gap-small' " |
| 1235 | "id='wikiedit-tab-diff-buttons'>"); |
| 1236 | CX("<button class='sbs'>Side-by-side</button>" |
| 1237 | "<button class='unified'>Unified</button>"); |
| 1238 | CX("<span class='save-button-slot'></span>"); |
| 1239 | CX("</div>"); |
| 1240 | CX("<div id='wikiedit-tab-diff-wrapper'>" |
| 1241 | "Diffs will be shown here." |
| 1242 | "</div>"); |
| 1243 | CX("</div>"/*#wikiedit-tab-diff*/); |
| 1244 | } |
| 1245 | |
| 1246 | /****** The obligatory "Misc" tab ******/ |
| 1247 | { |
| 1248 | CX("<div id='wikiedit-tab-misc' " |
| 1249 | "data-tab-parent='wikiedit-tabs' " |
| 1250 | "data-tab-label='Help' " |
| 1251 | "class='hidden'" |
| 1252 | ">"); |
| 1253 | CX("<h2>Wiki formatting rules</h2>"); |
| 1254 | CX("<ul>"); |
| 1255 | CX("<li><a href='%R/wiki_rules'>Fossil wiki format</a></li>"); |
| 1256 | CX("<li><a href='%R/md_rules'>Markdown format</a></li>"); |
| 1257 | CX("<li>Plain-text pages use no special formatting.</li>"); |
| 1258 | CX("</ul>"); |
| 1259 | CX("<h2>The \"Sandbox\" Page</h2>"); |
| 1260 | CX("<p>The page named \"Sandbox\" is not a real wiki page. " |
| 1261 | "It provides a place where users may test out wiki syntax " |
| 1262 | "without having to actually save anything, nor pollute " |
| 1263 | "the repo with endless test runs. Any attempt to save the " |
| 1264 | "sandbox page will fail.</p>"); |
| 1265 | CX("<h2>Wiki Name Rules</h2>"); |
| 1266 | well_formed_wiki_name_rules(); |
| 1267 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1268 | } |
| 1269 | |
| 1270 | builtin_request_js("sbsdiff.js"); |
| 1271 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1272 | "storage", "page.wikiedit", 0); |
| 1273 | builtin_fulfill_js_requests(); |
| 1274 | /* Dynamically populate the editor... */ |
| 1275 | style_emit_script_tag(0,0); |
| 1276 | { |
| 1277 | /* Render the current page list to save us an XHR request |
| 1278 | during page initialization. This must be OUTSIDE of |
| 1279 | an onPageLoad() handler or else it does not get applied |
| 1280 | until after the wiki list widget is initialized. Similarly, |
| 1281 | it must come *after* window.fossil is initialized. */ |
| 1282 | CX("\nfossil.page.initialPageList = "); |
| 1283 | wiki_render_page_list_json(1, 0); |
| 1284 | CX(";\n"); |
| 1285 | } |
| 1286 | CX("fossil.onPageLoad(function(){\n"); |
| 1287 | CX("const P = fossil.page;\n" |
| 1288 | "try{\n"); |
| 1289 | if(!found && zPageName && *zPageName){ |
| 1290 | /* For a new page, stick a dummy entry in the JS-side stash |
| 1291 | and "load" it from there. */ |
| 1292 | CX("const winfo = {" |
| 1293 | "\"name\": %!j, \"mimetype\": %!j, " |
| 1294 | "\"type\": %!j, " |
| 1295 | "\"parent\": null, \"version\": null" |
| 1296 | "};\n", |
| 1297 | zPageName, |
| 1298 | zMimetype ? zMimetype : "text/x-fossil-wiki", |
| 1299 | wiki_page_type_name(zPageName)); |
| 1300 | /* If the JS-side stash already has this page, load that |
| 1301 | copy from the stash, otherwise inject a new stash entry |
| 1302 | for it and load *that* one... */ |
| 1303 | CX("if(!P.$stash.getWinfo(winfo)){" |
| 1304 | "P.$stash.updateWinfo(winfo,'');" |
| 1305 | "}\n"); |
| 1306 | } |
| 1307 | if(zPageName && *zPageName){ |
| 1308 | CX("P.loadPage(%!j);\n", zPageName); |
| 1309 | } |
| 1310 | CX("}catch(e){" |
| 1311 | "fossil.error(e); console.error('Exception:',e);" |
| 1312 | "}\n"); |
| 1313 | CX("});\n"/*fossil.onPageLoad()*/); |
| 1314 | style_emit_script_tag(1,0); |
| 1315 | style_footer(); |
| 1316 | } |
| 1317 | |
| 1318 | /* |
| 1319 | ** WEBPAGE: wikinew |
| @@ -851,17 +1331,11 @@ | |
| 1331 | return; |
| 1332 | } |
| 1333 | zName = PD("name",""); |
| 1334 | zMimetype = wiki_filter_mimetypes(P("mimetype")); |
| 1335 | if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ |
| 1336 | cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); |
| 1337 | } |
| 1338 | style_header("Create A New Wiki Page"); |
| 1339 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1340 | @ <p>Rules for wiki page names:</p> |
| 1341 | well_formed_wiki_name_rules(); |
| @@ -1441,11 +1915,11 @@ | |
| 1915 | ** -M|--mimetype TEXT-FORMAT The mime type of the update. |
| 1916 | ** Defaults to the type used by |
| 1917 | ** the previous version of the |
| 1918 | ** page, or text/x-fossil-wiki. |
| 1919 | ** Valid values are: text/x-fossil-wiki, |
| 1920 | ** text/x-markdown and text/plain. fossil, |
| 1921 | ** markdown or plain can be specified as |
| 1922 | ** synonyms of these values. |
| 1923 | ** -t|--technote DATETIME Specifies the timestamp of |
| 1924 | ** the technote to be created or |
| 1925 | ** updated. When updating a tech note |
| @@ -1479,13 +1953,18 @@ | |
| 1953 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 1954 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 1955 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 1956 | ** means UTC. |
| 1957 | ** |
| 1958 | ** The "Sandbox" wiki pseudo-page is a special case. Its name is |
| 1959 | ** checked case-insensitively and either "create" or "commit" may be |
| 1960 | ** used to update its contents. |
| 1961 | */ |
| 1962 | void wiki_cmd(void){ |
| 1963 | int n; |
| 1964 | int isSandbox = 0; /* true if dealing with sandbox pseudo-page */ |
| 1965 | |
| 1966 | db_find_and_open_repository(0, 0); |
| 1967 | if( g.argc<3 ){ |
| 1968 | goto wiki_cmd_usage; |
| 1969 | } |
| 1970 | n = strlen(g.argv[2]); |
| @@ -1492,14 +1971,14 @@ | |
| 1971 | if( n==0 ){ |
| 1972 | goto wiki_cmd_usage; |
| 1973 | } |
| 1974 | |
| 1975 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 1976 | const char *zPageName = 0; /* Name of the wiki page to export */ |
| 1977 | const char *zFile; /* Name of the output file (0=stdout) */ |
| 1978 | const char *zETime; /* The name of the technote to export */ |
| 1979 | int rid = 0; /* Artifact ID of the wiki page */ |
| 1980 | int i; /* Loop counter */ |
| 1981 | char *zBody = 0; /* Wiki page content */ |
| 1982 | Blob body = empty_blob; /* Wiki page content */ |
| 1983 | Manifest *pWiki = 0; /* Parsed wiki page content */ |
| 1984 | int fHtml = 0; /* Export in HTML form */ |
| @@ -1517,17 +1996,18 @@ | |
| 1996 | if( !zETime ){ |
| 1997 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 1998 | usage("export ?-html? PAGENAME ?FILE?"); |
| 1999 | } |
| 2000 | zPageName = g.argv[3]; |
| 2001 | isSandbox = is_sandbox(zPageName); |
| 2002 | if(isSandbox){ |
| 2003 | zBody = db_get("sandbox", 0); |
| 2004 | }else{ |
| 2005 | wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); |
| 2006 | if(pWiki){ |
| 2007 | zBody = pWiki->zWiki; |
| 2008 | } |
| 2009 | } |
| 2010 | if( zBody==0 ){ |
| 2011 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 2012 | } |
| 2013 | zFile = (g.argc==4) ? "-" : g.argv[4]; |
| @@ -1553,30 +2033,24 @@ | |
| 2033 | blob_init(&body, zBody, -1); |
| 2034 | if(fHtml==0){ |
| 2035 | blob_append(&body, "\n", 1); |
| 2036 | }else{ |
| 2037 | Blob html = empty_blob; /* HTML-ized content */ |
| 2038 | const char * zMimetype = isSandbox |
| 2039 | ? db_get("sandbox-mimetype", "text/x-fossil-wiki") |
| 2040 | : wiki_filter_mimetypes(pWiki->zMimetype); |
| 2041 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 2042 | wiki_convert(&body,&html,0); |
| 2043 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 2044 | markdown_to_html(&body,0,&html); |
| 2045 | safe_html_context(DOCSRC_WIKI); |
| 2046 | safe_html(&html); |
| 2047 | }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ |
| 2048 | htmlize_to_blob(&html,zBody,i); |
| 2049 | }else{ |
| 2050 | fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", |
| 2051 | zMimetype, pWiki ? pWiki->zWikiTitle : zPageName ); |
| 2052 | } |
| 2053 | blob_reset(&body); |
| 2054 | body = html /* transfer memory */; |
| 2055 | } |
| 2056 | pFile = fossil_fopen_for_output(zFile); |
| @@ -1618,28 +2092,28 @@ | |
| 2092 | if( g.argc==4 ){ |
| 2093 | blob_read_from_channel(&content, stdin, -1); |
| 2094 | }else{ |
| 2095 | blob_read_from_file(&content, g.argv[4], ExtFILE); |
| 2096 | } |
| 2097 | isSandbox = is_sandbox(zPageName); |
| 2098 | if ( !zETime ){ |
| 2099 | if( !isSandbox ){ |
| 2100 | wiki_fetch_by_name(zPageName, 0, &rid, &pWiki); |
| 2101 | } |
| 2102 | }else{ |
| 2103 | rid = wiki_technote_to_rid(zETime); |
| 2104 | if( rid>0 ){ |
| 2105 | pWiki = manifest_get(rid, CFTYPE_EVENT, 0); |
| 2106 | } |
| 2107 | } |
| 2108 | if( !zMimeType || !*zMimeType ){ |
| 2109 | /* Try to deduce the mime type based on the prior version. */ |
| 2110 | if(isSandbox){ |
| 2111 | zMimeType = |
| 2112 | wiki_filter_mimetypes(db_get("sandbox-mimetype", |
| 2113 | "text/x-fossil-wiki")); |
| 2114 | }else if( pWiki!=0 && (pWiki->zMimetype && *pWiki->zMimetype) ){ |
| 2115 | zMimeType = pWiki->zMimetype; |
| 2116 | } |
| 2117 | }else{ |
| 2118 | zMimeType = wiki_filter_mimetypes(zMimeType); |
| 2119 | } |
| @@ -1649,24 +2123,30 @@ | |
| 2123 | }else{ |
| 2124 | /* Creating a tech note with same timestamp is permitted |
| 2125 | and should create a new tech note */ |
| 2126 | rid = 0; |
| 2127 | } |
| 2128 | }else if( !isCreate && rid==0 && isSandbox==0 ){ |
| 2129 | if ( !zETime ){ |
| 2130 | fossil_fatal("no such wiki page: %s", zPageName); |
| 2131 | }else{ |
| 2132 | fossil_fatal("no such tech note: %s", zETime); |
| 2133 | } |
| 2134 | } |
| 2135 | |
| 2136 | if( !zETime ){ |
| 2137 | if(isSandbox){ |
| 2138 | db_set("sandbox",blob_str(&content),0); |
| 2139 | db_set("sandbox-mimetype",zMimeType,0); |
| 2140 | fossil_print("Updated sandbox pseudo-page.\n"); |
| 2141 | }else{ |
| 2142 | wiki_cmd_commit(zPageName, rid, &content, zMimeType, 1); |
| 2143 | if( g.argv[2][1]=='r' ){ |
| 2144 | fossil_print("Created new wiki page %s.\n", zPageName); |
| 2145 | }else{ |
| 2146 | fossil_print("Updated wiki page %s.\n", zPageName); |
| 2147 | } |
| 2148 | } |
| 2149 | }else{ |
| 2150 | if( rid != -1 ){ |
| 2151 | char *zMETime; /* Normalized, mutable version of zETime */ |
| 2152 | zMETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", |
| @@ -1844,8 +2324,8 @@ | |
| 2324 | blob_reset(&tail); |
| 2325 | blob_reset(&title); |
| 2326 | blob_reset(&wiki); |
| 2327 | } |
| 2328 | manifest_destroy(pWiki); |
| 2329 | builtin_request_js("accordion.js"); |
| 2330 | return 1; |
| 2331 | } |
| 2332 | |
| 2333 | ELETED src/wysiwyg.c |
D
src/wysiwyg.c
-328
| --- a/src/wysiwyg.c | ||
| +++ b/src/wysiwyg.c | ||
| @@ -1,328 +0,0 @@ | ||
| 1 | -/* | |
| 2 | -** Copyright (c) 2012 D. Richard Hipp | |
| 3 | -** | |
| 4 | -** This program is free software; you can redistribute it and/or | |
| 5 | -** modify it under the terms of the Simplified BSD License (also | |
| 6 | -** known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | -** | |
| 8 | -** This program is distributed in the hope that it will be useful, | |
| 9 | -** but without any warranty; without even the implied warranty of | |
| 10 | -** merchantability or fitness for a particular purpose. | |
| 11 | -** | |
| 12 | -** Author contact information: | |
| 13 | -** [email protected] | |
| 14 | -** http://www.hwaci.com/drh/ | |
| 15 | -** | |
| 16 | -******************************************************************************* | |
| 17 | -** | |
| 18 | -** This file contains code that generates WYSIWYG text editors on | |
| 19 | -** web pages. | |
| 20 | -*/ | |
| 21 | -#include "config.h" | |
| 22 | -#include <assert.h> | |
| 23 | -#include <ctype.h> | |
| 24 | -#include "wysiwyg.h" | |
| 25 | - | |
| 26 | - | |
| 27 | -/* | |
| 28 | -** Output code for a WYSIWYG editor. The caller must have already generated | |
| 29 | -** the <form> that will contain the editor, and the call must generate the | |
| 30 | -** corresponding </form> after this routine returns. The caller must include | |
| 31 | -** an onsubmit= attribute on the <form> element that invokes the | |
| 32 | -** wysiwygSubmit() function. | |
| 33 | -** | |
| 34 | -** There can only be a single WYSIWYG editor per frame. | |
| 35 | -*/ | |
| 36 | -void wysiwygEditor( | |
| 37 | - const char *zId, /* ID for this editor */ | |
| 38 | - const char *zContent, /* Initial content (HTML) */ | |
| 39 | - int w, int h /* Initial width and height */ | |
| 40 | -){ | |
| 41 | - | |
| 42 | - @ <style type="text/css"> | |
| 43 | - @ .intLink { cursor: pointer; } | |
| 44 | - @ img.intLink { border: 0; } | |
| 45 | - @ #wysiwygBox { | |
| 46 | - @ border: 1px #000000 solid; | |
| 47 | - @ padding: 12px; | |
| 48 | - @ } | |
| 49 | - @ #editMode label { cursor: pointer; } | |
| 50 | - @ </style> | |
| 51 | - | |
| 52 | - @ <input id="wysiwygValue" type="hidden" name="%s(zId)"> | |
| 53 | - @ <div id="editModeDiv">Edit mode: | |
| 54 | - @ <select id="editMode" size=1> | |
| 55 | - @ <option value="0">WYSIWYG</option> | |
| 56 | - @ <option value="1">Raw HTML</option> | |
| 57 | - @ </select></div> | |
| 58 | - @ <div id="toolBar1"> | |
| 59 | - @ <select class="format" data-format="formatblock"> | |
| 60 | - @ <option selected>- formatting -</option> | |
| 61 | - @ <option value="h1">Title 1 <h1></option> | |
| 62 | - @ <option value="h2">Title 2 <h2></option> | |
| 63 | - @ <option value="h3">Title 3 <h3></option> | |
| 64 | - @ <option value="h4">Title 4 <h4></option> | |
| 65 | - @ <option value="h5">Title 5 <h5></option> | |
| 66 | - @ <option value="h6">Subtitle <h6></option> | |
| 67 | - @ <option value="p">Paragraph <p></option> | |
| 68 | - @ <option value="pre">Preformatted <pre></option> | |
| 69 | - @ </select> | |
| 70 | - @ <select class="format" data-format="fontname"> | |
| 71 | - @ <option class="heading" selected>- font -</option> | |
| 72 | - @ <option>Arial</option> | |
| 73 | - @ <option>Arial Black</option> | |
| 74 | - @ <option>Courier New</option> | |
| 75 | - @ <option>Times New Roman</option> | |
| 76 | - @ </select> | |
| 77 | - @ <select class="format" data-format="fontsize"> | |
| 78 | - @ <option class="heading" selected>- size -</option> | |
| 79 | - @ <option value="1">Very small</option> | |
| 80 | - @ <option value="2">A bit small</option> | |
| 81 | - @ <option value="3">Normal</option> | |
| 82 | - @ <option value="4">Medium-large</option> | |
| 83 | - @ <option value="5">Big</option> | |
| 84 | - @ <option value="6">Very big</option> | |
| 85 | - @ <option value="7">Maximum</option> | |
| 86 | - @ </select> | |
| 87 | - @ <select class="format" data-format="forecolor"> | |
| 88 | - @ <option class="heading" selected>- color -</option> | |
| 89 | - @ <option value="red">Red</option> | |
| 90 | - @ <option value="blue">Blue</option> | |
| 91 | - @ <option value="green">Green</option> | |
| 92 | - @ <option value="black">Black</option> | |
| 93 | - @ </select> | |
| 94 | - @ </div> | |
| 95 | - @ <div id="toolBar2"> | |
| 96 | - @ <img class="intLink" title="Undo" data-format="undo" | |
| 97 | - @ src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7 | |
| 98 | - @ /I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680 | |
| 99 | - @ 7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE | |
| 100 | - @ whdRIH4fh/DZMICe3/C4nBQBADs="> | |
| 101 | - | |
| 102 | - @ <img class="intLink" title="Redo" data-format="redo" | |
| 103 | - @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/ | |
| 104 | - @ ///yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na | |
| 105 | - @ EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw=="> | |
| 106 | - | |
| 107 | - @ <img class="intLink" title="Remove formatting" data-format="removeFormat" | |
| 108 | - @ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA | |
| 109 | - @ AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA | |
| 110 | - @ AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5 | |
| 111 | - @ 01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG | |
| 112 | - @ Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz | |
| 113 | - @ deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95 | |
| 114 | - @ DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2C | |
| 115 | - @ lrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA | |
| 116 | - @ wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n | |
| 117 | - @ 7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1 | |
| 118 | - @ lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj | |
| 119 | - @ 5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf | |
| 120 | - @ fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj | |
| 121 | - @ b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCc | |
| 122 | - @ XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66 | |
| 123 | - @ 6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH | |
| 124 | - @ 6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg=="> | |
| 125 | - | |
| 126 | - @ <img class="intLink" title="Bold" data-format="bold" | |
| 127 | - @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB | |
| 128 | - @ YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" /> | |
| 129 | - | |
| 130 | - @ <img class="intLink" title="Italic" data-format="italic" | |
| 131 | - @ src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALA | |
| 132 | - @ AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" /> | |
| 133 | - | |
| 134 | - @ <img class="intLink" title="Underline" data-format="underline" | |
| 135 | - @ src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALA | |
| 136 | - @ AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA | |
| 137 | - @ 7" /> | |
| 138 | - | |
| 139 | - @ <img class="intLink" title="Left align" data-format="justifyleft" | |
| 140 | - @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB | |
| 141 | - @ YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" /> | |
| 142 | - | |
| 143 | - @ <img class="intLink" title="Center align" data-format="justifycenter" | |
| 144 | - @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB | |
| 145 | - @ YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" /> | |
| 146 | - | |
| 147 | - @ <img class="intLink" title="Right align" data-format="justifyright" | |
| 148 | - @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB | |
| 149 | - @ YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" /> | |
| 150 | - @ <img class="intLink" title="Numbered list" | |
| 151 | - @ data-format="insertorderedlist" | |
| 152 | - @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P//// | |
| 153 | - @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO | |
| 154 | - @ zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" /> | |
| 155 | - | |
| 156 | - @ <img class="intLink" title="Dotted list" | |
| 157 | - @ data-format="insertunorderedlist" | |
| 158 | - @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv//// | |
| 159 | - @ ///yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6 | |
| 160 | - @ 4UwiuKnigGQliQuWOyKQykgAAOw==" /> | |
| 161 | - | |
| 162 | - @ <img class="intLink" title="Quote" data-format="formatblock" | |
| 163 | - @ src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2 | |
| 164 | - @ R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P///////// | |
| 165 | - @ //////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk | |
| 166 | - @ ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT | |
| 167 | - @ q/b7/i8fp8PAQA7" /> | |
| 168 | - | |
| 169 | - @ <img class="intLink" title="Delete indentation" data-format="outdent" | |
| 170 | - @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P | |
| 171 | - @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz | |
| 172 | - @ sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" /> | |
| 173 | - | |
| 174 | - @ <img class="intLink" title="Add indentation" data-format="indent" | |
| 175 | - @ src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N | |
| 176 | - @ Ha7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650 | |
| 177 | - @ B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw=="> | |
| 178 | - | |
| 179 | - @ <img class="intLink" title="Hyperlink" data-format="createlink" | |
| 180 | - @ src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb | |
| 181 | - @ /I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682 | |
| 182 | - @ 7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG | |
| 183 | - @ i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" /> | |
| 184 | - | |
| 185 | -#if 0 /* Cut/Copy/Paste requires special browser permissions for security | |
| 186 | - ** reasons. So omit these buttons */ | |
| 187 | - @ <img class="intLink" title="Cut" data-format="cut" | |
| 188 | - @ src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1 | |
| 189 | - @ dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P///////////////////////////// | |
| 190 | - @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE | |
| 191 | - @ bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9 | |
| 192 | - @ ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" /> | |
| 193 | - | |
| 194 | - @ <img class="intLink" title="Copy" data-format="copy" | |
| 195 | - @ src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31 | |
| 196 | - @ iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP | |
| 197 | - @ s++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/ | |
| 198 | - @ kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ | |
| 199 | - @ ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA | |
| 200 | - @ Grpy+wsbKzIiEAOw==" /> | |
| 201 | - | |
| 202 | - @ <img class="intLink" title="Paste" data-format="paste" | |
| 203 | - @ src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp | |
| 204 | - @ qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f///////////////////// | |
| 205 | - @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB | |
| 206 | - @ SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ | |
| 207 | - @ o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA | |
| 208 | - @ MOaK+bLAOrtLUyt7i5uiUhADs=" /> | |
| 209 | -#endif | |
| 210 | - | |
| 211 | - @ </div> | |
| 212 | - @ <div id="wysiwygBox" | |
| 213 | - @ style="resize:both;overflow:auto;width:95%%;min-height:%d(h)em;" | |
| 214 | - @ contenteditable="true">%s(zContent)</div> | |
| 215 | - @ <script nonce="%h(style_nonce())"> | |
| 216 | - @ var oDoc; | |
| 217 | - @ | |
| 218 | - @ /* Initialize the document editor */ | |
| 219 | - @ function initDoc() { | |
| 220 | - @ initEventHandlers(); | |
| 221 | - @ oDoc = document.getElementById("wysiwygBox"); | |
| 222 | - @ if (!isWysiwyg()) { setDocMode(true); } | |
| 223 | - @ } | |
| 224 | - @ | |
| 225 | - @ function initEventHandlers() { | |
| 226 | - @ document.querySelector('form').onsubmit = wysiwygSubmit; | |
| 227 | - @ document.querySelector('#editMode').onchange = function() { | |
| 228 | - @ setDocMode(this.selectedIndex) | |
| 229 | - @ }; | |
| 230 | - @ var controls = document.querySelectorAll('select.format'); | |
| 231 | - @ for(var i = 0; i < controls.length; i++) { | |
| 232 | - @ controls[i].onchange = handleDropDown; | |
| 233 | - @ } | |
| 234 | - @ controls = document.querySelectorAll('.intLink'); | |
| 235 | - @ for(i = 0; i < controls.length; i++) { | |
| 236 | - @ controls[i].onclick = handleFormatButton; | |
| 237 | - @ } | |
| 238 | - @ | |
| 239 | - @ function handleDropDown() { | |
| 240 | - @ formatDoc(this.dataset.format,this[this.selectedIndex].value); | |
| 241 | - @ this.selectedIndex = 0; | |
| 242 | - @ } | |
| 243 | - @ | |
| 244 | - @ function handleFormatButton() { | |
| 245 | - @ var extra; | |
| 246 | - @ switch (this.dataset.format) { | |
| 247 | - @ case 'createlink': | |
| 248 | - @ var sLnk = prompt('Target URL:',''); | |
| 249 | - @ if(sLnk && sLnk != '') | |
| 250 | - @ { | |
| 251 | - @ extra = sLnk; | |
| 252 | - @ } | |
| 253 | - @ break; | |
| 254 | - @ case 'formatblock': | |
| 255 | - @ extra = 'blockquote'; | |
| 256 | - @ break; | |
| 257 | - @ } | |
| 258 | - @ formatDoc(this.dataset.format, extra); | |
| 259 | - @ } | |
| 260 | - @ } | |
| 261 | - @ | |
| 262 | - @ /* Return true if the document editor is in WYSIWYG mode. Return | |
| 263 | - @ ** false if it is in Markup mode */ | |
| 264 | - @ function isWysiwyg() { | |
| 265 | - @ return document.getElementById("editMode").selectedIndex==0; | |
| 266 | - @ } | |
| 267 | - @ | |
| 268 | - @ /* Invoke this routine prior to submitting the HTML content back | |
| 269 | - @ ** to the server */ | |
| 270 | - @ function wysiwygSubmit() { | |
| 271 | - @ if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);} | |
| 272 | - @ document.getElementById("wysiwygValue").value=oDoc.innerHTML; | |
| 273 | - @ } | |
| 274 | - @ | |
| 275 | - @ /* Run the editing command if in WYSIWYG mode */ | |
| 276 | - @ function formatDoc(sCmd, sValue) { | |
| 277 | - @ if (isWysiwyg()){ | |
| 278 | - @ try { | |
| 279 | - @ // First, try the W3C draft standard way, which has | |
| 280 | - @ // been working on all non-IE browsers for a while. | |
| 281 | - @ // It is also supported by IE11 and higher. | |
| 282 | - @ document.execCommand("styleWithCSS", false, false); | |
| 283 | - @ } catch (e) { | |
| 284 | - @ try { | |
| 285 | - @ // For IE9 or IE10, this should work. | |
| 286 | - @ document.execCommand("useCSS", 0, true); | |
| 287 | - @ } catch (e) { | |
| 288 | - @ // OK, that apparently did not work, do nothing. | |
| 289 | - @ } | |
| 290 | - @ } | |
| 291 | - @ document.execCommand(sCmd, false, sValue); | |
| 292 | - @ oDoc.focus(); | |
| 293 | - @ } | |
| 294 | - @ } | |
| 295 | - @ | |
| 296 | - @ /* Change the editing mode. Convert to markup if the argument | |
| 297 | - @ ** is true and wysiwyg if the argument is false. */ | |
| 298 | - @ function setDocMode(bToMarkup) { | |
| 299 | - @ var oContent; | |
| 300 | - @ if (bToMarkup) { | |
| 301 | - @ /* WYSIWYG -> Markup */ | |
| 302 | - @ var linebreak = new RegExp("</p><p>","ig"); | |
| 303 | - @ oContent = document.createTextNode( | |
| 304 | - @ oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>")); | |
| 305 | - @ oDoc.innerHTML = ""; | |
| 306 | - @ oDoc.style.whiteSpace = "pre-wrap"; | |
| 307 | - @ oDoc.appendChild(oContent); | |
| 308 | - @ document.getElementById("toolBar1").style.visibility="hidden"; | |
| 309 | - @ document.getElementById("toolBar2").style.visibility="hidden"; | |
| 310 | - @ } else { | |
| 311 | - @ /* Markup -> WYSIWYG */ | |
| 312 | - @ if (document.all) { | |
| 313 | - @ oDoc.innerHTML = oDoc.innerText; | |
| 314 | - @ } else { | |
| 315 | - @ oContent = document.createRange(); | |
| 316 | - @ oContent.selectNodeContents(oDoc.firstChild); | |
| 317 | - @ oDoc.innerHTML = oContent.toString(); | |
| 318 | - @ } | |
| 319 | - @ oDoc.style.whiteSpace = "normal"; | |
| 320 | - @ document.getElementById("toolBar1").style.visibility="visible"; | |
| 321 | - @ document.getElementById("toolBar2").style.visibility="visible"; | |
| 322 | - @ } | |
| 323 | - @ oDoc.focus(); | |
| 324 | - @ } | |
| 325 | - @ initDoc(); | |
| 326 | - @ </script> | |
| 327 | - | |
| 328 | -} |
| --- a/src/wysiwyg.c | |
| +++ b/src/wysiwyg.c | |
| @@ -1,328 +0,0 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2012 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains code that generates WYSIWYG text editors on |
| 19 | ** web pages. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include <assert.h> |
| 23 | #include <ctype.h> |
| 24 | #include "wysiwyg.h" |
| 25 | |
| 26 | |
| 27 | /* |
| 28 | ** Output code for a WYSIWYG editor. The caller must have already generated |
| 29 | ** the <form> that will contain the editor, and the call must generate the |
| 30 | ** corresponding </form> after this routine returns. The caller must include |
| 31 | ** an onsubmit= attribute on the <form> element that invokes the |
| 32 | ** wysiwygSubmit() function. |
| 33 | ** |
| 34 | ** There can only be a single WYSIWYG editor per frame. |
| 35 | */ |
| 36 | void wysiwygEditor( |
| 37 | const char *zId, /* ID for this editor */ |
| 38 | const char *zContent, /* Initial content (HTML) */ |
| 39 | int w, int h /* Initial width and height */ |
| 40 | ){ |
| 41 | |
| 42 | @ <style type="text/css"> |
| 43 | @ .intLink { cursor: pointer; } |
| 44 | @ img.intLink { border: 0; } |
| 45 | @ #wysiwygBox { |
| 46 | @ border: 1px #000000 solid; |
| 47 | @ padding: 12px; |
| 48 | @ } |
| 49 | @ #editMode label { cursor: pointer; } |
| 50 | @ </style> |
| 51 | |
| 52 | @ <input id="wysiwygValue" type="hidden" name="%s(zId)"> |
| 53 | @ <div id="editModeDiv">Edit mode: |
| 54 | @ <select id="editMode" size=1> |
| 55 | @ <option value="0">WYSIWYG</option> |
| 56 | @ <option value="1">Raw HTML</option> |
| 57 | @ </select></div> |
| 58 | @ <div id="toolBar1"> |
| 59 | @ <select class="format" data-format="formatblock"> |
| 60 | @ <option selected>- formatting -</option> |
| 61 | @ <option value="h1">Title 1 <h1></option> |
| 62 | @ <option value="h2">Title 2 <h2></option> |
| 63 | @ <option value="h3">Title 3 <h3></option> |
| 64 | @ <option value="h4">Title 4 <h4></option> |
| 65 | @ <option value="h5">Title 5 <h5></option> |
| 66 | @ <option value="h6">Subtitle <h6></option> |
| 67 | @ <option value="p">Paragraph <p></option> |
| 68 | @ <option value="pre">Preformatted <pre></option> |
| 69 | @ </select> |
| 70 | @ <select class="format" data-format="fontname"> |
| 71 | @ <option class="heading" selected>- font -</option> |
| 72 | @ <option>Arial</option> |
| 73 | @ <option>Arial Black</option> |
| 74 | @ <option>Courier New</option> |
| 75 | @ <option>Times New Roman</option> |
| 76 | @ </select> |
| 77 | @ <select class="format" data-format="fontsize"> |
| 78 | @ <option class="heading" selected>- size -</option> |
| 79 | @ <option value="1">Very small</option> |
| 80 | @ <option value="2">A bit small</option> |
| 81 | @ <option value="3">Normal</option> |
| 82 | @ <option value="4">Medium-large</option> |
| 83 | @ <option value="5">Big</option> |
| 84 | @ <option value="6">Very big</option> |
| 85 | @ <option value="7">Maximum</option> |
| 86 | @ </select> |
| 87 | @ <select class="format" data-format="forecolor"> |
| 88 | @ <option class="heading" selected>- color -</option> |
| 89 | @ <option value="red">Red</option> |
| 90 | @ <option value="blue">Blue</option> |
| 91 | @ <option value="green">Green</option> |
| 92 | @ <option value="black">Black</option> |
| 93 | @ </select> |
| 94 | @ </div> |
| 95 | @ <div id="toolBar2"> |
| 96 | @ <img class="intLink" title="Undo" data-format="undo" |
| 97 | @ src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7 |
| 98 | @ /I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq704680 |
| 99 | @ 7TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfE |
| 100 | @ whdRIH4fh/DZMICe3/C4nBQBADs="> |
| 101 | |
| 102 | @ <img class="intLink" title="Redo" data-format="redo" |
| 103 | @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1/ |
| 104 | @ ///yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9Na |
| 105 | @ EDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw=="> |
| 106 | |
| 107 | @ <img class="intLink" title="Remove formatting" data-format="removeFormat" |
| 108 | @ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AA |
| 109 | @ AABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwA |
| 110 | @ AAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB5 |
| 111 | @ 01ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqG |
| 112 | @ Xz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMz |
| 113 | @ deiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95 |
| 114 | @ DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2C |
| 115 | @ lrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tA |
| 116 | @ wMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n |
| 117 | @ 7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1 |
| 118 | @ lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj |
| 119 | @ 5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwf |
| 120 | @ fj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrj |
| 121 | @ b/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCc |
| 122 | @ XmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc66 |
| 123 | @ 6s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH |
| 124 | @ 6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg=="> |
| 125 | |
| 126 | @ <img class="intLink" title="Bold" data-format="bold" |
| 127 | @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB |
| 128 | @ YAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" /> |
| 129 | |
| 130 | @ <img class="intLink" title="Italic" data-format="italic" |
| 131 | @ src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALA |
| 132 | @ AAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" /> |
| 133 | |
| 134 | @ <img class="intLink" title="Underline" data-format="underline" |
| 135 | @ src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALA |
| 136 | @ AAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA |
| 137 | @ 7" /> |
| 138 | |
| 139 | @ <img class="intLink" title="Left align" data-format="justifyleft" |
| 140 | @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB |
| 141 | @ YAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" /> |
| 142 | |
| 143 | @ <img class="intLink" title="Center align" data-format="justifycenter" |
| 144 | @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB |
| 145 | @ YAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" /> |
| 146 | |
| 147 | @ <img class="intLink" title="Right align" data-format="justifyright" |
| 148 | @ src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWAB |
| 149 | @ YAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" /> |
| 150 | @ <img class="intLink" title="Numbered list" |
| 151 | @ data-format="insertorderedlist" |
| 152 | @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P//// |
| 153 | @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEO |
| 154 | @ zyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" /> |
| 155 | |
| 156 | @ <img class="intLink" title="Dotted list" |
| 157 | @ data-format="insertunorderedlist" |
| 158 | @ src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv//// |
| 159 | @ ///yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud6 |
| 160 | @ 4UwiuKnigGQliQuWOyKQykgAAOw==" /> |
| 161 | |
| 162 | @ <img class="intLink" title="Quote" data-format="formatblock" |
| 163 | @ src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2 |
| 164 | @ R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P///////// |
| 165 | @ //////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2Cekk |
| 166 | @ ErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrT |
| 167 | @ q/b7/i8fp8PAQA7" /> |
| 168 | |
| 169 | @ <img class="intLink" title="Delete indentation" data-format="outdent" |
| 170 | @ src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P |
| 171 | @ ///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMcz |
| 172 | @ sYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" /> |
| 173 | |
| 174 | @ <img class="intLink" title="Add indentation" data-format="indent" |
| 175 | @ src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3N |
| 176 | @ Ha7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650 |
| 177 | @ B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw=="> |
| 178 | |
| 179 | @ <img class="intLink" title="Hyperlink" data-format="createlink" |
| 180 | @ src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb |
| 181 | @ /I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq704682 |
| 182 | @ 7/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghG |
| 183 | @ i9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" /> |
| 184 | |
| 185 | #if 0 /* Cut/Copy/Paste requires special browser permissions for security |
| 186 | ** reasons. So omit these buttons */ |
| 187 | @ <img class="intLink" title="Cut" data-format="cut" |
| 188 | @ src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1 |
| 189 | @ dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P///////////////////////////// |
| 190 | @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnE |
| 191 | @ bGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9 |
| 192 | @ ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" /> |
| 193 | |
| 194 | @ <img class="intLink" title="Copy" data-format="copy" |
| 195 | @ src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31 |
| 196 | @ iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OP |
| 197 | @ s++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/ |
| 198 | @ kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQ |
| 199 | @ ACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAA |
| 200 | @ Grpy+wsbKzIiEAOw==" /> |
| 201 | |
| 202 | @ <img class="intLink" title="Paste" data-format="paste" |
| 203 | @ src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrp |
| 204 | @ qbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f///////////////////// |
| 205 | @ //////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvB |
| 206 | @ SyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQ |
| 207 | @ o9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAA |
| 208 | @ MOaK+bLAOrtLUyt7i5uiUhADs=" /> |
| 209 | #endif |
| 210 | |
| 211 | @ </div> |
| 212 | @ <div id="wysiwygBox" |
| 213 | @ style="resize:both;overflow:auto;width:95%%;min-height:%d(h)em;" |
| 214 | @ contenteditable="true">%s(zContent)</div> |
| 215 | @ <script nonce="%h(style_nonce())"> |
| 216 | @ var oDoc; |
| 217 | @ |
| 218 | @ /* Initialize the document editor */ |
| 219 | @ function initDoc() { |
| 220 | @ initEventHandlers(); |
| 221 | @ oDoc = document.getElementById("wysiwygBox"); |
| 222 | @ if (!isWysiwyg()) { setDocMode(true); } |
| 223 | @ } |
| 224 | @ |
| 225 | @ function initEventHandlers() { |
| 226 | @ document.querySelector('form').onsubmit = wysiwygSubmit; |
| 227 | @ document.querySelector('#editMode').onchange = function() { |
| 228 | @ setDocMode(this.selectedIndex) |
| 229 | @ }; |
| 230 | @ var controls = document.querySelectorAll('select.format'); |
| 231 | @ for(var i = 0; i < controls.length; i++) { |
| 232 | @ controls[i].onchange = handleDropDown; |
| 233 | @ } |
| 234 | @ controls = document.querySelectorAll('.intLink'); |
| 235 | @ for(i = 0; i < controls.length; i++) { |
| 236 | @ controls[i].onclick = handleFormatButton; |
| 237 | @ } |
| 238 | @ |
| 239 | @ function handleDropDown() { |
| 240 | @ formatDoc(this.dataset.format,this[this.selectedIndex].value); |
| 241 | @ this.selectedIndex = 0; |
| 242 | @ } |
| 243 | @ |
| 244 | @ function handleFormatButton() { |
| 245 | @ var extra; |
| 246 | @ switch (this.dataset.format) { |
| 247 | @ case 'createlink': |
| 248 | @ var sLnk = prompt('Target URL:',''); |
| 249 | @ if(sLnk && sLnk != '') |
| 250 | @ { |
| 251 | @ extra = sLnk; |
| 252 | @ } |
| 253 | @ break; |
| 254 | @ case 'formatblock': |
| 255 | @ extra = 'blockquote'; |
| 256 | @ break; |
| 257 | @ } |
| 258 | @ formatDoc(this.dataset.format, extra); |
| 259 | @ } |
| 260 | @ } |
| 261 | @ |
| 262 | @ /* Return true if the document editor is in WYSIWYG mode. Return |
| 263 | @ ** false if it is in Markup mode */ |
| 264 | @ function isWysiwyg() { |
| 265 | @ return document.getElementById("editMode").selectedIndex==0; |
| 266 | @ } |
| 267 | @ |
| 268 | @ /* Invoke this routine prior to submitting the HTML content back |
| 269 | @ ** to the server */ |
| 270 | @ function wysiwygSubmit() { |
| 271 | @ if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);} |
| 272 | @ document.getElementById("wysiwygValue").value=oDoc.innerHTML; |
| 273 | @ } |
| 274 | @ |
| 275 | @ /* Run the editing command if in WYSIWYG mode */ |
| 276 | @ function formatDoc(sCmd, sValue) { |
| 277 | @ if (isWysiwyg()){ |
| 278 | @ try { |
| 279 | @ // First, try the W3C draft standard way, which has |
| 280 | @ // been working on all non-IE browsers for a while. |
| 281 | @ // It is also supported by IE11 and higher. |
| 282 | @ document.execCommand("styleWithCSS", false, false); |
| 283 | @ } catch (e) { |
| 284 | @ try { |
| 285 | @ // For IE9 or IE10, this should work. |
| 286 | @ document.execCommand("useCSS", 0, true); |
| 287 | @ } catch (e) { |
| 288 | @ // OK, that apparently did not work, do nothing. |
| 289 | @ } |
| 290 | @ } |
| 291 | @ document.execCommand(sCmd, false, sValue); |
| 292 | @ oDoc.focus(); |
| 293 | @ } |
| 294 | @ } |
| 295 | @ |
| 296 | @ /* Change the editing mode. Convert to markup if the argument |
| 297 | @ ** is true and wysiwyg if the argument is false. */ |
| 298 | @ function setDocMode(bToMarkup) { |
| 299 | @ var oContent; |
| 300 | @ if (bToMarkup) { |
| 301 | @ /* WYSIWYG -> Markup */ |
| 302 | @ var linebreak = new RegExp("</p><p>","ig"); |
| 303 | @ oContent = document.createTextNode( |
| 304 | @ oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>")); |
| 305 | @ oDoc.innerHTML = ""; |
| 306 | @ oDoc.style.whiteSpace = "pre-wrap"; |
| 307 | @ oDoc.appendChild(oContent); |
| 308 | @ document.getElementById("toolBar1").style.visibility="hidden"; |
| 309 | @ document.getElementById("toolBar2").style.visibility="hidden"; |
| 310 | @ } else { |
| 311 | @ /* Markup -> WYSIWYG */ |
| 312 | @ if (document.all) { |
| 313 | @ oDoc.innerHTML = oDoc.innerText; |
| 314 | @ } else { |
| 315 | @ oContent = document.createRange(); |
| 316 | @ oContent.selectNodeContents(oDoc.firstChild); |
| 317 | @ oDoc.innerHTML = oContent.toString(); |
| 318 | @ } |
| 319 | @ oDoc.style.whiteSpace = "normal"; |
| 320 | @ document.getElementById("toolBar1").style.visibility="visible"; |
| 321 | @ document.getElementById("toolBar2").style.visibility="visible"; |
| 322 | @ } |
| 323 | @ oDoc.focus(); |
| 324 | @ } |
| 325 | @ initDoc(); |
| 326 | @ </script> |
| 327 | |
| 328 | } |
| --- a/src/wysiwyg.c | |
| +++ b/src/wysiwyg.c | |
| @@ -1,328 +0,0 @@ | |
+2
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -1213,11 +1213,11 @@ | ||
| 1213 | 1213 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 1214 | 1214 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 1215 | 1215 | xfer.maxTime += time(NULL); |
| 1216 | 1216 | g.xferPanic = 1; |
| 1217 | 1217 | |
| 1218 | - db_begin_transaction(); | |
| 1218 | + db_begin_write(); | |
| 1219 | 1219 | db_multi_exec( |
| 1220 | 1220 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1221 | 1221 | "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;" |
| 1222 | 1222 | ); |
| 1223 | 1223 | manifest_crosslink_begin(); |
| @@ -1753,11 +1753,11 @@ | ||
| 1753 | 1753 | */ |
| 1754 | 1754 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1755 | 1755 | @ # timestamp %s(zNow) |
| 1756 | 1756 | free(zNow); |
| 1757 | 1757 | |
| 1758 | - db_end_transaction(0); | |
| 1758 | + db_commit_transaction(); | |
| 1759 | 1759 | configure_rebuild(); |
| 1760 | 1760 | } |
| 1761 | 1761 | |
| 1762 | 1762 | /* |
| 1763 | 1763 | ** COMMAND: test-xfer |
| 1764 | 1764 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1213,11 +1213,11 @@ | |
| 1213 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 1214 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 1215 | xfer.maxTime += time(NULL); |
| 1216 | g.xferPanic = 1; |
| 1217 | |
| 1218 | db_begin_transaction(); |
| 1219 | db_multi_exec( |
| 1220 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1221 | "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;" |
| 1222 | ); |
| 1223 | manifest_crosslink_begin(); |
| @@ -1753,11 +1753,11 @@ | |
| 1753 | */ |
| 1754 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1755 | @ # timestamp %s(zNow) |
| 1756 | free(zNow); |
| 1757 | |
| 1758 | db_end_transaction(0); |
| 1759 | configure_rebuild(); |
| 1760 | } |
| 1761 | |
| 1762 | /* |
| 1763 | ** COMMAND: test-xfer |
| 1764 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1213,11 +1213,11 @@ | |
| 1213 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 1214 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 1215 | xfer.maxTime += time(NULL); |
| 1216 | g.xferPanic = 1; |
| 1217 | |
| 1218 | db_begin_write(); |
| 1219 | db_multi_exec( |
| 1220 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 1221 | "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;" |
| 1222 | ); |
| 1223 | manifest_crosslink_begin(); |
| @@ -1753,11 +1753,11 @@ | |
| 1753 | */ |
| 1754 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 1755 | @ # timestamp %s(zNow) |
| 1756 | free(zNow); |
| 1757 | |
| 1758 | db_commit_transaction(); |
| 1759 | configure_rebuild(); |
| 1760 | } |
| 1761 | |
| 1762 | /* |
| 1763 | ** COMMAND: test-xfer |
| 1764 |
+6
-6
| --- test/merge1.test | ||
| +++ test/merge1.test | ||
| @@ -75,11 +75,11 @@ | ||
| 75 | 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | 76 | } |
| 77 | 77 | write_file_indented t23 { |
| 78 | 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 79 | 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | - ======= COMMON ANCESTOR content follows ============================ | |
| 80 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 81 | 81 | 111 - This is line one of the demo program - 1111 |
| 82 | 82 | ======= MERGED IN content follows ================================== |
| 83 | 83 | 111 - This is line one OF the demo program - 1111 |
| 84 | 84 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 85 | 85 | 222 - The second line program line in code - 2222 |
| @@ -88,11 +88,11 @@ | ||
| 88 | 88 | 555 - we think it well and other stuff too - 5555 |
| 89 | 89 | } |
| 90 | 90 | write_file_indented t32 { |
| 91 | 91 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 92 | 92 | 111 - This is line one OF the demo program - 1111 |
| 93 | - ======= COMMON ANCESTOR content follows ============================ | |
| 93 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 94 | 94 | 111 - This is line one of the demo program - 1111 |
| 95 | 95 | ======= MERGED IN content follows ================================== |
| 96 | 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | 97 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 98 | 98 | 222 - The second line program line in code - 2222 |
| @@ -159,11 +159,11 @@ | ||
| 159 | 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | 161 | } |
| 162 | 162 | write_file_indented t32 { |
| 163 | 163 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 164 | - ======= COMMON ANCESTOR content follows ============================ | |
| 164 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 165 | 165 | 111 - This is line one of the demo program - 1111 |
| 166 | 166 | ======= MERGED IN content follows ================================== |
| 167 | 167 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 168 | 111 - This is line one of the demo program - 1111 |
| 169 | 169 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -174,11 +174,11 @@ | ||
| 174 | 174 | } |
| 175 | 175 | write_file_indented t23 { |
| 176 | 176 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 177 | 177 | 000 - Zero lines added to the beginning of - 0000 |
| 178 | 178 | 111 - This is line one of the demo program - 1111 |
| 179 | - ======= COMMON ANCESTOR content follows ============================ | |
| 179 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 180 | 180 | 111 - This is line one of the demo program - 1111 |
| 181 | 181 | ======= MERGED IN content follows ================================== |
| 182 | 182 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 183 | 183 | 222 - The second line program line in code - 2222 |
| 184 | 184 | 333 - This is a test of the merging algohm - 3333 |
| @@ -306,11 +306,11 @@ | ||
| 306 | 306 | qrst |
| 307 | 307 | uvwx |
| 308 | 308 | yzAB 2 |
| 309 | 309 | CDEF 2 |
| 310 | 310 | GHIJ 2 |
| 311 | - ======= COMMON ANCESTOR content follows ============================ | |
| 311 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 312 | 312 | efgh |
| 313 | 313 | ijkl |
| 314 | 314 | mnop |
| 315 | 315 | qrst |
| 316 | 316 | uvwx |
| @@ -374,11 +374,11 @@ | ||
| 374 | 374 | qrst |
| 375 | 375 | uvwx |
| 376 | 376 | yzAB 2 |
| 377 | 377 | CDEF 2 |
| 378 | 378 | GHIJ 2 |
| 379 | - ======= COMMON ANCESTOR content follows ============================ | |
| 379 | + ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| | |
| 380 | 380 | efgh |
| 381 | 381 | ijkl |
| 382 | 382 | mnop |
| 383 | 383 | qrst |
| 384 | 384 | uvwx |
| 385 | 385 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -75,11 +75,11 @@ | |
| 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | } |
| 77 | write_file_indented t23 { |
| 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | ======= COMMON ANCESTOR content follows ============================ |
| 81 | 111 - This is line one of the demo program - 1111 |
| 82 | ======= MERGED IN content follows ================================== |
| 83 | 111 - This is line one OF the demo program - 1111 |
| 84 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 85 | 222 - The second line program line in code - 2222 |
| @@ -88,11 +88,11 @@ | |
| 88 | 555 - we think it well and other stuff too - 5555 |
| 89 | } |
| 90 | write_file_indented t32 { |
| 91 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 92 | 111 - This is line one OF the demo program - 1111 |
| 93 | ======= COMMON ANCESTOR content follows ============================ |
| 94 | 111 - This is line one of the demo program - 1111 |
| 95 | ======= MERGED IN content follows ================================== |
| 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 98 | 222 - The second line program line in code - 2222 |
| @@ -159,11 +159,11 @@ | |
| 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | } |
| 162 | write_file_indented t32 { |
| 163 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 164 | ======= COMMON ANCESTOR content follows ============================ |
| 165 | 111 - This is line one of the demo program - 1111 |
| 166 | ======= MERGED IN content follows ================================== |
| 167 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 111 - This is line one of the demo program - 1111 |
| 169 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -174,11 +174,11 @@ | |
| 174 | } |
| 175 | write_file_indented t23 { |
| 176 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 177 | 000 - Zero lines added to the beginning of - 0000 |
| 178 | 111 - This is line one of the demo program - 1111 |
| 179 | ======= COMMON ANCESTOR content follows ============================ |
| 180 | 111 - This is line one of the demo program - 1111 |
| 181 | ======= MERGED IN content follows ================================== |
| 182 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 183 | 222 - The second line program line in code - 2222 |
| 184 | 333 - This is a test of the merging algohm - 3333 |
| @@ -306,11 +306,11 @@ | |
| 306 | qrst |
| 307 | uvwx |
| 308 | yzAB 2 |
| 309 | CDEF 2 |
| 310 | GHIJ 2 |
| 311 | ======= COMMON ANCESTOR content follows ============================ |
| 312 | efgh |
| 313 | ijkl |
| 314 | mnop |
| 315 | qrst |
| 316 | uvwx |
| @@ -374,11 +374,11 @@ | |
| 374 | qrst |
| 375 | uvwx |
| 376 | yzAB 2 |
| 377 | CDEF 2 |
| 378 | GHIJ 2 |
| 379 | ======= COMMON ANCESTOR content follows ============================ |
| 380 | efgh |
| 381 | ijkl |
| 382 | mnop |
| 383 | qrst |
| 384 | uvwx |
| 385 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -75,11 +75,11 @@ | |
| 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | } |
| 77 | write_file_indented t23 { |
| 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 81 | 111 - This is line one of the demo program - 1111 |
| 82 | ======= MERGED IN content follows ================================== |
| 83 | 111 - This is line one OF the demo program - 1111 |
| 84 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 85 | 222 - The second line program line in code - 2222 |
| @@ -88,11 +88,11 @@ | |
| 88 | 555 - we think it well and other stuff too - 5555 |
| 89 | } |
| 90 | write_file_indented t32 { |
| 91 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 92 | 111 - This is line one OF the demo program - 1111 |
| 93 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 94 | 111 - This is line one of the demo program - 1111 |
| 95 | ======= MERGED IN content follows ================================== |
| 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 98 | 222 - The second line program line in code - 2222 |
| @@ -159,11 +159,11 @@ | |
| 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | } |
| 162 | write_file_indented t32 { |
| 163 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 164 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 165 | 111 - This is line one of the demo program - 1111 |
| 166 | ======= MERGED IN content follows ================================== |
| 167 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 111 - This is line one of the demo program - 1111 |
| 169 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -174,11 +174,11 @@ | |
| 174 | } |
| 175 | write_file_indented t23 { |
| 176 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<< |
| 177 | 000 - Zero lines added to the beginning of - 0000 |
| 178 | 111 - This is line one of the demo program - 1111 |
| 179 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 180 | 111 - This is line one of the demo program - 1111 |
| 181 | ======= MERGED IN content follows ================================== |
| 182 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| 183 | 222 - The second line program line in code - 2222 |
| 184 | 333 - This is a test of the merging algohm - 3333 |
| @@ -306,11 +306,11 @@ | |
| 306 | qrst |
| 307 | uvwx |
| 308 | yzAB 2 |
| 309 | CDEF 2 |
| 310 | GHIJ 2 |
| 311 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 312 | efgh |
| 313 | ijkl |
| 314 | mnop |
| 315 | qrst |
| 316 | uvwx |
| @@ -374,11 +374,11 @@ | |
| 374 | qrst |
| 375 | uvwx |
| 376 | yzAB 2 |
| 377 | CDEF 2 |
| 378 | GHIJ 2 |
| 379 | ||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||||| |
| 380 | efgh |
| 381 | ijkl |
| 382 | mnop |
| 383 | qrst |
| 384 | uvwx |
| 385 |
+1
-1
| --- test/merge3.test | ||
| +++ test/merge3.test | ||
| @@ -26,11 +26,11 @@ | ||
| 26 | 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | 28 | set x [read_file t4] |
| 29 | 29 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \ |
| 30 | 30 | {MINE:} x |
| 31 | - regsub -all {======= COMMON ANCESTOR content follows =+} $x {COM:} x | |
| 31 | + regsub -all {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+} $x {COM:} x | |
| 32 | 32 | regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x |
| 33 | 33 | regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x |
| 34 | 34 | set x [split [string trim $x] \n] |
| 35 | 35 | set result [string trim $result] |
| 36 | 36 | if {$x!=$result} { |
| 37 | 37 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -26,11 +26,11 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | set x [read_file t4] |
| 29 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \ |
| 30 | {MINE:} x |
| 31 | regsub -all {======= COMMON ANCESTOR content follows =+} $x {COM:} x |
| 32 | regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x |
| 33 | regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x |
| 34 | set x [split [string trim $x] \n] |
| 35 | set result [string trim $result] |
| 36 | if {$x!=$result} { |
| 37 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -26,11 +26,11 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | set x [read_file t4] |
| 29 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \ |
| 30 | {MINE:} x |
| 31 | regsub -all {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+} $x {COM:} x |
| 32 | regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x |
| 33 | regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x |
| 34 | set x [split [string trim $x] \n] |
| 35 | set result [string trim $result] |
| 36 | if {$x!=$result} { |
| 37 |
+5
-5
| --- test/merge4.test | ||
| +++ test/merge4.test | ||
| @@ -26,31 +26,31 @@ | ||
| 26 | 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | 28 | fossil 3-way-merge t1 t3 t2 t5 |
| 29 | 29 | set x [read_file t4] |
| 30 | 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $x {>} x |
| 31 | - regsub -all {=======.*=======} $x {=} x | |
| 31 | + regsub -all {\|\|\|\|\|\|\|.*=======} $x {=} x | |
| 32 | 32 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | 33 | set x [split [string trim $x] \n] |
| 34 | 34 | set y [read_file t5] |
| 35 | 35 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $y {>} y |
| 36 | - regsub -all {=======.*=======} $y {=} y | |
| 36 | + regsub -all {\|\|\|\|\|\|\|.*=======} $y {=} y | |
| 37 | 37 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | 38 | set y [split [string trim $y] \n] |
| 39 | 39 | set result1 [string trim $result1] |
| 40 | 40 | if {$x!=$result1} { |
| 41 | 41 | protOut " Expected \[$result1\]" |
| 42 | 42 | protOut " Got \[$x\]" |
| 43 | - test merge3-$testid 0 | |
| 43 | + test merge4-$testid 0 | |
| 44 | 44 | } else { |
| 45 | 45 | set result2 [string trim $result2] |
| 46 | 46 | if {$y!=$result2} { |
| 47 | 47 | protOut " Expected \[$result2\]" |
| 48 | 48 | protOut " Got \[$y\]" |
| 49 | - test merge3-$testid 0 | |
| 49 | + test merge4-$testid 0 | |
| 50 | 50 | } else { |
| 51 | - test merge3-$testid 1 | |
| 51 | + test merge4-$testid 1 | |
| 52 | 52 | } |
| 53 | 53 | } |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | 56 | merge-test 1000 { |
| 57 | 57 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -26,31 +26,31 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | fossil 3-way-merge t1 t3 t2 t5 |
| 29 | set x [read_file t4] |
| 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $x {>} x |
| 31 | regsub -all {=======.*=======} $x {=} x |
| 32 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | set x [split [string trim $x] \n] |
| 34 | set y [read_file t5] |
| 35 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $y {>} y |
| 36 | regsub -all {=======.*=======} $y {=} y |
| 37 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | set y [split [string trim $y] \n] |
| 39 | set result1 [string trim $result1] |
| 40 | if {$x!=$result1} { |
| 41 | protOut " Expected \[$result1\]" |
| 42 | protOut " Got \[$x\]" |
| 43 | test merge3-$testid 0 |
| 44 | } else { |
| 45 | set result2 [string trim $result2] |
| 46 | if {$y!=$result2} { |
| 47 | protOut " Expected \[$result2\]" |
| 48 | protOut " Got \[$y\]" |
| 49 | test merge3-$testid 0 |
| 50 | } else { |
| 51 | test merge3-$testid 1 |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | merge-test 1000 { |
| 57 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -26,31 +26,31 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 |
| 28 | fossil 3-way-merge t1 t3 t2 t5 |
| 29 | set x [read_file t4] |
| 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $x {>} x |
| 31 | regsub -all {\|\|\|\|\|\|\|.*=======} $x {=} x |
| 32 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | set x [split [string trim $x] \n] |
| 34 | set y [read_file t5] |
| 35 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $y {>} y |
| 36 | regsub -all {\|\|\|\|\|\|\|.*=======} $y {=} y |
| 37 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | set y [split [string trim $y] \n] |
| 39 | set result1 [string trim $result1] |
| 40 | if {$x!=$result1} { |
| 41 | protOut " Expected \[$result1\]" |
| 42 | protOut " Got \[$x\]" |
| 43 | test merge4-$testid 0 |
| 44 | } else { |
| 45 | set result2 [string trim $result2] |
| 46 | if {$y!=$result2} { |
| 47 | protOut " Expected \[$result2\]" |
| 48 | protOut " Got \[$y\]" |
| 49 | test merge4-$testid 0 |
| 50 | } else { |
| 51 | test merge4-$testid 1 |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | merge-test 1000 { |
| 57 |
+1
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -334,10 +334,11 @@ | ||
| 334 | 334 | proxy \ |
| 335 | 335 | redirect-to-https \ |
| 336 | 336 | relative-paths \ |
| 337 | 337 | repo-cksum \ |
| 338 | 338 | repolist-skin \ |
| 339 | + safe-html \ | |
| 339 | 340 | self-register \ |
| 340 | 341 | ssh-command \ |
| 341 | 342 | ssl-ca-location \ |
| 342 | 343 | ssl-identity \ |
| 343 | 344 | tclsh \ |
| 344 | 345 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -334,10 +334,11 @@ | |
| 334 | proxy \ |
| 335 | redirect-to-https \ |
| 336 | relative-paths \ |
| 337 | repo-cksum \ |
| 338 | repolist-skin \ |
| 339 | self-register \ |
| 340 | ssh-command \ |
| 341 | ssl-ca-location \ |
| 342 | ssl-identity \ |
| 343 | tclsh \ |
| 344 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -334,10 +334,11 @@ | |
| 334 | proxy \ |
| 335 | redirect-to-https \ |
| 336 | relative-paths \ |
| 337 | repo-cksum \ |
| 338 | repolist-skin \ |
| 339 | safe-html \ |
| 340 | self-register \ |
| 341 | ssh-command \ |
| 342 | ssl-ca-location \ |
| 343 | ssl-identity \ |
| 344 | tclsh \ |
| 345 |
+6
| --- test/wiki.test | ||
| +++ test/wiki.test | ||
| @@ -17,10 +17,16 @@ | ||
| 17 | 17 | # |
| 18 | 18 | # Test wiki and attachment command Support |
| 19 | 19 | # |
| 20 | 20 | |
| 21 | 21 | test_setup |
| 22 | + | |
| 23 | +# Disable backoffice for this test, otherwise its process lingers for some | |
| 24 | +# time after the test has completed. | |
| 25 | +# Perhaps, this should be done in test_setup and enabled explicitly only | |
| 26 | +# when needed. | |
| 27 | +fossil set backoffice-disable 1 | |
| 22 | 28 | |
| 23 | 29 | # Return true if two files are similar (i.e. not only compress trailing spaces |
| 24 | 30 | # from a line, but remove any final LF from the file as well) |
| 25 | 31 | proc similar_file {a b} { |
| 26 | 32 | set x "" |
| 27 | 33 |
| --- test/wiki.test | |
| +++ test/wiki.test | |
| @@ -17,10 +17,16 @@ | |
| 17 | # |
| 18 | # Test wiki and attachment command Support |
| 19 | # |
| 20 | |
| 21 | test_setup |
| 22 | |
| 23 | # Return true if two files are similar (i.e. not only compress trailing spaces |
| 24 | # from a line, but remove any final LF from the file as well) |
| 25 | proc similar_file {a b} { |
| 26 | set x "" |
| 27 |
| --- test/wiki.test | |
| +++ test/wiki.test | |
| @@ -17,10 +17,16 @@ | |
| 17 | # |
| 18 | # Test wiki and attachment command Support |
| 19 | # |
| 20 | |
| 21 | test_setup |
| 22 | |
| 23 | # Disable backoffice for this test, otherwise its process lingers for some |
| 24 | # time after the test has completed. |
| 25 | # Perhaps, this should be done in test_setup and enabled explicitly only |
| 26 | # when needed. |
| 27 | fossil set backoffice-disable 1 |
| 28 | |
| 29 | # Return true if two files are similar (i.e. not only compress trailing spaces |
| 30 | # from a line, but remove any final LF from the file as well) |
| 31 | proc similar_file {a b} { |
| 32 | set x "" |
| 33 |
+4
-10
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -28,13 +28,13 @@ | ||
| 28 | 28 | |
| 29 | 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | 30 | |
| 31 | 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | 32 | |
| 33 | -SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c | |
| 33 | +SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c | |
| 34 | 34 | |
| 35 | -OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 35 | +OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | RC=$(DMDIR)\bin\rcc |
| 39 | 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | 40 | |
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | |
| 50 | 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | 52 | |
| 53 | 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | - +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 54 | + +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 55 | 55 | +echo fossil >> $@ |
| 56 | 56 | +echo fossil >> $@ |
| 57 | 57 | +echo $(LIBS) >> $@ |
| 58 | 58 | +echo. >> $@ |
| 59 | 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | ||
| 962 | 962 | $(TCC) -o$@ -c winhttp_.c |
| 963 | 963 | |
| 964 | 964 | winhttp_.c : $(SRCDIR)\winhttp.c |
| 965 | 965 | +translate$E $** > $@ |
| 966 | 966 | |
| 967 | -$(OBJDIR)\wysiwyg$O : wysiwyg_.c wysiwyg.h | |
| 968 | - $(TCC) -o$@ -c wysiwyg_.c | |
| 969 | - | |
| 970 | -wysiwyg_.c : $(SRCDIR)\wysiwyg.c | |
| 971 | - +translate$E $** > $@ | |
| 972 | - | |
| 973 | 967 | $(OBJDIR)\xfer$O : xfer_.c xfer.h |
| 974 | 968 | $(TCC) -o$@ -c xfer_.c |
| 975 | 969 | |
| 976 | 970 | xfer_.c : $(SRCDIR)\xfer.c |
| 977 | 971 | +translate$E $** > $@ |
| @@ -987,7 +981,7 @@ | ||
| 987 | 981 | |
| 988 | 982 | zip_.c : $(SRCDIR)\zip.c |
| 989 | 983 | +translate$E $** > $@ |
| 990 | 984 | |
| 991 | 985 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 992 | - +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 986 | + +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 993 | 987 | @copy /Y nul: headers |
| 994 | 988 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | |
| 962 | $(TCC) -o$@ -c winhttp_.c |
| 963 | |
| 964 | winhttp_.c : $(SRCDIR)\winhttp.c |
| 965 | +translate$E $** > $@ |
| 966 | |
| 967 | $(OBJDIR)\wysiwyg$O : wysiwyg_.c wysiwyg.h |
| 968 | $(TCC) -o$@ -c wysiwyg_.c |
| 969 | |
| 970 | wysiwyg_.c : $(SRCDIR)\wysiwyg.c |
| 971 | +translate$E $** > $@ |
| 972 | |
| 973 | $(OBJDIR)\xfer$O : xfer_.c xfer.h |
| 974 | $(TCC) -o$@ -c xfer_.c |
| 975 | |
| 976 | xfer_.c : $(SRCDIR)\xfer.c |
| 977 | +translate$E $** > $@ |
| @@ -987,7 +981,7 @@ | |
| 987 | |
| 988 | zip_.c : $(SRCDIR)\zip.c |
| 989 | +translate$E $** > $@ |
| 990 | |
| 991 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 992 | +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 993 | @copy /Y nul: headers |
| 994 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 |
| 30 | |
| 31 | SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -962,16 +962,10 @@ | |
| 962 | $(TCC) -o$@ -c winhttp_.c |
| 963 | |
| 964 | winhttp_.c : $(SRCDIR)\winhttp.c |
| 965 | +translate$E $** > $@ |
| 966 | |
| 967 | $(OBJDIR)\xfer$O : xfer_.c xfer.h |
| 968 | $(TCC) -o$@ -c xfer_.c |
| 969 | |
| 970 | xfer_.c : $(SRCDIR)\xfer.c |
| 971 | +translate$E $** > $@ |
| @@ -987,7 +981,7 @@ | |
| 981 | |
| 982 | zip_.c : $(SRCDIR)\zip.c |
| 983 | +translate$E $** > $@ |
| 984 | |
| 985 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 986 | +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 987 | @copy /Y nul: headers |
| 988 |
+9
-13
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -566,11 +566,10 @@ | ||
| 566 | 566 | $(SRCDIR)/webmail.c \ |
| 567 | 567 | $(SRCDIR)/wiki.c \ |
| 568 | 568 | $(SRCDIR)/wikiformat.c \ |
| 569 | 569 | $(SRCDIR)/winfile.c \ |
| 570 | 570 | $(SRCDIR)/winhttp.c \ |
| 571 | - $(SRCDIR)/wysiwyg.c \ | |
| 572 | 571 | $(SRCDIR)/xfer.c \ |
| 573 | 572 | $(SRCDIR)/xfersetup.c \ |
| 574 | 573 | $(SRCDIR)/zip.c |
| 575 | 574 | |
| 576 | 575 | EXTRA_FILES = \ |
| @@ -636,14 +635,18 @@ | ||
| 636 | 635 | $(SRCDIR)/default.css \ |
| 637 | 636 | $(SRCDIR)/diff.tcl \ |
| 638 | 637 | $(SRCDIR)/forum.js \ |
| 639 | 638 | $(SRCDIR)/fossil.bootstrap.js \ |
| 640 | 639 | $(SRCDIR)/fossil.confirmer.js \ |
| 640 | + $(SRCDIR)/fossil.copybutton.js \ | |
| 641 | 641 | $(SRCDIR)/fossil.dom.js \ |
| 642 | 642 | $(SRCDIR)/fossil.fetch.js \ |
| 643 | + $(SRCDIR)/fossil.numbered-lines.js \ | |
| 643 | 644 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 644 | 645 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 646 | + $(SRCDIR)/fossil.page.wikiedit.js \ | |
| 647 | + $(SRCDIR)/fossil.popupwidget.js \ | |
| 645 | 648 | $(SRCDIR)/fossil.storage.js \ |
| 646 | 649 | $(SRCDIR)/fossil.tabs.js \ |
| 647 | 650 | $(SRCDIR)/graph.js \ |
| 648 | 651 | $(SRCDIR)/href.js \ |
| 649 | 652 | $(SRCDIR)/login.js \ |
| @@ -669,10 +672,11 @@ | ||
| 669 | 672 | $(SRCDIR)/sounds/d.wav \ |
| 670 | 673 | $(SRCDIR)/sounds/e.wav \ |
| 671 | 674 | $(SRCDIR)/sounds/f.wav \ |
| 672 | 675 | $(SRCDIR)/style.admin_log.css \ |
| 673 | 676 | $(SRCDIR)/style.fileedit.css \ |
| 677 | + $(SRCDIR)/style.wikiedit.css \ | |
| 674 | 678 | $(SRCDIR)/tree.js \ |
| 675 | 679 | $(SRCDIR)/useredit.js \ |
| 676 | 680 | $(SRCDIR)/wiki.wiki |
| 677 | 681 | |
| 678 | 682 | TRANS_SRC = \ |
| @@ -814,11 +818,10 @@ | ||
| 814 | 818 | $(OBJDIR)/webmail_.c \ |
| 815 | 819 | $(OBJDIR)/wiki_.c \ |
| 816 | 820 | $(OBJDIR)/wikiformat_.c \ |
| 817 | 821 | $(OBJDIR)/winfile_.c \ |
| 818 | 822 | $(OBJDIR)/winhttp_.c \ |
| 819 | - $(OBJDIR)/wysiwyg_.c \ | |
| 820 | 823 | $(OBJDIR)/xfer_.c \ |
| 821 | 824 | $(OBJDIR)/xfersetup_.c \ |
| 822 | 825 | $(OBJDIR)/zip_.c |
| 823 | 826 | |
| 824 | 827 | OBJ = \ |
| @@ -960,11 +963,10 @@ | ||
| 960 | 963 | $(OBJDIR)/webmail.o \ |
| 961 | 964 | $(OBJDIR)/wiki.o \ |
| 962 | 965 | $(OBJDIR)/wikiformat.o \ |
| 963 | 966 | $(OBJDIR)/winfile.o \ |
| 964 | 967 | $(OBJDIR)/winhttp.o \ |
| 965 | - $(OBJDIR)/wysiwyg.o \ | |
| 966 | 968 | $(OBJDIR)/xfer.o \ |
| 967 | 969 | $(OBJDIR)/xfersetup.o \ |
| 968 | 970 | $(OBJDIR)/zip.o |
| 969 | 971 | |
| 970 | 972 | APPNAME = fossil.exe |
| @@ -1061,13 +1063,16 @@ | ||
| 1061 | 1063 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1062 | 1064 | # the repository after running the tests. |
| 1063 | 1065 | test: $(OBJDIR) $(APPNAME) |
| 1064 | 1066 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1065 | 1067 | |
| 1066 | -$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) | |
| 1068 | +$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h | |
| 1067 | 1069 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1068 | 1070 | |
| 1071 | +$(OBJDIR)/phony.h: | |
| 1072 | + # Force rebuild of VERSION.h every time "make" is run | |
| 1073 | + | |
| 1069 | 1074 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1070 | 1075 | # to 1. If it is set to 1, then there is no need to build or link |
| 1071 | 1076 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1072 | 1077 | # using -lsqlite3. |
| 1073 | 1078 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1318,11 +1323,10 @@ | ||
| 1318 | 1323 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 1319 | 1324 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1320 | 1325 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1321 | 1326 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1322 | 1327 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1323 | - $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ | |
| 1324 | 1328 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 1325 | 1329 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 1326 | 1330 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 1327 | 1331 | $(SRCDIR)/sqlite3.h \ |
| 1328 | 1332 | $(SRCDIR)/th.h \ |
| @@ -2451,18 +2455,10 @@ | ||
| 2451 | 2455 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2452 | 2456 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2453 | 2457 | |
| 2454 | 2458 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2455 | 2459 | |
| 2456 | -$(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(TRANSLATE) | |
| 2457 | - $(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@ | |
| 2458 | - | |
| 2459 | -$(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h | |
| 2460 | - $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c | |
| 2461 | - | |
| 2462 | -$(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers | |
| 2463 | - | |
| 2464 | 2460 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(TRANSLATE) |
| 2465 | 2461 | $(TRANSLATE) $(SRCDIR)/xfer.c >$@ |
| 2466 | 2462 | |
| 2467 | 2463 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2468 | 2464 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2469 | 2465 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -566,11 +566,10 @@ | |
| 566 | $(SRCDIR)/webmail.c \ |
| 567 | $(SRCDIR)/wiki.c \ |
| 568 | $(SRCDIR)/wikiformat.c \ |
| 569 | $(SRCDIR)/winfile.c \ |
| 570 | $(SRCDIR)/winhttp.c \ |
| 571 | $(SRCDIR)/wysiwyg.c \ |
| 572 | $(SRCDIR)/xfer.c \ |
| 573 | $(SRCDIR)/xfersetup.c \ |
| 574 | $(SRCDIR)/zip.c |
| 575 | |
| 576 | EXTRA_FILES = \ |
| @@ -636,14 +635,18 @@ | |
| 636 | $(SRCDIR)/default.css \ |
| 637 | $(SRCDIR)/diff.tcl \ |
| 638 | $(SRCDIR)/forum.js \ |
| 639 | $(SRCDIR)/fossil.bootstrap.js \ |
| 640 | $(SRCDIR)/fossil.confirmer.js \ |
| 641 | $(SRCDIR)/fossil.dom.js \ |
| 642 | $(SRCDIR)/fossil.fetch.js \ |
| 643 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 644 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 645 | $(SRCDIR)/fossil.storage.js \ |
| 646 | $(SRCDIR)/fossil.tabs.js \ |
| 647 | $(SRCDIR)/graph.js \ |
| 648 | $(SRCDIR)/href.js \ |
| 649 | $(SRCDIR)/login.js \ |
| @@ -669,10 +672,11 @@ | |
| 669 | $(SRCDIR)/sounds/d.wav \ |
| 670 | $(SRCDIR)/sounds/e.wav \ |
| 671 | $(SRCDIR)/sounds/f.wav \ |
| 672 | $(SRCDIR)/style.admin_log.css \ |
| 673 | $(SRCDIR)/style.fileedit.css \ |
| 674 | $(SRCDIR)/tree.js \ |
| 675 | $(SRCDIR)/useredit.js \ |
| 676 | $(SRCDIR)/wiki.wiki |
| 677 | |
| 678 | TRANS_SRC = \ |
| @@ -814,11 +818,10 @@ | |
| 814 | $(OBJDIR)/webmail_.c \ |
| 815 | $(OBJDIR)/wiki_.c \ |
| 816 | $(OBJDIR)/wikiformat_.c \ |
| 817 | $(OBJDIR)/winfile_.c \ |
| 818 | $(OBJDIR)/winhttp_.c \ |
| 819 | $(OBJDIR)/wysiwyg_.c \ |
| 820 | $(OBJDIR)/xfer_.c \ |
| 821 | $(OBJDIR)/xfersetup_.c \ |
| 822 | $(OBJDIR)/zip_.c |
| 823 | |
| 824 | OBJ = \ |
| @@ -960,11 +963,10 @@ | |
| 960 | $(OBJDIR)/webmail.o \ |
| 961 | $(OBJDIR)/wiki.o \ |
| 962 | $(OBJDIR)/wikiformat.o \ |
| 963 | $(OBJDIR)/winfile.o \ |
| 964 | $(OBJDIR)/winhttp.o \ |
| 965 | $(OBJDIR)/wysiwyg.o \ |
| 966 | $(OBJDIR)/xfer.o \ |
| 967 | $(OBJDIR)/xfersetup.o \ |
| 968 | $(OBJDIR)/zip.o |
| 969 | |
| 970 | APPNAME = fossil.exe |
| @@ -1061,13 +1063,16 @@ | |
| 1061 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1062 | # the repository after running the tests. |
| 1063 | test: $(OBJDIR) $(APPNAME) |
| 1064 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1065 | |
| 1066 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) |
| 1067 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1068 | |
| 1069 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1070 | # to 1. If it is set to 1, then there is no need to build or link |
| 1071 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1072 | # using -lsqlite3. |
| 1073 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1318,11 +1323,10 @@ | |
| 1318 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 1319 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1320 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1321 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1322 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1323 | $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ |
| 1324 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 1325 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 1326 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 1327 | $(SRCDIR)/sqlite3.h \ |
| 1328 | $(SRCDIR)/th.h \ |
| @@ -2451,18 +2455,10 @@ | |
| 2451 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2452 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2453 | |
| 2454 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2455 | |
| 2456 | $(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(TRANSLATE) |
| 2457 | $(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@ |
| 2458 | |
| 2459 | $(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h |
| 2460 | $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c |
| 2461 | |
| 2462 | $(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers |
| 2463 | |
| 2464 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(TRANSLATE) |
| 2465 | $(TRANSLATE) $(SRCDIR)/xfer.c >$@ |
| 2466 | |
| 2467 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2468 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2469 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -566,11 +566,10 @@ | |
| 566 | $(SRCDIR)/webmail.c \ |
| 567 | $(SRCDIR)/wiki.c \ |
| 568 | $(SRCDIR)/wikiformat.c \ |
| 569 | $(SRCDIR)/winfile.c \ |
| 570 | $(SRCDIR)/winhttp.c \ |
| 571 | $(SRCDIR)/xfer.c \ |
| 572 | $(SRCDIR)/xfersetup.c \ |
| 573 | $(SRCDIR)/zip.c |
| 574 | |
| 575 | EXTRA_FILES = \ |
| @@ -636,14 +635,18 @@ | |
| 635 | $(SRCDIR)/default.css \ |
| 636 | $(SRCDIR)/diff.tcl \ |
| 637 | $(SRCDIR)/forum.js \ |
| 638 | $(SRCDIR)/fossil.bootstrap.js \ |
| 639 | $(SRCDIR)/fossil.confirmer.js \ |
| 640 | $(SRCDIR)/fossil.copybutton.js \ |
| 641 | $(SRCDIR)/fossil.dom.js \ |
| 642 | $(SRCDIR)/fossil.fetch.js \ |
| 643 | $(SRCDIR)/fossil.numbered-lines.js \ |
| 644 | $(SRCDIR)/fossil.page.fileedit.js \ |
| 645 | $(SRCDIR)/fossil.page.forumpost.js \ |
| 646 | $(SRCDIR)/fossil.page.wikiedit.js \ |
| 647 | $(SRCDIR)/fossil.popupwidget.js \ |
| 648 | $(SRCDIR)/fossil.storage.js \ |
| 649 | $(SRCDIR)/fossil.tabs.js \ |
| 650 | $(SRCDIR)/graph.js \ |
| 651 | $(SRCDIR)/href.js \ |
| 652 | $(SRCDIR)/login.js \ |
| @@ -669,10 +672,11 @@ | |
| 672 | $(SRCDIR)/sounds/d.wav \ |
| 673 | $(SRCDIR)/sounds/e.wav \ |
| 674 | $(SRCDIR)/sounds/f.wav \ |
| 675 | $(SRCDIR)/style.admin_log.css \ |
| 676 | $(SRCDIR)/style.fileedit.css \ |
| 677 | $(SRCDIR)/style.wikiedit.css \ |
| 678 | $(SRCDIR)/tree.js \ |
| 679 | $(SRCDIR)/useredit.js \ |
| 680 | $(SRCDIR)/wiki.wiki |
| 681 | |
| 682 | TRANS_SRC = \ |
| @@ -814,11 +818,10 @@ | |
| 818 | $(OBJDIR)/webmail_.c \ |
| 819 | $(OBJDIR)/wiki_.c \ |
| 820 | $(OBJDIR)/wikiformat_.c \ |
| 821 | $(OBJDIR)/winfile_.c \ |
| 822 | $(OBJDIR)/winhttp_.c \ |
| 823 | $(OBJDIR)/xfer_.c \ |
| 824 | $(OBJDIR)/xfersetup_.c \ |
| 825 | $(OBJDIR)/zip_.c |
| 826 | |
| 827 | OBJ = \ |
| @@ -960,11 +963,10 @@ | |
| 963 | $(OBJDIR)/webmail.o \ |
| 964 | $(OBJDIR)/wiki.o \ |
| 965 | $(OBJDIR)/wikiformat.o \ |
| 966 | $(OBJDIR)/winfile.o \ |
| 967 | $(OBJDIR)/winhttp.o \ |
| 968 | $(OBJDIR)/xfer.o \ |
| 969 | $(OBJDIR)/xfersetup.o \ |
| 970 | $(OBJDIR)/zip.o |
| 971 | |
| 972 | APPNAME = fossil.exe |
| @@ -1061,13 +1063,16 @@ | |
| 1063 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 1064 | # the repository after running the tests. |
| 1065 | test: $(OBJDIR) $(APPNAME) |
| 1066 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) |
| 1067 | |
| 1068 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(OBJDIR)/phony.h |
| 1069 | $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ |
| 1070 | |
| 1071 | $(OBJDIR)/phony.h: |
| 1072 | # Force rebuild of VERSION.h every time "make" is run |
| 1073 | |
| 1074 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| 1075 | # to 1. If it is set to 1, then there is no need to build or link |
| 1076 | # the sqlite3.o object. Instead, the system SQLite will be linked |
| 1077 | # using -lsqlite3. |
| 1078 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| @@ -1318,11 +1323,10 @@ | |
| 1323 | $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ |
| 1324 | $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ |
| 1325 | $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ |
| 1326 | $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ |
| 1327 | $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ |
| 1328 | $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ |
| 1329 | $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
| 1330 | $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ |
| 1331 | $(SRCDIR)/sqlite3.h \ |
| 1332 | $(SRCDIR)/th.h \ |
| @@ -2451,18 +2455,10 @@ | |
| 2455 | $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h |
| 2456 | $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c |
| 2457 | |
| 2458 | $(OBJDIR)/winhttp.h: $(OBJDIR)/headers |
| 2459 | |
| 2460 | $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(TRANSLATE) |
| 2461 | $(TRANSLATE) $(SRCDIR)/xfer.c >$@ |
| 2462 | |
| 2463 | $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h |
| 2464 | $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c |
| 2465 |
+15
-12
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -488,11 +488,10 @@ | ||
| 488 | 488 | "$(OX)\webmail_.c" \ |
| 489 | 489 | "$(OX)\wiki_.c" \ |
| 490 | 490 | "$(OX)\wikiformat_.c" \ |
| 491 | 491 | "$(OX)\winfile_.c" \ |
| 492 | 492 | "$(OX)\winhttp_.c" \ |
| 493 | - "$(OX)\wysiwyg_.c" \ | |
| 494 | 493 | "$(OX)\xfer_.c" \ |
| 495 | 494 | "$(OX)\xfersetup_.c" \ |
| 496 | 495 | "$(OX)\zip_.c" |
| 497 | 496 | |
| 498 | 497 | EXTRA_FILES = "$(SRCDIR)\..\skins\aht\details.txt" \ |
| @@ -557,14 +556,18 @@ | ||
| 557 | 556 | "$(SRCDIR)\default.css" \ |
| 558 | 557 | "$(SRCDIR)\diff.tcl" \ |
| 559 | 558 | "$(SRCDIR)\forum.js" \ |
| 560 | 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 561 | 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | + "$(SRCDIR)\fossil.copybutton.js" \ | |
| 562 | 562 | "$(SRCDIR)\fossil.dom.js" \ |
| 563 | 563 | "$(SRCDIR)\fossil.fetch.js" \ |
| 564 | + "$(SRCDIR)\fossil.numbered-lines.js" \ | |
| 564 | 565 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 565 | 566 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 567 | + "$(SRCDIR)\fossil.page.wikiedit.js" \ | |
| 568 | + "$(SRCDIR)\fossil.popupwidget.js" \ | |
| 566 | 569 | "$(SRCDIR)\fossil.storage.js" \ |
| 567 | 570 | "$(SRCDIR)\fossil.tabs.js" \ |
| 568 | 571 | "$(SRCDIR)\graph.js" \ |
| 569 | 572 | "$(SRCDIR)\href.js" \ |
| 570 | 573 | "$(SRCDIR)\login.js" \ |
| @@ -590,10 +593,11 @@ | ||
| 590 | 593 | "$(SRCDIR)\sounds\d.wav" \ |
| 591 | 594 | "$(SRCDIR)\sounds\e.wav" \ |
| 592 | 595 | "$(SRCDIR)\sounds\f.wav" \ |
| 593 | 596 | "$(SRCDIR)\style.admin_log.css" \ |
| 594 | 597 | "$(SRCDIR)\style.fileedit.css" \ |
| 598 | + "$(SRCDIR)\style.wikiedit.css" \ | |
| 595 | 599 | "$(SRCDIR)\tree.js" \ |
| 596 | 600 | "$(SRCDIR)\useredit.js" \ |
| 597 | 601 | "$(SRCDIR)\wiki.wiki" |
| 598 | 602 | |
| 599 | 603 | OBJ = "$(OX)\add$O" \ |
| @@ -740,11 +744,10 @@ | ||
| 740 | 744 | "$(OX)\webmail$O" \ |
| 741 | 745 | "$(OX)\wiki$O" \ |
| 742 | 746 | "$(OX)\wikiformat$O" \ |
| 743 | 747 | "$(OX)\winfile$O" \ |
| 744 | 748 | "$(OX)\winhttp$O" \ |
| 745 | - "$(OX)\wysiwyg$O" \ | |
| 746 | 749 | "$(OX)\xfer$O" \ |
| 747 | 750 | "$(OX)\xfersetup$O" \ |
| 748 | 751 | "$(OX)\zip$O" \ |
| 749 | 752 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 750 | 753 | "$(OX)\miniz$O" \ |
| @@ -967,11 +970,10 @@ | ||
| 967 | 970 | echo "$(OX)\webmail.obj" >> $@ |
| 968 | 971 | echo "$(OX)\wiki.obj" >> $@ |
| 969 | 972 | echo "$(OX)\wikiformat.obj" >> $@ |
| 970 | 973 | echo "$(OX)\winfile.obj" >> $@ |
| 971 | 974 | echo "$(OX)\winhttp.obj" >> $@ |
| 972 | - echo "$(OX)\wysiwyg.obj" >> $@ | |
| 973 | 975 | echo "$(OX)\xfer.obj" >> $@ |
| 974 | 976 | echo "$(OX)\xfersetup.obj" >> $@ |
| 975 | 977 | echo "$(OX)\zip.obj" >> $@ |
| 976 | 978 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 977 | 979 | echo "$(OX)\miniz.obj" >> $@ |
| @@ -1022,12 +1024,15 @@ | ||
| 1022 | 1024 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1023 | 1025 | |
| 1024 | 1026 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1025 | 1027 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1026 | 1028 | |
| 1027 | -"$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" | |
| 1028 | - $** > $@ | |
| 1029 | +"$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" | |
| 1030 | + "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ | |
| 1031 | + | |
| 1032 | +"$(B)\phony.h" : | |
| 1033 | + rem Force rebuild of VERSION.h whenever nmake is run | |
| 1029 | 1034 | |
| 1030 | 1035 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1031 | 1036 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1032 | 1037 | |
| 1033 | 1038 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| @@ -1148,14 +1153,18 @@ | ||
| 1148 | 1153 | echo "$(SRCDIR)\default.css" >> $@ |
| 1149 | 1154 | echo "$(SRCDIR)\diff.tcl" >> $@ |
| 1150 | 1155 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1151 | 1156 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1152 | 1157 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1158 | + echo "$(SRCDIR)\fossil.copybutton.js" >> $@ | |
| 1153 | 1159 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1154 | 1160 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1161 | + echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ | |
| 1155 | 1162 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1156 | 1163 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1164 | + echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ | |
| 1165 | + echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ | |
| 1157 | 1166 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1158 | 1167 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1159 | 1168 | echo "$(SRCDIR)\graph.js" >> $@ |
| 1160 | 1169 | echo "$(SRCDIR)\href.js" >> $@ |
| 1161 | 1170 | echo "$(SRCDIR)\login.js" >> $@ |
| @@ -1181,10 +1190,11 @@ | ||
| 1181 | 1190 | echo "$(SRCDIR)\sounds/d.wav" >> $@ |
| 1182 | 1191 | echo "$(SRCDIR)\sounds/e.wav" >> $@ |
| 1183 | 1192 | echo "$(SRCDIR)\sounds/f.wav" >> $@ |
| 1184 | 1193 | echo "$(SRCDIR)\style.admin_log.css" >> $@ |
| 1185 | 1194 | echo "$(SRCDIR)\style.fileedit.css" >> $@ |
| 1195 | + echo "$(SRCDIR)\style.wikiedit.css" >> $@ | |
| 1186 | 1196 | echo "$(SRCDIR)\tree.js" >> $@ |
| 1187 | 1197 | echo "$(SRCDIR)\useredit.js" >> $@ |
| 1188 | 1198 | echo "$(SRCDIR)\wiki.wiki" >> $@ |
| 1189 | 1199 | |
| 1190 | 1200 | "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" |
| @@ -2025,16 +2035,10 @@ | ||
| 2025 | 2035 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winhttp_.c" |
| 2026 | 2036 | |
| 2027 | 2037 | "$(OX)\winhttp_.c" : "$(SRCDIR)\winhttp.c" |
| 2028 | 2038 | "$(OBJDIR)\translate$E" $** > $@ |
| 2029 | 2039 | |
| 2030 | -"$(OX)\wysiwyg$O" : "$(OX)\wysiwyg_.c" "$(OX)\wysiwyg.h" | |
| 2031 | - $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wysiwyg_.c" | |
| 2032 | - | |
| 2033 | -"$(OX)\wysiwyg_.c" : "$(SRCDIR)\wysiwyg.c" | |
| 2034 | - "$(OBJDIR)\translate$E" $** > $@ | |
| 2035 | - | |
| 2036 | 2040 | "$(OX)\xfer$O" : "$(OX)\xfer_.c" "$(OX)\xfer.h" |
| 2037 | 2041 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfer_.c" |
| 2038 | 2042 | |
| 2039 | 2043 | "$(OX)\xfer_.c" : "$(SRCDIR)\xfer.c" |
| 2040 | 2044 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2193,14 +2197,13 @@ | ||
| 2193 | 2197 | "$(OX)\webmail_.c":"$(OX)\webmail.h" \ |
| 2194 | 2198 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2195 | 2199 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2196 | 2200 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2197 | 2201 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2198 | - "$(OX)\wysiwyg_.c":"$(OX)\wysiwyg.h" \ | |
| 2199 | 2202 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2200 | 2203 | "$(OX)\xfersetup_.c":"$(OX)\xfersetup.h" \ |
| 2201 | 2204 | "$(OX)\zip_.c":"$(OX)\zip.h" \ |
| 2202 | 2205 | "$(SRCDIR)\sqlite3.h" \ |
| 2203 | 2206 | "$(SRCDIR)\th.h" \ |
| 2204 | 2207 | "$(OX)\VERSION.h" \ |
| 2205 | 2208 | "$(SRCDIR)\cson_amalgamation.h" |
| 2206 | 2209 | @copy /Y nul: $@ |
| 2207 | 2210 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -488,11 +488,10 @@ | |
| 488 | "$(OX)\webmail_.c" \ |
| 489 | "$(OX)\wiki_.c" \ |
| 490 | "$(OX)\wikiformat_.c" \ |
| 491 | "$(OX)\winfile_.c" \ |
| 492 | "$(OX)\winhttp_.c" \ |
| 493 | "$(OX)\wysiwyg_.c" \ |
| 494 | "$(OX)\xfer_.c" \ |
| 495 | "$(OX)\xfersetup_.c" \ |
| 496 | "$(OX)\zip_.c" |
| 497 | |
| 498 | EXTRA_FILES = "$(SRCDIR)\..\skins\aht\details.txt" \ |
| @@ -557,14 +556,18 @@ | |
| 557 | "$(SRCDIR)\default.css" \ |
| 558 | "$(SRCDIR)\diff.tcl" \ |
| 559 | "$(SRCDIR)\forum.js" \ |
| 560 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 561 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 562 | "$(SRCDIR)\fossil.dom.js" \ |
| 563 | "$(SRCDIR)\fossil.fetch.js" \ |
| 564 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 565 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 566 | "$(SRCDIR)\fossil.storage.js" \ |
| 567 | "$(SRCDIR)\fossil.tabs.js" \ |
| 568 | "$(SRCDIR)\graph.js" \ |
| 569 | "$(SRCDIR)\href.js" \ |
| 570 | "$(SRCDIR)\login.js" \ |
| @@ -590,10 +593,11 @@ | |
| 590 | "$(SRCDIR)\sounds\d.wav" \ |
| 591 | "$(SRCDIR)\sounds\e.wav" \ |
| 592 | "$(SRCDIR)\sounds\f.wav" \ |
| 593 | "$(SRCDIR)\style.admin_log.css" \ |
| 594 | "$(SRCDIR)\style.fileedit.css" \ |
| 595 | "$(SRCDIR)\tree.js" \ |
| 596 | "$(SRCDIR)\useredit.js" \ |
| 597 | "$(SRCDIR)\wiki.wiki" |
| 598 | |
| 599 | OBJ = "$(OX)\add$O" \ |
| @@ -740,11 +744,10 @@ | |
| 740 | "$(OX)\webmail$O" \ |
| 741 | "$(OX)\wiki$O" \ |
| 742 | "$(OX)\wikiformat$O" \ |
| 743 | "$(OX)\winfile$O" \ |
| 744 | "$(OX)\winhttp$O" \ |
| 745 | "$(OX)\wysiwyg$O" \ |
| 746 | "$(OX)\xfer$O" \ |
| 747 | "$(OX)\xfersetup$O" \ |
| 748 | "$(OX)\zip$O" \ |
| 749 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 750 | "$(OX)\miniz$O" \ |
| @@ -967,11 +970,10 @@ | |
| 967 | echo "$(OX)\webmail.obj" >> $@ |
| 968 | echo "$(OX)\wiki.obj" >> $@ |
| 969 | echo "$(OX)\wikiformat.obj" >> $@ |
| 970 | echo "$(OX)\winfile.obj" >> $@ |
| 971 | echo "$(OX)\winhttp.obj" >> $@ |
| 972 | echo "$(OX)\wysiwyg.obj" >> $@ |
| 973 | echo "$(OX)\xfer.obj" >> $@ |
| 974 | echo "$(OX)\xfersetup.obj" >> $@ |
| 975 | echo "$(OX)\zip.obj" >> $@ |
| 976 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 977 | echo "$(OX)\miniz.obj" >> $@ |
| @@ -1022,12 +1024,15 @@ | |
| 1022 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1023 | |
| 1024 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1025 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1026 | |
| 1027 | "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" |
| 1028 | $** > $@ |
| 1029 | |
| 1030 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1031 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1032 | |
| 1033 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| @@ -1148,14 +1153,18 @@ | |
| 1148 | echo "$(SRCDIR)\default.css" >> $@ |
| 1149 | echo "$(SRCDIR)\diff.tcl" >> $@ |
| 1150 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1151 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1152 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1153 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1154 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1155 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1156 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1157 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1158 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1159 | echo "$(SRCDIR)\graph.js" >> $@ |
| 1160 | echo "$(SRCDIR)\href.js" >> $@ |
| 1161 | echo "$(SRCDIR)\login.js" >> $@ |
| @@ -1181,10 +1190,11 @@ | |
| 1181 | echo "$(SRCDIR)\sounds/d.wav" >> $@ |
| 1182 | echo "$(SRCDIR)\sounds/e.wav" >> $@ |
| 1183 | echo "$(SRCDIR)\sounds/f.wav" >> $@ |
| 1184 | echo "$(SRCDIR)\style.admin_log.css" >> $@ |
| 1185 | echo "$(SRCDIR)\style.fileedit.css" >> $@ |
| 1186 | echo "$(SRCDIR)\tree.js" >> $@ |
| 1187 | echo "$(SRCDIR)\useredit.js" >> $@ |
| 1188 | echo "$(SRCDIR)\wiki.wiki" >> $@ |
| 1189 | |
| 1190 | "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" |
| @@ -2025,16 +2035,10 @@ | |
| 2025 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winhttp_.c" |
| 2026 | |
| 2027 | "$(OX)\winhttp_.c" : "$(SRCDIR)\winhttp.c" |
| 2028 | "$(OBJDIR)\translate$E" $** > $@ |
| 2029 | |
| 2030 | "$(OX)\wysiwyg$O" : "$(OX)\wysiwyg_.c" "$(OX)\wysiwyg.h" |
| 2031 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wysiwyg_.c" |
| 2032 | |
| 2033 | "$(OX)\wysiwyg_.c" : "$(SRCDIR)\wysiwyg.c" |
| 2034 | "$(OBJDIR)\translate$E" $** > $@ |
| 2035 | |
| 2036 | "$(OX)\xfer$O" : "$(OX)\xfer_.c" "$(OX)\xfer.h" |
| 2037 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfer_.c" |
| 2038 | |
| 2039 | "$(OX)\xfer_.c" : "$(SRCDIR)\xfer.c" |
| 2040 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2193,14 +2197,13 @@ | |
| 2193 | "$(OX)\webmail_.c":"$(OX)\webmail.h" \ |
| 2194 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2195 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2196 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2197 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2198 | "$(OX)\wysiwyg_.c":"$(OX)\wysiwyg.h" \ |
| 2199 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2200 | "$(OX)\xfersetup_.c":"$(OX)\xfersetup.h" \ |
| 2201 | "$(OX)\zip_.c":"$(OX)\zip.h" \ |
| 2202 | "$(SRCDIR)\sqlite3.h" \ |
| 2203 | "$(SRCDIR)\th.h" \ |
| 2204 | "$(OX)\VERSION.h" \ |
| 2205 | "$(SRCDIR)\cson_amalgamation.h" |
| 2206 | @copy /Y nul: $@ |
| 2207 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -488,11 +488,10 @@ | |
| 488 | "$(OX)\webmail_.c" \ |
| 489 | "$(OX)\wiki_.c" \ |
| 490 | "$(OX)\wikiformat_.c" \ |
| 491 | "$(OX)\winfile_.c" \ |
| 492 | "$(OX)\winhttp_.c" \ |
| 493 | "$(OX)\xfer_.c" \ |
| 494 | "$(OX)\xfersetup_.c" \ |
| 495 | "$(OX)\zip_.c" |
| 496 | |
| 497 | EXTRA_FILES = "$(SRCDIR)\..\skins\aht\details.txt" \ |
| @@ -557,14 +556,18 @@ | |
| 556 | "$(SRCDIR)\default.css" \ |
| 557 | "$(SRCDIR)\diff.tcl" \ |
| 558 | "$(SRCDIR)\forum.js" \ |
| 559 | "$(SRCDIR)\fossil.bootstrap.js" \ |
| 560 | "$(SRCDIR)\fossil.confirmer.js" \ |
| 561 | "$(SRCDIR)\fossil.copybutton.js" \ |
| 562 | "$(SRCDIR)\fossil.dom.js" \ |
| 563 | "$(SRCDIR)\fossil.fetch.js" \ |
| 564 | "$(SRCDIR)\fossil.numbered-lines.js" \ |
| 565 | "$(SRCDIR)\fossil.page.fileedit.js" \ |
| 566 | "$(SRCDIR)\fossil.page.forumpost.js" \ |
| 567 | "$(SRCDIR)\fossil.page.wikiedit.js" \ |
| 568 | "$(SRCDIR)\fossil.popupwidget.js" \ |
| 569 | "$(SRCDIR)\fossil.storage.js" \ |
| 570 | "$(SRCDIR)\fossil.tabs.js" \ |
| 571 | "$(SRCDIR)\graph.js" \ |
| 572 | "$(SRCDIR)\href.js" \ |
| 573 | "$(SRCDIR)\login.js" \ |
| @@ -590,10 +593,11 @@ | |
| 593 | "$(SRCDIR)\sounds\d.wav" \ |
| 594 | "$(SRCDIR)\sounds\e.wav" \ |
| 595 | "$(SRCDIR)\sounds\f.wav" \ |
| 596 | "$(SRCDIR)\style.admin_log.css" \ |
| 597 | "$(SRCDIR)\style.fileedit.css" \ |
| 598 | "$(SRCDIR)\style.wikiedit.css" \ |
| 599 | "$(SRCDIR)\tree.js" \ |
| 600 | "$(SRCDIR)\useredit.js" \ |
| 601 | "$(SRCDIR)\wiki.wiki" |
| 602 | |
| 603 | OBJ = "$(OX)\add$O" \ |
| @@ -740,11 +744,10 @@ | |
| 744 | "$(OX)\webmail$O" \ |
| 745 | "$(OX)\wiki$O" \ |
| 746 | "$(OX)\wikiformat$O" \ |
| 747 | "$(OX)\winfile$O" \ |
| 748 | "$(OX)\winhttp$O" \ |
| 749 | "$(OX)\xfer$O" \ |
| 750 | "$(OX)\xfersetup$O" \ |
| 751 | "$(OX)\zip$O" \ |
| 752 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 753 | "$(OX)\miniz$O" \ |
| @@ -967,11 +970,10 @@ | |
| 970 | echo "$(OX)\webmail.obj" >> $@ |
| 971 | echo "$(OX)\wiki.obj" >> $@ |
| 972 | echo "$(OX)\wikiformat.obj" >> $@ |
| 973 | echo "$(OX)\winfile.obj" >> $@ |
| 974 | echo "$(OX)\winhttp.obj" >> $@ |
| 975 | echo "$(OX)\xfer.obj" >> $@ |
| 976 | echo "$(OX)\xfersetup.obj" >> $@ |
| 977 | echo "$(OX)\zip.obj" >> $@ |
| 978 | !if $(FOSSIL_ENABLE_MINIZ)!=0 |
| 979 | echo "$(OX)\miniz.obj" >> $@ |
| @@ -1022,12 +1024,15 @@ | |
| 1024 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1025 | |
| 1026 | "$(OX)\miniz$O" : "$(SRCDIR)\miniz.c" |
| 1027 | $(TCC) /Fo$@ /Fd$(@D)\ -c $(MINIZ_OPTIONS) $** |
| 1028 | |
| 1029 | "$(OX)\VERSION.h" : "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" "$(B)\phony.h" |
| 1030 | "$(OBJDIR)\mkversion$E" "$(B)\manifest.uuid" "$(B)\manifest" "$(B)\VERSION" > $@ |
| 1031 | |
| 1032 | "$(B)\phony.h" : |
| 1033 | rem Force rebuild of VERSION.h whenever nmake is run |
| 1034 | |
| 1035 | "$(OX)\cson_amalgamation$O" : "$(SRCDIR)\cson_amalgamation.c" |
| 1036 | $(TCC) /Fo$@ /Fd$(@D)\ -c $** |
| 1037 | |
| 1038 | "$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC) |
| @@ -1148,14 +1153,18 @@ | |
| 1153 | echo "$(SRCDIR)\default.css" >> $@ |
| 1154 | echo "$(SRCDIR)\diff.tcl" >> $@ |
| 1155 | echo "$(SRCDIR)\forum.js" >> $@ |
| 1156 | echo "$(SRCDIR)\fossil.bootstrap.js" >> $@ |
| 1157 | echo "$(SRCDIR)\fossil.confirmer.js" >> $@ |
| 1158 | echo "$(SRCDIR)\fossil.copybutton.js" >> $@ |
| 1159 | echo "$(SRCDIR)\fossil.dom.js" >> $@ |
| 1160 | echo "$(SRCDIR)\fossil.fetch.js" >> $@ |
| 1161 | echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ |
| 1162 | echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ |
| 1163 | echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ |
| 1164 | echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ |
| 1165 | echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ |
| 1166 | echo "$(SRCDIR)\fossil.storage.js" >> $@ |
| 1167 | echo "$(SRCDIR)\fossil.tabs.js" >> $@ |
| 1168 | echo "$(SRCDIR)\graph.js" >> $@ |
| 1169 | echo "$(SRCDIR)\href.js" >> $@ |
| 1170 | echo "$(SRCDIR)\login.js" >> $@ |
| @@ -1181,10 +1190,11 @@ | |
| 1190 | echo "$(SRCDIR)\sounds/d.wav" >> $@ |
| 1191 | echo "$(SRCDIR)\sounds/e.wav" >> $@ |
| 1192 | echo "$(SRCDIR)\sounds/f.wav" >> $@ |
| 1193 | echo "$(SRCDIR)\style.admin_log.css" >> $@ |
| 1194 | echo "$(SRCDIR)\style.fileedit.css" >> $@ |
| 1195 | echo "$(SRCDIR)\style.wikiedit.css" >> $@ |
| 1196 | echo "$(SRCDIR)\tree.js" >> $@ |
| 1197 | echo "$(SRCDIR)\useredit.js" >> $@ |
| 1198 | echo "$(SRCDIR)\wiki.wiki" >> $@ |
| 1199 | |
| 1200 | "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" |
| @@ -2025,16 +2035,10 @@ | |
| 2035 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\winhttp_.c" |
| 2036 | |
| 2037 | "$(OX)\winhttp_.c" : "$(SRCDIR)\winhttp.c" |
| 2038 | "$(OBJDIR)\translate$E" $** > $@ |
| 2039 | |
| 2040 | "$(OX)\xfer$O" : "$(OX)\xfer_.c" "$(OX)\xfer.h" |
| 2041 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\xfer_.c" |
| 2042 | |
| 2043 | "$(OX)\xfer_.c" : "$(SRCDIR)\xfer.c" |
| 2044 | "$(OBJDIR)\translate$E" $** > $@ |
| @@ -2193,14 +2197,13 @@ | |
| 2197 | "$(OX)\webmail_.c":"$(OX)\webmail.h" \ |
| 2198 | "$(OX)\wiki_.c":"$(OX)\wiki.h" \ |
| 2199 | "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \ |
| 2200 | "$(OX)\winfile_.c":"$(OX)\winfile.h" \ |
| 2201 | "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \ |
| 2202 | "$(OX)\xfer_.c":"$(OX)\xfer.h" \ |
| 2203 | "$(OX)\xfersetup_.c":"$(OX)\xfersetup.h" \ |
| 2204 | "$(OX)\zip_.c":"$(OX)\zip.h" \ |
| 2205 | "$(SRCDIR)\sqlite3.h" \ |
| 2206 | "$(SRCDIR)\th.h" \ |
| 2207 | "$(OX)\VERSION.h" \ |
| 2208 | "$(SRCDIR)\cson_amalgamation.h" |
| 2209 | @copy /Y nul: $@ |
| 2210 |
+19
-3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,22 +1,27 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <a name='v2_12'></a> |
| 4 | -<h2>Changes for Version 2.12 (pending)</h2> | |
| 4 | +<h2>Changes for Version 2.12</h2> | |
| 5 | 5 | |
| 6 | 6 | * Security fix in the "[/help?cmd=git|fossil git export]" command. |
| 7 | 7 | The same fix is also backported to version 2.10.1 and 2.11.1. |
| 8 | 8 | New "safety-net" features were added to prevent similar problems |
| 9 | 9 | in the future. |
| 10 | 10 | * Enhancements to the graph display for cases when there are |
| 11 | 11 | many cherry-pick merges into a single check-in. |
| 12 | 12 | [/timeline?f=2d75e87b760c0a9|Example] |
| 13 | + * Enhance the [/help?cmd=open|fossil open] command with the new | |
| 14 | + --workdir option and the ability to accept a URL as the repository | |
| 15 | + name, causing the remote repository to be cloned automatically. | |
| 16 | + Do not allow "fossil open" to open in a non-empty working directory | |
| 17 | + unless the --keep option or the new --force option is used. | |
| 13 | 18 | * Enhance the markdown formatter to more closely follow the |
| 14 | 19 | [https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis|CommonMark specification] |
| 15 | 20 | with regard to text highlighting. |
| 16 | - Underscores in the middle of identifiers (ex: fossil_printf() | |
| 17 | - no longer need to be escaped). | |
| 21 | + Underscores in the middle of identifiers (ex: fossil_printf()) | |
| 22 | + no longer need to be escaped. | |
| 18 | 23 | * The markdown-to-html translator can prevent unsafe HTML |
| 19 | 24 | (for example: <script>) on user-contributed pages like forum and |
| 20 | 25 | tickets and wiki. The admin can adjust this behavior using |
| 21 | 26 | the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page. |
| 22 | 27 | The default is to disallow unsafe HTML everywhere. |
| @@ -57,11 +62,22 @@ | ||
| 57 | 62 | * Update the built-in SQLite so that the |
| 58 | 63 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 59 | 64 | modes ".mode box" and ".mode json". |
| 60 | 65 | * Add the "<tt>obscure()</tt>" SQL function to the |
| 61 | 66 | "[/help?cmd=sql|fossil sql]" command. |
| 67 | + * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to | |
| 68 | + the "[/help?cmd=sql|fossil sql]" command, providing access to the | |
| 69 | + dispatch table including all help text, and the builtin data files, | |
| 70 | + respectively. | |
| 62 | 71 | * [./delta_format.wiki|Delta compression] is now applied to forum edits. |
| 72 | + * The [/help?cmd=/wikiedit|wiki editor] has been modernized and is | |
| 73 | + now Ajax-based. The WYSIWYG editing option for Fossil-format wiki | |
| 74 | + pages was removed. (Please let us know, via the site's Support menu, | |
| 75 | + if that removal unduly impacts you.) This also changes the semantics | |
| 76 | + of the wiki "Sandbox": that pseudo-page may be freely edited but | |
| 77 | + no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command] | |
| 78 | + can, though). | |
| 63 | 79 | * Countless documentation enhancements. |
| 64 | 80 | |
| 65 | 81 | <a name='v2_11'></a> |
| 66 | 82 | <h2>Changes for Version 2.11 (2020-05-25)</h2> |
| 67 | 83 | |
| 68 | 84 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,22 +1,27 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_12'></a> |
| 4 | <h2>Changes for Version 2.12 (pending)</h2> |
| 5 | |
| 6 | * Security fix in the "[/help?cmd=git|fossil git export]" command. |
| 7 | The same fix is also backported to version 2.10.1 and 2.11.1. |
| 8 | New "safety-net" features were added to prevent similar problems |
| 9 | in the future. |
| 10 | * Enhancements to the graph display for cases when there are |
| 11 | many cherry-pick merges into a single check-in. |
| 12 | [/timeline?f=2d75e87b760c0a9|Example] |
| 13 | * Enhance the markdown formatter to more closely follow the |
| 14 | [https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis|CommonMark specification] |
| 15 | with regard to text highlighting. |
| 16 | Underscores in the middle of identifiers (ex: fossil_printf() |
| 17 | no longer need to be escaped). |
| 18 | * The markdown-to-html translator can prevent unsafe HTML |
| 19 | (for example: <script>) on user-contributed pages like forum and |
| 20 | tickets and wiki. The admin can adjust this behavior using |
| 21 | the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page. |
| 22 | The default is to disallow unsafe HTML everywhere. |
| @@ -57,11 +62,22 @@ | |
| 57 | * Update the built-in SQLite so that the |
| 58 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 59 | modes ".mode box" and ".mode json". |
| 60 | * Add the "<tt>obscure()</tt>" SQL function to the |
| 61 | "[/help?cmd=sql|fossil sql]" command. |
| 62 | * [./delta_format.wiki|Delta compression] is now applied to forum edits. |
| 63 | * Countless documentation enhancements. |
| 64 | |
| 65 | <a name='v2_11'></a> |
| 66 | <h2>Changes for Version 2.11 (2020-05-25)</h2> |
| 67 | |
| 68 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,22 +1,27 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <a name='v2_12'></a> |
| 4 | <h2>Changes for Version 2.12</h2> |
| 5 | |
| 6 | * Security fix in the "[/help?cmd=git|fossil git export]" command. |
| 7 | The same fix is also backported to version 2.10.1 and 2.11.1. |
| 8 | New "safety-net" features were added to prevent similar problems |
| 9 | in the future. |
| 10 | * Enhancements to the graph display for cases when there are |
| 11 | many cherry-pick merges into a single check-in. |
| 12 | [/timeline?f=2d75e87b760c0a9|Example] |
| 13 | * Enhance the [/help?cmd=open|fossil open] command with the new |
| 14 | --workdir option and the ability to accept a URL as the repository |
| 15 | name, causing the remote repository to be cloned automatically. |
| 16 | Do not allow "fossil open" to open in a non-empty working directory |
| 17 | unless the --keep option or the new --force option is used. |
| 18 | * Enhance the markdown formatter to more closely follow the |
| 19 | [https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis|CommonMark specification] |
| 20 | with regard to text highlighting. |
| 21 | Underscores in the middle of identifiers (ex: fossil_printf()) |
| 22 | no longer need to be escaped. |
| 23 | * The markdown-to-html translator can prevent unsafe HTML |
| 24 | (for example: <script>) on user-contributed pages like forum and |
| 25 | tickets and wiki. The admin can adjust this behavior using |
| 26 | the [/help?cmd=safe-html|safe-html setting] on the Admin/Wiki page. |
| 27 | The default is to disallow unsafe HTML everywhere. |
| @@ -57,11 +62,22 @@ | |
| 62 | * Update the built-in SQLite so that the |
| 63 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 64 | modes ".mode box" and ".mode json". |
| 65 | * Add the "<tt>obscure()</tt>" SQL function to the |
| 66 | "[/help?cmd=sql|fossil sql]" command. |
| 67 | * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to |
| 68 | the "[/help?cmd=sql|fossil sql]" command, providing access to the |
| 69 | dispatch table including all help text, and the builtin data files, |
| 70 | respectively. |
| 71 | * [./delta_format.wiki|Delta compression] is now applied to forum edits. |
| 72 | * The [/help?cmd=/wikiedit|wiki editor] has been modernized and is |
| 73 | now Ajax-based. The WYSIWYG editing option for Fossil-format wiki |
| 74 | pages was removed. (Please let us know, via the site's Support menu, |
| 75 | if that removal unduly impacts you.) This also changes the semantics |
| 76 | of the wiki "Sandbox": that pseudo-page may be freely edited but |
| 77 | no longer saved via the UI (the [/help?cmd=wiki|wiki CLI command] |
| 78 | can, though). |
| 79 | * Countless documentation enhancements. |
| 80 | |
| 81 | <a name='v2_11'></a> |
| 82 | <h2>Changes for Version 2.11 (2020-05-25)</h2> |
| 83 | |
| 84 |
+1
-1
| --- www/fileedit-page.md | ||
| +++ www/fileedit-page.md | ||
| @@ -245,11 +245,11 @@ | ||
| 245 | 245 | |
| 246 | 246 | First, install proxy functions so that `fossil.page.fileContent()` |
| 247 | 247 | can get and set your content: |
| 248 | 248 | |
| 249 | 249 | ``` |
| 250 | -fossil.page.setFileContentMethods( | |
| 250 | +fossil.page.setContentMethods( | |
| 251 | 251 | function(){ return text-form content of your widget }, |
| 252 | 252 | function(content){ set text-form content of your widget } |
| 253 | 253 | }; |
| 254 | 254 | ``` |
| 255 | 255 | |
| 256 | 256 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -245,11 +245,11 @@ | |
| 245 | |
| 246 | First, install proxy functions so that `fossil.page.fileContent()` |
| 247 | can get and set your content: |
| 248 | |
| 249 | ``` |
| 250 | fossil.page.setFileContentMethods( |
| 251 | function(){ return text-form content of your widget }, |
| 252 | function(content){ set text-form content of your widget } |
| 253 | }; |
| 254 | ``` |
| 255 | |
| 256 |
| --- www/fileedit-page.md | |
| +++ www/fileedit-page.md | |
| @@ -245,11 +245,11 @@ | |
| 245 | |
| 246 | First, install proxy functions so that `fossil.page.fileContent()` |
| 247 | can get and set your content: |
| 248 | |
| 249 | ``` |
| 250 | fossil.page.setContentMethods( |
| 251 | function(){ return text-form content of your widget }, |
| 252 | function(content){ set text-form content of your widget } |
| 253 | }; |
| 254 | ``` |
| 255 | |
| 256 |
+1
-1
| --- www/gitusers.md | ||
| +++ www/gitusers.md | ||
| @@ -137,11 +137,11 @@ | ||
| 137 | 137 | [3]: ./rebaseharm.md |
| 138 | 138 | |
| 139 | 139 | ## Branch and Tag Names |
| 140 | 140 | |
| 141 | 141 | Fossil has no special restrictions on the names of tags and branches, |
| 142 | -though you might want to to keep [Git's tag and branch name restrictions][4] | |
| 142 | +though you might want to keep [Git's tag and branch name restrictions][4] | |
| 143 | 143 | in mind if you plan on mirroring your Fossil repository to GitHub. |
| 144 | 144 | |
| 145 | 145 | [4]: https://git-scm.com/docs/git-check-ref-format |
| 146 | 146 | |
| 147 | 147 | Fossil does not require tag and branch names to be unique. It is |
| 148 | 148 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -137,11 +137,11 @@ | |
| 137 | [3]: ./rebaseharm.md |
| 138 | |
| 139 | ## Branch and Tag Names |
| 140 | |
| 141 | Fossil has no special restrictions on the names of tags and branches, |
| 142 | though you might want to to keep [Git's tag and branch name restrictions][4] |
| 143 | in mind if you plan on mirroring your Fossil repository to GitHub. |
| 144 | |
| 145 | [4]: https://git-scm.com/docs/git-check-ref-format |
| 146 | |
| 147 | Fossil does not require tag and branch names to be unique. It is |
| 148 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -137,11 +137,11 @@ | |
| 137 | [3]: ./rebaseharm.md |
| 138 | |
| 139 | ## Branch and Tag Names |
| 140 | |
| 141 | Fossil has no special restrictions on the names of tags and branches, |
| 142 | though you might want to keep [Git's tag and branch name restrictions][4] |
| 143 | in mind if you plan on mirroring your Fossil repository to GitHub. |
| 144 | |
| 145 | [4]: https://git-scm.com/docs/git-check-ref-format |
| 146 | |
| 147 | Fossil does not require tag and branch names to be unique. It is |
| 148 |
+10
-6
| --- www/server/openbsd/fastcgi.md | ||
| +++ www/server/openbsd/fastcgi.md | ||
| @@ -66,20 +66,24 @@ | ||
| 66 | 66 | ## <a name="chroot"></a>Setup chroot |
| 67 | 67 | |
| 68 | 68 | Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible |
| 69 | 69 | from within the chroot, so need to be constructed; `/var`, however, is |
| 70 | 70 | mounted with the `nodev` option. Rather than removing this default |
| 71 | -setting, create a small memory filesystem with [`mount_mfs(8)`][mfs] | |
| 72 | -upon which `/var/www/dev` will be mounted so that the `random` and | |
| 73 | -`null` device files can be created. | |
| 71 | +setting, create a small memory filesystem and then mount it on to | |
| 72 | +`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and | |
| 73 | +`null` device files can be created. In order to avoid neccessitating a | |
| 74 | +startup script to recreate the device files at boot, create a template | |
| 75 | +of the needed ``/dev`` tree to automatically populate the memory | |
| 76 | +filesystem. | |
| 74 | 77 | |
| 75 | 78 | ```console |
| 76 | 79 | $ doas mkdir /var/www/dev |
| 77 | - $ doas mount_mfs -s 1M /dev/sd0b /var/www/dev | |
| 78 | - $ doas cd /var/www/dev | |
| 80 | + $ doas install -d -g daemon /template/dev | |
| 81 | + $ cd /template/dev | |
| 79 | 82 | $ doas /dev/MAKEDEV urandom |
| 80 | 83 | $ doas mknod -m 666 null c 2 2 |
| 84 | + $ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev | |
| 81 | 85 | $ ls -l |
| 82 | 86 | total 0 |
| 83 | 87 | crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null |
| 84 | 88 | lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom |
| 85 | 89 | crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom |
| @@ -90,11 +94,11 @@ | ||
| 90 | 94 | To make the mountable memory filesystem permanent, open `/etc/fstab` as |
| 91 | 95 | a privileged user and add the following line to automate creation of the |
| 92 | 96 | filesystem at startup: |
| 93 | 97 | |
| 94 | 98 | ```console |
| 95 | - swap /var/www/dev mfs rw,-s=1048576 0 0 | |
| 99 | + swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0 | |
| 96 | 100 | ``` |
| 97 | 101 | |
| 98 | 102 | The same user that executes the fossil binary must have writable access |
| 99 | 103 | to the repository directory that resides within the chroot; on OpenBSD |
| 100 | 104 | this is `www`. In addition, grant repository directory ownership to the |
| 101 | 105 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -66,20 +66,24 @@ | |
| 66 | ## <a name="chroot"></a>Setup chroot |
| 67 | |
| 68 | Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible |
| 69 | from within the chroot, so need to be constructed; `/var`, however, is |
| 70 | mounted with the `nodev` option. Rather than removing this default |
| 71 | setting, create a small memory filesystem with [`mount_mfs(8)`][mfs] |
| 72 | upon which `/var/www/dev` will be mounted so that the `random` and |
| 73 | `null` device files can be created. |
| 74 | |
| 75 | ```console |
| 76 | $ doas mkdir /var/www/dev |
| 77 | $ doas mount_mfs -s 1M /dev/sd0b /var/www/dev |
| 78 | $ doas cd /var/www/dev |
| 79 | $ doas /dev/MAKEDEV urandom |
| 80 | $ doas mknod -m 666 null c 2 2 |
| 81 | $ ls -l |
| 82 | total 0 |
| 83 | crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null |
| 84 | lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom |
| 85 | crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom |
| @@ -90,11 +94,11 @@ | |
| 90 | To make the mountable memory filesystem permanent, open `/etc/fstab` as |
| 91 | a privileged user and add the following line to automate creation of the |
| 92 | filesystem at startup: |
| 93 | |
| 94 | ```console |
| 95 | swap /var/www/dev mfs rw,-s=1048576 0 0 |
| 96 | ``` |
| 97 | |
| 98 | The same user that executes the fossil binary must have writable access |
| 99 | to the repository directory that resides within the chroot; on OpenBSD |
| 100 | this is `www`. In addition, grant repository directory ownership to the |
| 101 |
| --- www/server/openbsd/fastcgi.md | |
| +++ www/server/openbsd/fastcgi.md | |
| @@ -66,20 +66,24 @@ | |
| 66 | ## <a name="chroot"></a>Setup chroot |
| 67 | |
| 68 | Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible |
| 69 | from within the chroot, so need to be constructed; `/var`, however, is |
| 70 | mounted with the `nodev` option. Rather than removing this default |
| 71 | setting, create a small memory filesystem and then mount it on to |
| 72 | `/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and |
| 73 | `null` device files can be created. In order to avoid neccessitating a |
| 74 | startup script to recreate the device files at boot, create a template |
| 75 | of the needed ``/dev`` tree to automatically populate the memory |
| 76 | filesystem. |
| 77 | |
| 78 | ```console |
| 79 | $ doas mkdir /var/www/dev |
| 80 | $ doas install -d -g daemon /template/dev |
| 81 | $ cd /template/dev |
| 82 | $ doas /dev/MAKEDEV urandom |
| 83 | $ doas mknod -m 666 null c 2 2 |
| 84 | $ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev |
| 85 | $ ls -l |
| 86 | total 0 |
| 87 | crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null |
| 88 | lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom |
| 89 | crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom |
| @@ -90,11 +94,11 @@ | |
| 94 | To make the mountable memory filesystem permanent, open `/etc/fstab` as |
| 95 | a privileged user and add the following line to automate creation of the |
| 96 | filesystem at startup: |
| 97 | |
| 98 | ```console |
| 99 | swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0 |
| 100 | ``` |
| 101 | |
| 102 | The same user that executes the fossil binary must have writable access |
| 103 | to the repository directory that resides within the chroot; on OpenBSD |
| 104 | this is `www`. In addition, grant repository directory ownership to the |
| 105 |