Fossil SCM
Merge in updates from trunk.
Commit
870c20885ba98d678337c5627a8c7f1040f52658
Parent
7df56d1c14ba993…
28 files changed
+11
-1
+67
-27
+61
+47
-4
+4
-3
+5
-4
+1
-1
+1
-1
+3
-3
+5
-5
+233
-92
+4
-1
+1
+31
-12
+106
-11
+108
-26
+6
-5
+14
-9
+157
+26
-2
+42
+159
+1
-1
+1
-1
+2
-2
+1
-1
+8
-3
+31
-6
~
src/allrepo.c
~
src/checkin.c
~
src/delta.c
~
src/deltacmd.c
~
src/doc.c
~
src/foci.c
~
src/info.c
~
src/info.c
~
src/makemake.tcl
~
src/merge.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stash.c
~
src/th_lang.c
~
src/th_tcl.c
~
src/undo.c
~
src/update.c
~
src/utf8.c
~
test/clean.test
~
test/tester.tcl
~
test/th1-tcl.test
~
test/th1.test
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
www/build.wiki
~
www/changes.wiki
~
www/th1.md
+11
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -86,10 +86,14 @@ | ||
| 86 | 86 | ** |
| 87 | 87 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 88 | 88 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 89 | 89 | ** |
| 90 | 90 | ** Available operations are: |
| 91 | +** | |
| 92 | +** cache Mangages the cache used for potentially expensive web | |
| 93 | +** pages. Any additional arguments are passed on verbatim | |
| 94 | +** to the cache command. | |
| 91 | 95 | ** |
| 92 | 96 | ** changes Shows all local checkouts that have uncommitted changes. |
| 93 | 97 | ** This operation has no additional options. |
| 94 | 98 | ** |
| 95 | 99 | ** clean Delete all "extra" files in all local checkouts. Extreme |
| @@ -192,15 +196,17 @@ | ||
| 192 | 196 | zCmd = "clean --chdir"; |
| 193 | 197 | collect_argument(&extra, "allckouts",0); |
| 194 | 198 | collect_argument_value(&extra, "case-sensitive"); |
| 195 | 199 | collect_argument_value(&extra, "clean"); |
| 196 | 200 | collect_argument(&extra, "dirsonly",0); |
| 201 | + collect_argument(&extra, "disable-undo",0); | |
| 197 | 202 | collect_argument(&extra, "dotfiles",0); |
| 198 | 203 | collect_argument(&extra, "emptydirs",0); |
| 199 | 204 | collect_argument(&extra, "force","f"); |
| 200 | 205 | collect_argument_value(&extra, "ignore"); |
| 201 | 206 | collect_argument_value(&extra, "keep"); |
| 207 | + collect_argument(&extra, "no-prompt",0); | |
| 202 | 208 | collect_argument(&extra, "temp",0); |
| 203 | 209 | collect_argument(&extra, "verbose","v"); |
| 204 | 210 | collect_argument(&extra, "whatif",0); |
| 205 | 211 | useCheckouts = 1; |
| 206 | 212 | }else if( strncmp(zCmd, "dbstat", n)==0 ){ |
| @@ -327,13 +333,17 @@ | ||
| 327 | 333 | return; |
| 328 | 334 | }else if( strncmp(zCmd, "info", n)==0 ){ |
| 329 | 335 | zCmd = "info"; |
| 330 | 336 | showLabel = 1; |
| 331 | 337 | quiet = 1; |
| 338 | + }else if( strncmp(zCmd, "cache", n)==0 ){ | |
| 339 | + zCmd = "cache -R"; | |
| 340 | + showLabel = 1; | |
| 341 | + collect_argv(&extra, 3); | |
| 332 | 342 | }else{ |
| 333 | 343 | fossil_fatal("\"all\" subcommand should be one of: " |
| 334 | - "add changes clean dbstat extras fts-config ignore " | |
| 344 | + "add cache changes clean dbstat extras fts-config ignore " | |
| 335 | 345 | "info list ls pull push rebuild setting sync unset"); |
| 336 | 346 | } |
| 337 | 347 | verify_all_options(); |
| 338 | 348 | zFossil = quoteFilename(g.nameOfExe); |
| 339 | 349 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 340 | 350 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -86,10 +86,14 @@ | |
| 86 | ** |
| 87 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 88 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 89 | ** |
| 90 | ** Available operations are: |
| 91 | ** |
| 92 | ** changes Shows all local checkouts that have uncommitted changes. |
| 93 | ** This operation has no additional options. |
| 94 | ** |
| 95 | ** clean Delete all "extra" files in all local checkouts. Extreme |
| @@ -192,15 +196,17 @@ | |
| 192 | zCmd = "clean --chdir"; |
| 193 | collect_argument(&extra, "allckouts",0); |
| 194 | collect_argument_value(&extra, "case-sensitive"); |
| 195 | collect_argument_value(&extra, "clean"); |
| 196 | collect_argument(&extra, "dirsonly",0); |
| 197 | collect_argument(&extra, "dotfiles",0); |
| 198 | collect_argument(&extra, "emptydirs",0); |
| 199 | collect_argument(&extra, "force","f"); |
| 200 | collect_argument_value(&extra, "ignore"); |
| 201 | collect_argument_value(&extra, "keep"); |
| 202 | collect_argument(&extra, "temp",0); |
| 203 | collect_argument(&extra, "verbose","v"); |
| 204 | collect_argument(&extra, "whatif",0); |
| 205 | useCheckouts = 1; |
| 206 | }else if( strncmp(zCmd, "dbstat", n)==0 ){ |
| @@ -327,13 +333,17 @@ | |
| 327 | return; |
| 328 | }else if( strncmp(zCmd, "info", n)==0 ){ |
| 329 | zCmd = "info"; |
| 330 | showLabel = 1; |
| 331 | quiet = 1; |
| 332 | }else{ |
| 333 | fossil_fatal("\"all\" subcommand should be one of: " |
| 334 | "add changes clean dbstat extras fts-config ignore " |
| 335 | "info list ls pull push rebuild setting sync unset"); |
| 336 | } |
| 337 | verify_all_options(); |
| 338 | zFossil = quoteFilename(g.nameOfExe); |
| 339 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 340 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -86,10 +86,14 @@ | |
| 86 | ** |
| 87 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 88 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 89 | ** |
| 90 | ** Available operations are: |
| 91 | ** |
| 92 | ** cache Mangages the cache used for potentially expensive web |
| 93 | ** pages. Any additional arguments are passed on verbatim |
| 94 | ** to the cache command. |
| 95 | ** |
| 96 | ** changes Shows all local checkouts that have uncommitted changes. |
| 97 | ** This operation has no additional options. |
| 98 | ** |
| 99 | ** clean Delete all "extra" files in all local checkouts. Extreme |
| @@ -192,15 +196,17 @@ | |
| 196 | zCmd = "clean --chdir"; |
| 197 | collect_argument(&extra, "allckouts",0); |
| 198 | collect_argument_value(&extra, "case-sensitive"); |
| 199 | collect_argument_value(&extra, "clean"); |
| 200 | collect_argument(&extra, "dirsonly",0); |
| 201 | collect_argument(&extra, "disable-undo",0); |
| 202 | collect_argument(&extra, "dotfiles",0); |
| 203 | collect_argument(&extra, "emptydirs",0); |
| 204 | collect_argument(&extra, "force","f"); |
| 205 | collect_argument_value(&extra, "ignore"); |
| 206 | collect_argument_value(&extra, "keep"); |
| 207 | collect_argument(&extra, "no-prompt",0); |
| 208 | collect_argument(&extra, "temp",0); |
| 209 | collect_argument(&extra, "verbose","v"); |
| 210 | collect_argument(&extra, "whatif",0); |
| 211 | useCheckouts = 1; |
| 212 | }else if( strncmp(zCmd, "dbstat", n)==0 ){ |
| @@ -327,13 +333,17 @@ | |
| 333 | return; |
| 334 | }else if( strncmp(zCmd, "info", n)==0 ){ |
| 335 | zCmd = "info"; |
| 336 | showLabel = 1; |
| 337 | quiet = 1; |
| 338 | }else if( strncmp(zCmd, "cache", n)==0 ){ |
| 339 | zCmd = "cache -R"; |
| 340 | showLabel = 1; |
| 341 | collect_argv(&extra, 3); |
| 342 | }else{ |
| 343 | fossil_fatal("\"all\" subcommand should be one of: " |
| 344 | "add cache changes clean dbstat extras fts-config ignore " |
| 345 | "info list ls pull push rebuild setting sync unset"); |
| 346 | } |
| 347 | verify_all_options(); |
| 348 | zFossil = quoteFilename(g.nameOfExe); |
| 349 | db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); |
| 350 |
+67
-27
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -644,11 +644,12 @@ | ||
| 644 | 644 | ** Files and subdirectories whose names begin with "." are automatically |
| 645 | 645 | ** ignored unless the --dotfiles option is used. |
| 646 | 646 | ** |
| 647 | 647 | ** The --verily option ignores the keep-glob and ignore-glob settings |
| 648 | 648 | ** and turns on --force, --dotfiles, and --emptydirs. Use the --verily |
| 649 | -** option when you really want to clean up everything. | |
| 649 | +** option when you really want to clean up everything. Extreme care | |
| 650 | +** should be exercised when using the --verily option. | |
| 650 | 651 | ** |
| 651 | 652 | ** Options: |
| 652 | 653 | ** --allckouts Check for empty directories within any checkouts |
| 653 | 654 | ** that may be nested within the current one. This |
| 654 | 655 | ** option should be used with great care because the |
| @@ -657,51 +658,70 @@ | ||
| 657 | 658 | ** not be checked. |
| 658 | 659 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 659 | 660 | ** --dirsonly Only remove empty directories. No files will |
| 660 | 661 | ** be removed. Using this option will automatically |
| 661 | 662 | ** enable the --emptydirs option as well. |
| 663 | +** --disable-undo WARNING: This option disables use of the undo | |
| 664 | +** mechanism for this clean operation and should be | |
| 665 | +** used with extreme caution. | |
| 662 | 666 | ** --dotfiles Include files beginning with a dot ("."). |
| 663 | 667 | ** --emptydirs Remove any empty directories that are not |
| 664 | 668 | ** explicitly exempted via the empty-dirs setting |
| 665 | 669 | ** or another applicable setting or command line |
| 666 | 670 | ** argument. Matching files, if any, are removed |
| 667 | 671 | ** prior to checking for any empty directories; |
| 668 | 672 | ** therefore, directories that contain only files |
| 669 | 673 | ** that were removed will be removed as well. |
| 670 | 674 | ** -f|--force Remove files without prompting. |
| 671 | -** -x|--verily Remove everything that is not a managed file or | |
| 672 | -** the repository itself. Implies -f --emptydirs | |
| 673 | -** --dotfiles. Disregard keep-glob and ignore-glob. | |
| 674 | -** --clean <CSG> Never prompt for files matching this | |
| 675 | -** comma separated list of glob patterns. | |
| 675 | +** -x|--verily WARNING: Removes everything that is not a managed | |
| 676 | +** file or the repository itself. This option | |
| 677 | +** implies the --force, --emptydirs, --dotfiles, and | |
| 678 | +** --disable-undo options. Furthermore, it completely | |
| 679 | +** disregards the keep-glob and ignore-glob settings. | |
| 680 | +** However, it does honor the --ignore and --keep | |
| 681 | +** options. | |
| 682 | +** --clean <CSG> WARNING: Never prompt to delete any files matching | |
| 683 | +** this comma separated list of glob patterns. Also, | |
| 684 | +** deletions of any files matching this pattern list | |
| 685 | +** cannot be undone. | |
| 676 | 686 | ** --ignore <CSG> Ignore files matching patterns from the |
| 677 | 687 | ** comma separated list of glob patterns. |
| 678 | 688 | ** --keep <CSG> Keep files matching this comma separated |
| 679 | 689 | ** list of glob patterns. |
| 680 | 690 | ** -n|--dry-run Delete nothing, but display what would have been |
| 681 | 691 | ** deleted. |
| 692 | +** --no-prompt This option disables prompting the user for input | |
| 693 | +** and assumes an answer of 'No' for every question. | |
| 682 | 694 | ** --temp Remove only Fossil-generated temporary files. |
| 683 | 695 | ** -v|--verbose Show all files as they are removed. |
| 684 | 696 | ** |
| 685 | 697 | ** See also: addremove, extras, status |
| 686 | 698 | */ |
| 687 | 699 | void clean_cmd(void){ |
| 688 | 700 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 689 | 701 | int emptyDirsFlag, dirsOnlyFlag; |
| 702 | + int disableUndo, noPrompt; | |
| 690 | 703 | unsigned scanFlags = 0; |
| 691 | 704 | int verilyFlag = 0; |
| 692 | 705 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 693 | 706 | Glob *pIgnore, *pKeep, *pClean; |
| 694 | 707 | int nRoot; |
| 695 | 708 | |
| 709 | +#ifndef UNDO_SIZE_LIMIT /* TODO: Setting? */ | |
| 710 | +#define UNDO_SIZE_LIMIT (10*1024*1024) /* 10MiB */ | |
| 711 | +#endif | |
| 712 | + | |
| 713 | + undo_capture_command_line(); | |
| 696 | 714 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 697 | 715 | if( !dryRunFlag ){ |
| 698 | 716 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 699 | 717 | } |
| 700 | 718 | if( !dryRunFlag ){ |
| 701 | 719 | dryRunFlag = find_option("whatif",0,0)!=0; |
| 702 | 720 | } |
| 721 | + disableUndo = find_option("disable-undo",0,0)!=0; | |
| 722 | + noPrompt = find_option("no-prompt",0,0)!=0; | |
| 703 | 723 | allFileFlag = allDirFlag = find_option("force","f",0)!=0; |
| 704 | 724 | dirsOnlyFlag = find_option("dirsonly",0,0)!=0; |
| 705 | 725 | emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; |
| 706 | 726 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 707 | 727 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| @@ -712,10 +732,11 @@ | ||
| 712 | 732 | zCleanFlag = find_option("clean",0,1); |
| 713 | 733 | db_must_be_within_tree(); |
| 714 | 734 | if( find_option("verily","x",0)!=0 ){ |
| 715 | 735 | verilyFlag = allFileFlag = allDirFlag = 1; |
| 716 | 736 | emptyDirsFlag = 1; |
| 737 | + disableUndo = 1; | |
| 717 | 738 | scanFlags |= SCAN_ALL; |
| 718 | 739 | zCleanFlag = 0; |
| 719 | 740 | } |
| 720 | 741 | if( zIgnoreFlag==0 && !verilyFlag ){ |
| 721 | 742 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -734,10 +755,11 @@ | ||
| 734 | 755 | nRoot = (int)strlen(g.zLocalRoot); |
| 735 | 756 | g.allowSymlinks = 1; /* Find symlinks too */ |
| 736 | 757 | if( !dirsOnlyFlag ){ |
| 737 | 758 | Stmt q; |
| 738 | 759 | Blob repo; |
| 760 | + if( !dryRunFlag && !disableUndo ) undo_begin(); | |
| 739 | 761 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 740 | 762 | db_prepare(&q, |
| 741 | 763 | "SELECT %Q || x FROM sfile" |
| 742 | 764 | " WHERE x NOT IN (%s)" |
| 743 | 765 | " ORDER BY 1", |
| @@ -754,24 +776,38 @@ | ||
| 754 | 776 | fossil_print("KEPT file \"%s\" not removed (due to --keep" |
| 755 | 777 | " or \"keep-glob\")\n", zName+nRoot); |
| 756 | 778 | } |
| 757 | 779 | continue; |
| 758 | 780 | } |
| 759 | - if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ | |
| 760 | - Blob ans; | |
| 761 | - char cReply; | |
| 762 | - char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", | |
| 763 | - zName+nRoot); | |
| 764 | - prompt_user(prompt, &ans); | |
| 765 | - cReply = blob_str(&ans)[0]; | |
| 766 | - if( cReply=='a' || cReply=='A' ){ | |
| 767 | - allFileFlag = 1; | |
| 768 | - }else if( cReply!='y' && cReply!='Y' ){ | |
| 769 | - blob_reset(&ans); | |
| 770 | - continue; | |
| 771 | - } | |
| 772 | - blob_reset(&ans); | |
| 781 | + if( !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ | |
| 782 | + int undoRc = UNDO_NONE; | |
| 783 | + if( !disableUndo ){ | |
| 784 | + undoRc = undo_maybe_save(zName+nRoot, UNDO_SIZE_LIMIT); | |
| 785 | + } | |
| 786 | + if( undoRc!=UNDO_SAVED_OK ){ | |
| 787 | + char cReply; | |
| 788 | + if( allFileFlag ){ | |
| 789 | + cReply = 'Y'; | |
| 790 | + }else if( !noPrompt ){ | |
| 791 | + Blob ans; | |
| 792 | + char *prompt = mprintf("\nWARNING: Deletion of this file will " | |
| 793 | + "not be undoable via the 'undo'\n" | |
| 794 | + " command because %s.\n\n" | |
| 795 | + "Remove unmanaged file \"%s\" (a=all/y/N)? ", | |
| 796 | + undo_save_message(undoRc), zName+nRoot); | |
| 797 | + prompt_user(prompt, &ans); | |
| 798 | + cReply = blob_str(&ans)[0]; | |
| 799 | + blob_reset(&ans); | |
| 800 | + }else{ | |
| 801 | + cReply = 'N'; | |
| 802 | + } | |
| 803 | + if( cReply=='a' || cReply=='A' ){ | |
| 804 | + allFileFlag = 1; | |
| 805 | + }else if( cReply!='y' && cReply!='Y' ){ | |
| 806 | + continue; | |
| 807 | + } | |
| 808 | + } | |
| 773 | 809 | } |
| 774 | 810 | if( dryRunFlag || file_delete(zName)==0 ){ |
| 775 | 811 | if( verboseFlag || dryRunFlag ){ |
| 776 | 812 | fossil_print("Removed unmanaged file: %s\n", zName+nRoot); |
| 777 | 813 | } |
| @@ -778,10 +814,11 @@ | ||
| 778 | 814 | }else if( verboseFlag ){ |
| 779 | 815 | fossil_print("Could not remove file: %s\n", zName+nRoot); |
| 780 | 816 | } |
| 781 | 817 | } |
| 782 | 818 | db_finalize(&q); |
| 819 | + if( !dryRunFlag && !disableUndo ) undo_finish(); | |
| 783 | 820 | } |
| 784 | 821 | if( emptyDirsFlag ){ |
| 785 | 822 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 786 | 823 | Stmt q; |
| 787 | 824 | Blob root; |
| @@ -803,23 +840,26 @@ | ||
| 803 | 840 | " or \"keep-glob\")\n", zName+nRoot); |
| 804 | 841 | } |
| 805 | 842 | continue; |
| 806 | 843 | } |
| 807 | 844 | if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 808 | - Blob ans; | |
| 809 | 845 | char cReply; |
| 810 | - char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", | |
| 811 | - zName+nRoot); | |
| 812 | - prompt_user(prompt, &ans); | |
| 813 | - cReply = blob_str(&ans)[0]; | |
| 846 | + if( !noPrompt ){ | |
| 847 | + Blob ans; | |
| 848 | + char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", | |
| 849 | + zName+nRoot); | |
| 850 | + prompt_user(prompt, &ans); | |
| 851 | + cReply = blob_str(&ans)[0]; | |
| 852 | + blob_reset(&ans); | |
| 853 | + }else{ | |
| 854 | + cReply = 'N'; | |
| 855 | + } | |
| 814 | 856 | if( cReply=='a' || cReply=='A' ){ |
| 815 | 857 | allDirFlag = 1; |
| 816 | 858 | }else if( cReply!='y' && cReply!='Y' ){ |
| 817 | - blob_reset(&ans); | |
| 818 | 859 | continue; |
| 819 | 860 | } |
| 820 | - blob_reset(&ans); | |
| 821 | 861 | } |
| 822 | 862 | if( dryRunFlag || file_rmdir(zName)==0 ){ |
| 823 | 863 | if( verboseFlag || dryRunFlag ){ |
| 824 | 864 | fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); |
| 825 | 865 | } |
| 826 | 866 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -644,11 +644,12 @@ | |
| 644 | ** Files and subdirectories whose names begin with "." are automatically |
| 645 | ** ignored unless the --dotfiles option is used. |
| 646 | ** |
| 647 | ** The --verily option ignores the keep-glob and ignore-glob settings |
| 648 | ** and turns on --force, --dotfiles, and --emptydirs. Use the --verily |
| 649 | ** option when you really want to clean up everything. |
| 650 | ** |
| 651 | ** Options: |
| 652 | ** --allckouts Check for empty directories within any checkouts |
| 653 | ** that may be nested within the current one. This |
| 654 | ** option should be used with great care because the |
| @@ -657,51 +658,70 @@ | |
| 657 | ** not be checked. |
| 658 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 659 | ** --dirsonly Only remove empty directories. No files will |
| 660 | ** be removed. Using this option will automatically |
| 661 | ** enable the --emptydirs option as well. |
| 662 | ** --dotfiles Include files beginning with a dot ("."). |
| 663 | ** --emptydirs Remove any empty directories that are not |
| 664 | ** explicitly exempted via the empty-dirs setting |
| 665 | ** or another applicable setting or command line |
| 666 | ** argument. Matching files, if any, are removed |
| 667 | ** prior to checking for any empty directories; |
| 668 | ** therefore, directories that contain only files |
| 669 | ** that were removed will be removed as well. |
| 670 | ** -f|--force Remove files without prompting. |
| 671 | ** -x|--verily Remove everything that is not a managed file or |
| 672 | ** the repository itself. Implies -f --emptydirs |
| 673 | ** --dotfiles. Disregard keep-glob and ignore-glob. |
| 674 | ** --clean <CSG> Never prompt for files matching this |
| 675 | ** comma separated list of glob patterns. |
| 676 | ** --ignore <CSG> Ignore files matching patterns from the |
| 677 | ** comma separated list of glob patterns. |
| 678 | ** --keep <CSG> Keep files matching this comma separated |
| 679 | ** list of glob patterns. |
| 680 | ** -n|--dry-run Delete nothing, but display what would have been |
| 681 | ** deleted. |
| 682 | ** --temp Remove only Fossil-generated temporary files. |
| 683 | ** -v|--verbose Show all files as they are removed. |
| 684 | ** |
| 685 | ** See also: addremove, extras, status |
| 686 | */ |
| 687 | void clean_cmd(void){ |
| 688 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 689 | int emptyDirsFlag, dirsOnlyFlag; |
| 690 | unsigned scanFlags = 0; |
| 691 | int verilyFlag = 0; |
| 692 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 693 | Glob *pIgnore, *pKeep, *pClean; |
| 694 | int nRoot; |
| 695 | |
| 696 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 697 | if( !dryRunFlag ){ |
| 698 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 699 | } |
| 700 | if( !dryRunFlag ){ |
| 701 | dryRunFlag = find_option("whatif",0,0)!=0; |
| 702 | } |
| 703 | allFileFlag = allDirFlag = find_option("force","f",0)!=0; |
| 704 | dirsOnlyFlag = find_option("dirsonly",0,0)!=0; |
| 705 | emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; |
| 706 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 707 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| @@ -712,10 +732,11 @@ | |
| 712 | zCleanFlag = find_option("clean",0,1); |
| 713 | db_must_be_within_tree(); |
| 714 | if( find_option("verily","x",0)!=0 ){ |
| 715 | verilyFlag = allFileFlag = allDirFlag = 1; |
| 716 | emptyDirsFlag = 1; |
| 717 | scanFlags |= SCAN_ALL; |
| 718 | zCleanFlag = 0; |
| 719 | } |
| 720 | if( zIgnoreFlag==0 && !verilyFlag ){ |
| 721 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -734,10 +755,11 @@ | |
| 734 | nRoot = (int)strlen(g.zLocalRoot); |
| 735 | g.allowSymlinks = 1; /* Find symlinks too */ |
| 736 | if( !dirsOnlyFlag ){ |
| 737 | Stmt q; |
| 738 | Blob repo; |
| 739 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 740 | db_prepare(&q, |
| 741 | "SELECT %Q || x FROM sfile" |
| 742 | " WHERE x NOT IN (%s)" |
| 743 | " ORDER BY 1", |
| @@ -754,24 +776,38 @@ | |
| 754 | fossil_print("KEPT file \"%s\" not removed (due to --keep" |
| 755 | " or \"keep-glob\")\n", zName+nRoot); |
| 756 | } |
| 757 | continue; |
| 758 | } |
| 759 | if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 760 | Blob ans; |
| 761 | char cReply; |
| 762 | char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 763 | zName+nRoot); |
| 764 | prompt_user(prompt, &ans); |
| 765 | cReply = blob_str(&ans)[0]; |
| 766 | if( cReply=='a' || cReply=='A' ){ |
| 767 | allFileFlag = 1; |
| 768 | }else if( cReply!='y' && cReply!='Y' ){ |
| 769 | blob_reset(&ans); |
| 770 | continue; |
| 771 | } |
| 772 | blob_reset(&ans); |
| 773 | } |
| 774 | if( dryRunFlag || file_delete(zName)==0 ){ |
| 775 | if( verboseFlag || dryRunFlag ){ |
| 776 | fossil_print("Removed unmanaged file: %s\n", zName+nRoot); |
| 777 | } |
| @@ -778,10 +814,11 @@ | |
| 778 | }else if( verboseFlag ){ |
| 779 | fossil_print("Could not remove file: %s\n", zName+nRoot); |
| 780 | } |
| 781 | } |
| 782 | db_finalize(&q); |
| 783 | } |
| 784 | if( emptyDirsFlag ){ |
| 785 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 786 | Stmt q; |
| 787 | Blob root; |
| @@ -803,23 +840,26 @@ | |
| 803 | " or \"keep-glob\")\n", zName+nRoot); |
| 804 | } |
| 805 | continue; |
| 806 | } |
| 807 | if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 808 | Blob ans; |
| 809 | char cReply; |
| 810 | char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", |
| 811 | zName+nRoot); |
| 812 | prompt_user(prompt, &ans); |
| 813 | cReply = blob_str(&ans)[0]; |
| 814 | if( cReply=='a' || cReply=='A' ){ |
| 815 | allDirFlag = 1; |
| 816 | }else if( cReply!='y' && cReply!='Y' ){ |
| 817 | blob_reset(&ans); |
| 818 | continue; |
| 819 | } |
| 820 | blob_reset(&ans); |
| 821 | } |
| 822 | if( dryRunFlag || file_rmdir(zName)==0 ){ |
| 823 | if( verboseFlag || dryRunFlag ){ |
| 824 | fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); |
| 825 | } |
| 826 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -644,11 +644,12 @@ | |
| 644 | ** Files and subdirectories whose names begin with "." are automatically |
| 645 | ** ignored unless the --dotfiles option is used. |
| 646 | ** |
| 647 | ** The --verily option ignores the keep-glob and ignore-glob settings |
| 648 | ** and turns on --force, --dotfiles, and --emptydirs. Use the --verily |
| 649 | ** option when you really want to clean up everything. Extreme care |
| 650 | ** should be exercised when using the --verily option. |
| 651 | ** |
| 652 | ** Options: |
| 653 | ** --allckouts Check for empty directories within any checkouts |
| 654 | ** that may be nested within the current one. This |
| 655 | ** option should be used with great care because the |
| @@ -657,51 +658,70 @@ | |
| 658 | ** not be checked. |
| 659 | ** --case-sensitive <BOOL> override case-sensitive setting |
| 660 | ** --dirsonly Only remove empty directories. No files will |
| 661 | ** be removed. Using this option will automatically |
| 662 | ** enable the --emptydirs option as well. |
| 663 | ** --disable-undo WARNING: This option disables use of the undo |
| 664 | ** mechanism for this clean operation and should be |
| 665 | ** used with extreme caution. |
| 666 | ** --dotfiles Include files beginning with a dot ("."). |
| 667 | ** --emptydirs Remove any empty directories that are not |
| 668 | ** explicitly exempted via the empty-dirs setting |
| 669 | ** or another applicable setting or command line |
| 670 | ** argument. Matching files, if any, are removed |
| 671 | ** prior to checking for any empty directories; |
| 672 | ** therefore, directories that contain only files |
| 673 | ** that were removed will be removed as well. |
| 674 | ** -f|--force Remove files without prompting. |
| 675 | ** -x|--verily WARNING: Removes everything that is not a managed |
| 676 | ** file or the repository itself. This option |
| 677 | ** implies the --force, --emptydirs, --dotfiles, and |
| 678 | ** --disable-undo options. Furthermore, it completely |
| 679 | ** disregards the keep-glob and ignore-glob settings. |
| 680 | ** However, it does honor the --ignore and --keep |
| 681 | ** options. |
| 682 | ** --clean <CSG> WARNING: Never prompt to delete any files matching |
| 683 | ** this comma separated list of glob patterns. Also, |
| 684 | ** deletions of any files matching this pattern list |
| 685 | ** cannot be undone. |
| 686 | ** --ignore <CSG> Ignore files matching patterns from the |
| 687 | ** comma separated list of glob patterns. |
| 688 | ** --keep <CSG> Keep files matching this comma separated |
| 689 | ** list of glob patterns. |
| 690 | ** -n|--dry-run Delete nothing, but display what would have been |
| 691 | ** deleted. |
| 692 | ** --no-prompt This option disables prompting the user for input |
| 693 | ** and assumes an answer of 'No' for every question. |
| 694 | ** --temp Remove only Fossil-generated temporary files. |
| 695 | ** -v|--verbose Show all files as they are removed. |
| 696 | ** |
| 697 | ** See also: addremove, extras, status |
| 698 | */ |
| 699 | void clean_cmd(void){ |
| 700 | int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; |
| 701 | int emptyDirsFlag, dirsOnlyFlag; |
| 702 | int disableUndo, noPrompt; |
| 703 | unsigned scanFlags = 0; |
| 704 | int verilyFlag = 0; |
| 705 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 706 | Glob *pIgnore, *pKeep, *pClean; |
| 707 | int nRoot; |
| 708 | |
| 709 | #ifndef UNDO_SIZE_LIMIT /* TODO: Setting? */ |
| 710 | #define UNDO_SIZE_LIMIT (10*1024*1024) /* 10MiB */ |
| 711 | #endif |
| 712 | |
| 713 | undo_capture_command_line(); |
| 714 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 715 | if( !dryRunFlag ){ |
| 716 | dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ |
| 717 | } |
| 718 | if( !dryRunFlag ){ |
| 719 | dryRunFlag = find_option("whatif",0,0)!=0; |
| 720 | } |
| 721 | disableUndo = find_option("disable-undo",0,0)!=0; |
| 722 | noPrompt = find_option("no-prompt",0,0)!=0; |
| 723 | allFileFlag = allDirFlag = find_option("force","f",0)!=0; |
| 724 | dirsOnlyFlag = find_option("dirsonly",0,0)!=0; |
| 725 | emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; |
| 726 | if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; |
| 727 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| @@ -712,10 +732,11 @@ | |
| 732 | zCleanFlag = find_option("clean",0,1); |
| 733 | db_must_be_within_tree(); |
| 734 | if( find_option("verily","x",0)!=0 ){ |
| 735 | verilyFlag = allFileFlag = allDirFlag = 1; |
| 736 | emptyDirsFlag = 1; |
| 737 | disableUndo = 1; |
| 738 | scanFlags |= SCAN_ALL; |
| 739 | zCleanFlag = 0; |
| 740 | } |
| 741 | if( zIgnoreFlag==0 && !verilyFlag ){ |
| 742 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -734,10 +755,11 @@ | |
| 755 | nRoot = (int)strlen(g.zLocalRoot); |
| 756 | g.allowSymlinks = 1; /* Find symlinks too */ |
| 757 | if( !dirsOnlyFlag ){ |
| 758 | Stmt q; |
| 759 | Blob repo; |
| 760 | if( !dryRunFlag && !disableUndo ) undo_begin(); |
| 761 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 762 | db_prepare(&q, |
| 763 | "SELECT %Q || x FROM sfile" |
| 764 | " WHERE x NOT IN (%s)" |
| 765 | " ORDER BY 1", |
| @@ -754,24 +776,38 @@ | |
| 776 | fossil_print("KEPT file \"%s\" not removed (due to --keep" |
| 777 | " or \"keep-glob\")\n", zName+nRoot); |
| 778 | } |
| 779 | continue; |
| 780 | } |
| 781 | if( !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 782 | int undoRc = UNDO_NONE; |
| 783 | if( !disableUndo ){ |
| 784 | undoRc = undo_maybe_save(zName+nRoot, UNDO_SIZE_LIMIT); |
| 785 | } |
| 786 | if( undoRc!=UNDO_SAVED_OK ){ |
| 787 | char cReply; |
| 788 | if( allFileFlag ){ |
| 789 | cReply = 'Y'; |
| 790 | }else if( !noPrompt ){ |
| 791 | Blob ans; |
| 792 | char *prompt = mprintf("\nWARNING: Deletion of this file will " |
| 793 | "not be undoable via the 'undo'\n" |
| 794 | " command because %s.\n\n" |
| 795 | "Remove unmanaged file \"%s\" (a=all/y/N)? ", |
| 796 | undo_save_message(undoRc), zName+nRoot); |
| 797 | prompt_user(prompt, &ans); |
| 798 | cReply = blob_str(&ans)[0]; |
| 799 | blob_reset(&ans); |
| 800 | }else{ |
| 801 | cReply = 'N'; |
| 802 | } |
| 803 | if( cReply=='a' || cReply=='A' ){ |
| 804 | allFileFlag = 1; |
| 805 | }else if( cReply!='y' && cReply!='Y' ){ |
| 806 | continue; |
| 807 | } |
| 808 | } |
| 809 | } |
| 810 | if( dryRunFlag || file_delete(zName)==0 ){ |
| 811 | if( verboseFlag || dryRunFlag ){ |
| 812 | fossil_print("Removed unmanaged file: %s\n", zName+nRoot); |
| 813 | } |
| @@ -778,10 +814,11 @@ | |
| 814 | }else if( verboseFlag ){ |
| 815 | fossil_print("Could not remove file: %s\n", zName+nRoot); |
| 816 | } |
| 817 | } |
| 818 | db_finalize(&q); |
| 819 | if( !dryRunFlag && !disableUndo ) undo_finish(); |
| 820 | } |
| 821 | if( emptyDirsFlag ){ |
| 822 | Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); |
| 823 | Stmt q; |
| 824 | Blob root; |
| @@ -803,23 +840,26 @@ | |
| 840 | " or \"keep-glob\")\n", zName+nRoot); |
| 841 | } |
| 842 | continue; |
| 843 | } |
| 844 | if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ |
| 845 | char cReply; |
| 846 | if( !noPrompt ){ |
| 847 | Blob ans; |
| 848 | char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", |
| 849 | zName+nRoot); |
| 850 | prompt_user(prompt, &ans); |
| 851 | cReply = blob_str(&ans)[0]; |
| 852 | blob_reset(&ans); |
| 853 | }else{ |
| 854 | cReply = 'N'; |
| 855 | } |
| 856 | if( cReply=='a' || cReply=='A' ){ |
| 857 | allDirFlag = 1; |
| 858 | }else if( cReply!='y' && cReply!='Y' ){ |
| 859 | continue; |
| 860 | } |
| 861 | } |
| 862 | if( dryRunFlag || file_rmdir(zName)==0 ){ |
| 863 | if( verboseFlag || dryRunFlag ){ |
| 864 | fossil_print("Removed unmanaged directory: %s\n", zName+nRoot); |
| 865 | } |
| 866 |
+61
| --- src/delta.c | ||
| +++ src/delta.c | ||
| @@ -589,10 +589,71 @@ | ||
| 589 | 589 | /* ERROR: generated size does not match predicted size */ |
| 590 | 590 | return -1; |
| 591 | 591 | } |
| 592 | 592 | return total; |
| 593 | 593 | } |
| 594 | + default: { | |
| 595 | + /* ERROR: unknown delta operator */ | |
| 596 | + return -1; | |
| 597 | + } | |
| 598 | + } | |
| 599 | + } | |
| 600 | + /* ERROR: unterminated delta */ | |
| 601 | + return -1; | |
| 602 | +} | |
| 603 | + | |
| 604 | +/* | |
| 605 | +** Analyze a delta. Figure out the total number of bytes copied from | |
| 606 | +** source to target, and the total number of bytes inserted by the delta, | |
| 607 | +** and return both numbers. | |
| 608 | +*/ | |
| 609 | +int delta_analyze( | |
| 610 | + const char *zDelta, /* Delta to apply to the pattern */ | |
| 611 | + int lenDelta, /* Length of the delta */ | |
| 612 | + int *pnCopy, /* OUT: Number of bytes copied */ | |
| 613 | + int *pnInsert /* OUT: Number of bytes inserted */ | |
| 614 | +){ | |
| 615 | + unsigned int nInsert = 0; | |
| 616 | + unsigned int nCopy = 0; | |
| 617 | + | |
| 618 | + (void)getInt(&zDelta, &lenDelta); | |
| 619 | + if( *zDelta!='\n' ){ | |
| 620 | + /* ERROR: size integer not terminated by "\n" */ | |
| 621 | + return -1; | |
| 622 | + } | |
| 623 | + zDelta++; lenDelta--; | |
| 624 | + while( *zDelta && lenDelta>0 ){ | |
| 625 | + unsigned int cnt, ofst; | |
| 626 | + cnt = getInt(&zDelta, &lenDelta); | |
| 627 | + switch( zDelta[0] ){ | |
| 628 | + case '@': { | |
| 629 | + zDelta++; lenDelta--; | |
| 630 | + ofst = getInt(&zDelta, &lenDelta); | |
| 631 | + if( lenDelta>0 && zDelta[0]!=',' ){ | |
| 632 | + /* ERROR: copy command not terminated by ',' */ | |
| 633 | + return -1; | |
| 634 | + } | |
| 635 | + zDelta++; lenDelta--; | |
| 636 | + nCopy += cnt; | |
| 637 | + break; | |
| 638 | + } | |
| 639 | + case ':': { | |
| 640 | + zDelta++; lenDelta--; | |
| 641 | + nInsert += cnt; | |
| 642 | + if( cnt>lenDelta ){ | |
| 643 | + /* ERROR: insert count exceeds size of delta */ | |
| 644 | + return -1; | |
| 645 | + } | |
| 646 | + zDelta += cnt; | |
| 647 | + lenDelta -= cnt; | |
| 648 | + break; | |
| 649 | + } | |
| 650 | + case ';': { | |
| 651 | + *pnCopy = nCopy; | |
| 652 | + *pnInsert = nInsert; | |
| 653 | + return 0; | |
| 654 | + } | |
| 594 | 655 | default: { |
| 595 | 656 | /* ERROR: unknown delta operator */ |
| 596 | 657 | return -1; |
| 597 | 658 | } |
| 598 | 659 | } |
| 599 | 660 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -589,10 +589,71 @@ | |
| 589 | /* ERROR: generated size does not match predicted size */ |
| 590 | return -1; |
| 591 | } |
| 592 | return total; |
| 593 | } |
| 594 | default: { |
| 595 | /* ERROR: unknown delta operator */ |
| 596 | return -1; |
| 597 | } |
| 598 | } |
| 599 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -589,10 +589,71 @@ | |
| 589 | /* ERROR: generated size does not match predicted size */ |
| 590 | return -1; |
| 591 | } |
| 592 | return total; |
| 593 | } |
| 594 | default: { |
| 595 | /* ERROR: unknown delta operator */ |
| 596 | return -1; |
| 597 | } |
| 598 | } |
| 599 | } |
| 600 | /* ERROR: unterminated delta */ |
| 601 | return -1; |
| 602 | } |
| 603 | |
| 604 | /* |
| 605 | ** Analyze a delta. Figure out the total number of bytes copied from |
| 606 | ** source to target, and the total number of bytes inserted by the delta, |
| 607 | ** and return both numbers. |
| 608 | */ |
| 609 | int delta_analyze( |
| 610 | const char *zDelta, /* Delta to apply to the pattern */ |
| 611 | int lenDelta, /* Length of the delta */ |
| 612 | int *pnCopy, /* OUT: Number of bytes copied */ |
| 613 | int *pnInsert /* OUT: Number of bytes inserted */ |
| 614 | ){ |
| 615 | unsigned int nInsert = 0; |
| 616 | unsigned int nCopy = 0; |
| 617 | |
| 618 | (void)getInt(&zDelta, &lenDelta); |
| 619 | if( *zDelta!='\n' ){ |
| 620 | /* ERROR: size integer not terminated by "\n" */ |
| 621 | return -1; |
| 622 | } |
| 623 | zDelta++; lenDelta--; |
| 624 | while( *zDelta && lenDelta>0 ){ |
| 625 | unsigned int cnt, ofst; |
| 626 | cnt = getInt(&zDelta, &lenDelta); |
| 627 | switch( zDelta[0] ){ |
| 628 | case '@': { |
| 629 | zDelta++; lenDelta--; |
| 630 | ofst = getInt(&zDelta, &lenDelta); |
| 631 | if( lenDelta>0 && zDelta[0]!=',' ){ |
| 632 | /* ERROR: copy command not terminated by ',' */ |
| 633 | return -1; |
| 634 | } |
| 635 | zDelta++; lenDelta--; |
| 636 | nCopy += cnt; |
| 637 | break; |
| 638 | } |
| 639 | case ':': { |
| 640 | zDelta++; lenDelta--; |
| 641 | nInsert += cnt; |
| 642 | if( cnt>lenDelta ){ |
| 643 | /* ERROR: insert count exceeds size of delta */ |
| 644 | return -1; |
| 645 | } |
| 646 | zDelta += cnt; |
| 647 | lenDelta -= cnt; |
| 648 | break; |
| 649 | } |
| 650 | case ';': { |
| 651 | *pnCopy = nCopy; |
| 652 | *pnInsert = nInsert; |
| 653 | return 0; |
| 654 | } |
| 655 | default: { |
| 656 | /* ERROR: unknown delta operator */ |
| 657 | return -1; |
| 658 | } |
| 659 | } |
| 660 |
+47
-4
| --- src/deltacmd.c | ||
| +++ src/deltacmd.c | ||
| @@ -43,12 +43,14 @@ | ||
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | /* |
| 46 | 46 | ** COMMAND: test-delta-create |
| 47 | 47 | ** |
| 48 | -** Given two input files, create and output a delta that carries | |
| 49 | -** the first file into the second. | |
| 48 | +** Usage: %fossil test-delta-create FILE1 FILE2 DELTA | |
| 49 | +** | |
| 50 | +** Create and output a delta that carries FILE1 into FILE2. | |
| 51 | +** Store the result in DELTA. | |
| 50 | 52 | */ |
| 51 | 53 | void delta_create_cmd(void){ |
| 52 | 54 | Blob orig, target, delta; |
| 53 | 55 | if( g.argc!=5 ){ |
| 54 | 56 | usage("ORIGIN TARGET DELTA"); |
| @@ -65,10 +67,47 @@ | ||
| 65 | 67 | } |
| 66 | 68 | blob_reset(&orig); |
| 67 | 69 | blob_reset(&target); |
| 68 | 70 | blob_reset(&delta); |
| 69 | 71 | } |
| 72 | + | |
| 73 | +/* | |
| 74 | +** COMMAND: test-delta-analyze | |
| 75 | +** | |
| 76 | +** Usage: %fossil test-delta-analyze FILE1 FILE2 | |
| 77 | +** | |
| 78 | +** Create and a delta that carries FILE1 into FILE2. Print the | |
| 79 | +** number bytes copied and the number of bytes inserted. | |
| 80 | +*/ | |
| 81 | +void delta_analyze_cmd(void){ | |
| 82 | + Blob orig, target, delta; | |
| 83 | + int nCopy = 0; | |
| 84 | + int nInsert = 0; | |
| 85 | + int sz1, sz2; | |
| 86 | + if( g.argc!=4 ){ | |
| 87 | + usage("ORIGIN TARGET"); | |
| 88 | + } | |
| 89 | + if( blob_read_from_file(&orig, g.argv[2])<0 ){ | |
| 90 | + fossil_fatal("cannot read %s\n", g.argv[2]); | |
| 91 | + } | |
| 92 | + if( blob_read_from_file(&target, g.argv[3])<0 ){ | |
| 93 | + fossil_fatal("cannot read %s\n", g.argv[3]); | |
| 94 | + } | |
| 95 | + blob_delta_create(&orig, &target, &delta); | |
| 96 | + delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert); | |
| 97 | + sz1 = blob_size(&orig); | |
| 98 | + sz2 = blob_size(&target); | |
| 99 | + blob_reset(&orig); | |
| 100 | + blob_reset(&target); | |
| 101 | + blob_reset(&delta); | |
| 102 | + fossil_print("original size: %8d\n", sz1); | |
| 103 | + fossil_print("bytes copied: %8d (%.1f%% of target)\n", | |
| 104 | + nCopy, (100.0*nCopy)/sz2); | |
| 105 | + fossil_print("bytes inserted: %8d (%.1f%% of target)\n", | |
| 106 | + nInsert, (100.0*nInsert)/sz2); | |
| 107 | + fossil_print("final size: %8d\n", sz2); | |
| 108 | +} | |
| 70 | 109 | |
| 71 | 110 | /* |
| 72 | 111 | ** Apply the delta in pDelta to the original file pOriginal to generate |
| 73 | 112 | ** the target file pTarget. The pTarget blob is initialized by this |
| 74 | 113 | ** routine. |
| @@ -102,12 +141,13 @@ | ||
| 102 | 141 | } |
| 103 | 142 | |
| 104 | 143 | /* |
| 105 | 144 | ** COMMAND: test-delta-apply |
| 106 | 145 | ** |
| 107 | -** Given an input files and a delta, apply the delta to the input file | |
| 108 | -** and write the result. | |
| 146 | +** Usage: %fossil test-delta-apply FILE1 DELTA | |
| 147 | +** | |
| 148 | +** Apply DELTA to FILE1 and output the result. | |
| 109 | 149 | */ |
| 110 | 150 | void delta_apply_cmd(void){ |
| 111 | 151 | Blob orig, target, delta; |
| 112 | 152 | if( g.argc!=5 ){ |
| 113 | 153 | usage("ORIGIN DELTA TARGET"); |
| @@ -124,13 +164,16 @@ | ||
| 124 | 164 | } |
| 125 | 165 | blob_reset(&orig); |
| 126 | 166 | blob_reset(&target); |
| 127 | 167 | blob_reset(&delta); |
| 128 | 168 | } |
| 169 | + | |
| 129 | 170 | |
| 130 | 171 | /* |
| 131 | 172 | ** COMMAND: test-delta |
| 173 | +** | |
| 174 | +** Usage: %fossil test-delta FILE1 FILE2 | |
| 132 | 175 | ** |
| 133 | 176 | ** Read two files named on the command-line. Create and apply deltas |
| 134 | 177 | ** going in both directions. Verify that the original files are |
| 135 | 178 | ** correctly recovered. |
| 136 | 179 | */ |
| 137 | 180 |
| --- src/deltacmd.c | |
| +++ src/deltacmd.c | |
| @@ -43,12 +43,14 @@ | |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | ** COMMAND: test-delta-create |
| 47 | ** |
| 48 | ** Given two input files, create and output a delta that carries |
| 49 | ** the first file into the second. |
| 50 | */ |
| 51 | void delta_create_cmd(void){ |
| 52 | Blob orig, target, delta; |
| 53 | if( g.argc!=5 ){ |
| 54 | usage("ORIGIN TARGET DELTA"); |
| @@ -65,10 +67,47 @@ | |
| 65 | } |
| 66 | blob_reset(&orig); |
| 67 | blob_reset(&target); |
| 68 | blob_reset(&delta); |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | ** Apply the delta in pDelta to the original file pOriginal to generate |
| 73 | ** the target file pTarget. The pTarget blob is initialized by this |
| 74 | ** routine. |
| @@ -102,12 +141,13 @@ | |
| 102 | } |
| 103 | |
| 104 | /* |
| 105 | ** COMMAND: test-delta-apply |
| 106 | ** |
| 107 | ** Given an input files and a delta, apply the delta to the input file |
| 108 | ** and write the result. |
| 109 | */ |
| 110 | void delta_apply_cmd(void){ |
| 111 | Blob orig, target, delta; |
| 112 | if( g.argc!=5 ){ |
| 113 | usage("ORIGIN DELTA TARGET"); |
| @@ -124,13 +164,16 @@ | |
| 124 | } |
| 125 | blob_reset(&orig); |
| 126 | blob_reset(&target); |
| 127 | blob_reset(&delta); |
| 128 | } |
| 129 | |
| 130 | /* |
| 131 | ** COMMAND: test-delta |
| 132 | ** |
| 133 | ** Read two files named on the command-line. Create and apply deltas |
| 134 | ** going in both directions. Verify that the original files are |
| 135 | ** correctly recovered. |
| 136 | */ |
| 137 |
| --- src/deltacmd.c | |
| +++ src/deltacmd.c | |
| @@ -43,12 +43,14 @@ | |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | ** COMMAND: test-delta-create |
| 47 | ** |
| 48 | ** Usage: %fossil test-delta-create FILE1 FILE2 DELTA |
| 49 | ** |
| 50 | ** Create and output a delta that carries FILE1 into FILE2. |
| 51 | ** Store the result in DELTA. |
| 52 | */ |
| 53 | void delta_create_cmd(void){ |
| 54 | Blob orig, target, delta; |
| 55 | if( g.argc!=5 ){ |
| 56 | usage("ORIGIN TARGET DELTA"); |
| @@ -65,10 +67,47 @@ | |
| 67 | } |
| 68 | blob_reset(&orig); |
| 69 | blob_reset(&target); |
| 70 | blob_reset(&delta); |
| 71 | } |
| 72 | |
| 73 | /* |
| 74 | ** COMMAND: test-delta-analyze |
| 75 | ** |
| 76 | ** Usage: %fossil test-delta-analyze FILE1 FILE2 |
| 77 | ** |
| 78 | ** Create and a delta that carries FILE1 into FILE2. Print the |
| 79 | ** number bytes copied and the number of bytes inserted. |
| 80 | */ |
| 81 | void delta_analyze_cmd(void){ |
| 82 | Blob orig, target, delta; |
| 83 | int nCopy = 0; |
| 84 | int nInsert = 0; |
| 85 | int sz1, sz2; |
| 86 | if( g.argc!=4 ){ |
| 87 | usage("ORIGIN TARGET"); |
| 88 | } |
| 89 | if( blob_read_from_file(&orig, g.argv[2])<0 ){ |
| 90 | fossil_fatal("cannot read %s\n", g.argv[2]); |
| 91 | } |
| 92 | if( blob_read_from_file(&target, g.argv[3])<0 ){ |
| 93 | fossil_fatal("cannot read %s\n", g.argv[3]); |
| 94 | } |
| 95 | blob_delta_create(&orig, &target, &delta); |
| 96 | delta_analyze(blob_buffer(&delta), blob_size(&delta), &nCopy, &nInsert); |
| 97 | sz1 = blob_size(&orig); |
| 98 | sz2 = blob_size(&target); |
| 99 | blob_reset(&orig); |
| 100 | blob_reset(&target); |
| 101 | blob_reset(&delta); |
| 102 | fossil_print("original size: %8d\n", sz1); |
| 103 | fossil_print("bytes copied: %8d (%.1f%% of target)\n", |
| 104 | nCopy, (100.0*nCopy)/sz2); |
| 105 | fossil_print("bytes inserted: %8d (%.1f%% of target)\n", |
| 106 | nInsert, (100.0*nInsert)/sz2); |
| 107 | fossil_print("final size: %8d\n", sz2); |
| 108 | } |
| 109 | |
| 110 | /* |
| 111 | ** Apply the delta in pDelta to the original file pOriginal to generate |
| 112 | ** the target file pTarget. The pTarget blob is initialized by this |
| 113 | ** routine. |
| @@ -102,12 +141,13 @@ | |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** COMMAND: test-delta-apply |
| 145 | ** |
| 146 | ** Usage: %fossil test-delta-apply FILE1 DELTA |
| 147 | ** |
| 148 | ** Apply DELTA to FILE1 and output the result. |
| 149 | */ |
| 150 | void delta_apply_cmd(void){ |
| 151 | Blob orig, target, delta; |
| 152 | if( g.argc!=5 ){ |
| 153 | usage("ORIGIN DELTA TARGET"); |
| @@ -124,13 +164,16 @@ | |
| 164 | } |
| 165 | blob_reset(&orig); |
| 166 | blob_reset(&target); |
| 167 | blob_reset(&delta); |
| 168 | } |
| 169 | |
| 170 | |
| 171 | /* |
| 172 | ** COMMAND: test-delta |
| 173 | ** |
| 174 | ** Usage: %fossil test-delta FILE1 FILE2 |
| 175 | ** |
| 176 | ** Read two files named on the command-line. Create and apply deltas |
| 177 | ** going in both directions. Verify that the original files are |
| 178 | ** correctly recovered. |
| 179 | */ |
| 180 |
+4
-3
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -523,11 +523,11 @@ | ||
| 523 | 523 | ** it gets checked in. |
| 524 | 524 | ** |
| 525 | 525 | ** The file extension is used to decide how to render the file. |
| 526 | 526 | ** |
| 527 | 527 | ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", |
| 528 | -** and "FILE/index.md" are in that order. If none of those are found, | |
| 528 | +** and "FILE/index.md" are tried in that order. If none of those are found, | |
| 529 | 529 | ** then FILE is completely replaced by "404.md" and tried. If that is |
| 530 | 530 | ** not found, then a default 404 screen is generated. |
| 531 | 531 | */ |
| 532 | 532 | void doc_page(void){ |
| 533 | 533 | const char *zName; /* Argument to the /doc page */ |
| @@ -547,14 +547,15 @@ | ||
| 547 | 547 | login_check_credentials(); |
| 548 | 548 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 549 | 549 | blob_init(&title, 0, 0); |
| 550 | 550 | db_begin_transaction(); |
| 551 | 551 | while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ |
| 552 | - zName = PD("name", "tip/index.wiki"); | |
| 552 | + zName = P("name"); | |
| 553 | + if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki"; | |
| 553 | 554 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 554 | 555 | zCheckin = mprintf("%.*s", i, zName); |
| 555 | - if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ | |
| 556 | + if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){ | |
| 556 | 557 | zCheckin = "tip"; |
| 557 | 558 | } |
| 558 | 559 | if( nMiss==ArraySize(azSuffix) ){ |
| 559 | 560 | zName = "404.md"; |
| 560 | 561 | }else if( zName[i]==0 ){ |
| 561 | 562 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | ** it gets checked in. |
| 524 | ** |
| 525 | ** The file extension is used to decide how to render the file. |
| 526 | ** |
| 527 | ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", |
| 528 | ** and "FILE/index.md" are in that order. If none of those are found, |
| 529 | ** then FILE is completely replaced by "404.md" and tried. If that is |
| 530 | ** not found, then a default 404 screen is generated. |
| 531 | */ |
| 532 | void doc_page(void){ |
| 533 | const char *zName; /* Argument to the /doc page */ |
| @@ -547,14 +547,15 @@ | |
| 547 | login_check_credentials(); |
| 548 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 549 | blob_init(&title, 0, 0); |
| 550 | db_begin_transaction(); |
| 551 | while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ |
| 552 | zName = PD("name", "tip/index.wiki"); |
| 553 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 554 | zCheckin = mprintf("%.*s", i, zName); |
| 555 | if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ |
| 556 | zCheckin = "tip"; |
| 557 | } |
| 558 | if( nMiss==ArraySize(azSuffix) ){ |
| 559 | zName = "404.md"; |
| 560 | }else if( zName[i]==0 ){ |
| 561 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | ** it gets checked in. |
| 524 | ** |
| 525 | ** The file extension is used to decide how to render the file. |
| 526 | ** |
| 527 | ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", |
| 528 | ** and "FILE/index.md" are tried in that order. If none of those are found, |
| 529 | ** then FILE is completely replaced by "404.md" and tried. If that is |
| 530 | ** not found, then a default 404 screen is generated. |
| 531 | */ |
| 532 | void doc_page(void){ |
| 533 | const char *zName; /* Argument to the /doc page */ |
| @@ -547,14 +547,15 @@ | |
| 547 | login_check_credentials(); |
| 548 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 549 | blob_init(&title, 0, 0); |
| 550 | db_begin_transaction(); |
| 551 | while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ |
| 552 | zName = P("name"); |
| 553 | if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki"; |
| 554 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 555 | zCheckin = mprintf("%.*s", i, zName); |
| 556 | if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){ |
| 557 | zCheckin = "tip"; |
| 558 | } |
| 559 | if( nMiss==ArraySize(azSuffix) ){ |
| 560 | zName = "404.md"; |
| 561 | }else if( zName[i]==0 ){ |
| 562 |
+5
-4
| --- src/foci.c | ||
| +++ src/foci.c | ||
| @@ -167,17 +167,18 @@ | ||
| 167 | 167 | ){ |
| 168 | 168 | FociCursor *pCur = (FociCursor *)pCursor; |
| 169 | 169 | manifest_destroy(pCur->pMan); |
| 170 | 170 | if( idxNum ){ |
| 171 | 171 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 172 | - pCur->iFile = 0; | |
| 173 | - manifest_file_rewind(pCur->pMan); | |
| 174 | - pCur->pFile = manifest_file_next(pCur->pMan, 0); | |
| 172 | + if( pCur->pMan ){ | |
| 173 | + manifest_file_rewind(pCur->pMan); | |
| 174 | + pCur->pFile = manifest_file_next(pCur->pMan, 0); | |
| 175 | + } | |
| 175 | 176 | }else{ |
| 176 | 177 | pCur->pMan = 0; |
| 177 | - pCur->iFile = 0; | |
| 178 | 178 | } |
| 179 | + pCur->iFile = 0; | |
| 179 | 180 | return SQLITE_OK; |
| 180 | 181 | } |
| 181 | 182 | |
| 182 | 183 | static int fociColumn( |
| 183 | 184 | sqlite3_vtab_cursor *pCursor, |
| 184 | 185 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -167,17 +167,18 @@ | |
| 167 | ){ |
| 168 | FociCursor *pCur = (FociCursor *)pCursor; |
| 169 | manifest_destroy(pCur->pMan); |
| 170 | if( idxNum ){ |
| 171 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 172 | pCur->iFile = 0; |
| 173 | manifest_file_rewind(pCur->pMan); |
| 174 | pCur->pFile = manifest_file_next(pCur->pMan, 0); |
| 175 | }else{ |
| 176 | pCur->pMan = 0; |
| 177 | pCur->iFile = 0; |
| 178 | } |
| 179 | return SQLITE_OK; |
| 180 | } |
| 181 | |
| 182 | static int fociColumn( |
| 183 | sqlite3_vtab_cursor *pCursor, |
| 184 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -167,17 +167,18 @@ | |
| 167 | ){ |
| 168 | FociCursor *pCur = (FociCursor *)pCursor; |
| 169 | manifest_destroy(pCur->pMan); |
| 170 | if( idxNum ){ |
| 171 | pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); |
| 172 | if( pCur->pMan ){ |
| 173 | manifest_file_rewind(pCur->pMan); |
| 174 | pCur->pFile = manifest_file_next(pCur->pMan, 0); |
| 175 | } |
| 176 | }else{ |
| 177 | pCur->pMan = 0; |
| 178 | } |
| 179 | pCur->iFile = 0; |
| 180 | return SQLITE_OK; |
| 181 | } |
| 182 | |
| 183 | static int fociColumn( |
| 184 | sqlite3_vtab_cursor *pCursor, |
| 185 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1886,11 +1886,11 @@ | ||
| 1886 | 1886 | } |
| 1887 | 1887 | if( descOnly ){ |
| 1888 | 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | 1889 | }else{ |
| 1890 | 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | - "%R/info/%s%s",zUuid, | |
| 1891 | + "%R/artifact/%s%s",zUuid, | |
| 1892 | 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | 1893 | @ <hr /> |
| 1894 | 1894 | content_get(rid, &content); |
| 1895 | 1895 | if( renderAsWiki ){ |
| 1896 | 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 | 1897 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1886,11 +1886,11 @@ | |
| 1886 | } |
| 1887 | if( descOnly ){ |
| 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | }else{ |
| 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | "%R/info/%s%s",zUuid, |
| 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | @ <hr /> |
| 1894 | content_get(rid, &content); |
| 1895 | if( renderAsWiki ){ |
| 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1886,11 +1886,11 @@ | |
| 1886 | } |
| 1887 | if( descOnly ){ |
| 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | }else{ |
| 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | "%R/artifact/%s%s",zUuid, |
| 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | @ <hr /> |
| 1894 | content_get(rid, &content); |
| 1895 | if( renderAsWiki ){ |
| 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1886,11 +1886,11 @@ | ||
| 1886 | 1886 | } |
| 1887 | 1887 | if( descOnly ){ |
| 1888 | 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | 1889 | }else{ |
| 1890 | 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | - "%R/info/%s%s",zUuid, | |
| 1891 | + "%R/artifact/%s%s",zUuid, | |
| 1892 | 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | 1893 | @ <hr /> |
| 1894 | 1894 | content_get(rid, &content); |
| 1895 | 1895 | if( renderAsWiki ){ |
| 1896 | 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 | 1897 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1886,11 +1886,11 @@ | |
| 1886 | } |
| 1887 | if( descOnly ){ |
| 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | }else{ |
| 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | "%R/info/%s%s",zUuid, |
| 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | @ <hr /> |
| 1894 | content_get(rid, &content); |
| 1895 | if( renderAsWiki ){ |
| 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1886,11 +1886,11 @@ | |
| 1886 | } |
| 1887 | if( descOnly ){ |
| 1888 | style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); |
| 1889 | }else{ |
| 1890 | style_submenu_element("Line Numbers", "Line Numbers", |
| 1891 | "%R/artifact/%s%s",zUuid, |
| 1892 | ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); |
| 1893 | @ <hr /> |
| 1894 | content_get(rid, &content); |
| 1895 | if( renderAsWiki ){ |
| 1896 | wiki_render_by_mimetype(&content, zMime); |
| 1897 |
+3
-3
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -597,11 +597,11 @@ | ||
| 597 | 597 | #### The directories where the OpenSSL include and library files are located. |
| 598 | 598 | # The recommended usage here is to use the Sysinternals junction tool |
| 599 | 599 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 600 | 600 | # Fossil source code directory and the target OpenSSL source directory. |
| 601 | 601 | # |
| 602 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c | |
| 602 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d | |
| 603 | 603 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 604 | 604 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 605 | 605 | |
| 606 | 606 | #### Either the directory where the Tcl library is installed or the Tcl |
| 607 | 607 | # source code directory resides (depending on the value of the macro |
| @@ -1324,11 +1324,11 @@ | ||
| 1324 | 1324 | FOSSIL_BUILD_SSL = 0 |
| 1325 | 1325 | !endif |
| 1326 | 1326 | |
| 1327 | 1327 | # Build the included zlib library? |
| 1328 | 1328 | !ifndef FOSSIL_BUILD_ZLIB |
| 1329 | -FOSSIL_BUILD_ZLIB = 0 | |
| 1329 | +FOSSIL_BUILD_ZLIB = 1 | |
| 1330 | 1330 | !endif |
| 1331 | 1331 | |
| 1332 | 1332 | # Link everything except SQLite dynamically? |
| 1333 | 1333 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 1334 | 1334 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -1373,11 +1373,11 @@ | ||
| 1373 | 1373 | !ifndef FOSSIL_ENABLE_WINXP |
| 1374 | 1374 | FOSSIL_ENABLE_WINXP = 0 |
| 1375 | 1375 | !endif |
| 1376 | 1376 | |
| 1377 | 1377 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1378 | -SSLDIR = $(B)\compat\openssl-1.0.2c | |
| 1378 | +SSLDIR = $(B)\compat\openssl-1.0.2d | |
| 1379 | 1379 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1380 | 1380 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1381 | 1381 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 1382 | 1382 | !else |
| 1383 | 1383 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1384 | 1384 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -597,11 +597,11 @@ | |
| 597 | #### The directories where the OpenSSL include and library files are located. |
| 598 | # The recommended usage here is to use the Sysinternals junction tool |
| 599 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 600 | # Fossil source code directory and the target OpenSSL source directory. |
| 601 | # |
| 602 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c |
| 603 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 604 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 605 | |
| 606 | #### Either the directory where the Tcl library is installed or the Tcl |
| 607 | # source code directory resides (depending on the value of the macro |
| @@ -1324,11 +1324,11 @@ | |
| 1324 | FOSSIL_BUILD_SSL = 0 |
| 1325 | !endif |
| 1326 | |
| 1327 | # Build the included zlib library? |
| 1328 | !ifndef FOSSIL_BUILD_ZLIB |
| 1329 | FOSSIL_BUILD_ZLIB = 0 |
| 1330 | !endif |
| 1331 | |
| 1332 | # Link everything except SQLite dynamically? |
| 1333 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 1334 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -1373,11 +1373,11 @@ | |
| 1373 | !ifndef FOSSIL_ENABLE_WINXP |
| 1374 | FOSSIL_ENABLE_WINXP = 0 |
| 1375 | !endif |
| 1376 | |
| 1377 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1378 | SSLDIR = $(B)\compat\openssl-1.0.2c |
| 1379 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1380 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1381 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 1382 | !else |
| 1383 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1384 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -597,11 +597,11 @@ | |
| 597 | #### The directories where the OpenSSL include and library files are located. |
| 598 | # The recommended usage here is to use the Sysinternals junction tool |
| 599 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 600 | # Fossil source code directory and the target OpenSSL source directory. |
| 601 | # |
| 602 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d |
| 603 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 604 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 605 | |
| 606 | #### Either the directory where the Tcl library is installed or the Tcl |
| 607 | # source code directory resides (depending on the value of the macro |
| @@ -1324,11 +1324,11 @@ | |
| 1324 | FOSSIL_BUILD_SSL = 0 |
| 1325 | !endif |
| 1326 | |
| 1327 | # Build the included zlib library? |
| 1328 | !ifndef FOSSIL_BUILD_ZLIB |
| 1329 | FOSSIL_BUILD_ZLIB = 1 |
| 1330 | !endif |
| 1331 | |
| 1332 | # Link everything except SQLite dynamically? |
| 1333 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 1334 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -1373,11 +1373,11 @@ | |
| 1373 | !ifndef FOSSIL_ENABLE_WINXP |
| 1374 | FOSSIL_ENABLE_WINXP = 0 |
| 1375 | !endif |
| 1376 | |
| 1377 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 1378 | SSLDIR = $(B)\compat\openssl-1.0.2d |
| 1379 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1380 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 1381 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 1382 | !else |
| 1383 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1384 |
+5
-5
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -592,11 +592,11 @@ | ||
| 592 | 592 | } |
| 593 | 593 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 594 | 594 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 595 | 595 | nConflict++; |
| 596 | 596 | }else{ |
| 597 | - undo_save(zName); | |
| 597 | + if( !dryRunFlag ) undo_save(zName); | |
| 598 | 598 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 599 | 599 | content_get(ridp, &p); |
| 600 | 600 | content_get(ridm, &m); |
| 601 | 601 | if( isBinary ){ |
| 602 | 602 | rc = -1; |
| @@ -643,11 +643,11 @@ | ||
| 643 | 643 | fossil_print("DELETE %s\n", zName); |
| 644 | 644 | if( chnged ){ |
| 645 | 645 | fossil_warning("WARNING: local edits lost for %s\n", zName); |
| 646 | 646 | nConflict++; |
| 647 | 647 | } |
| 648 | - undo_save(zName); | |
| 648 | + if( !dryRunFlag ) undo_save(zName); | |
| 649 | 649 | db_multi_exec( |
| 650 | 650 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 651 | 651 | ); |
| 652 | 652 | if( !dryRunFlag ){ |
| 653 | 653 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -669,12 +669,12 @@ | ||
| 669 | 669 | while( db_step(&q)==SQLITE_ROW ){ |
| 670 | 670 | int idv = db_column_int(&q, 0); |
| 671 | 671 | const char *zOldName = db_column_text(&q, 1); |
| 672 | 672 | const char *zNewName = db_column_text(&q, 2); |
| 673 | 673 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 674 | - undo_save(zOldName); | |
| 675 | - undo_save(zNewName); | |
| 674 | + if( !dryRunFlag ) undo_save(zOldName); | |
| 675 | + if( !dryRunFlag ) undo_save(zNewName); | |
| 676 | 676 | db_multi_exec( |
| 677 | 677 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 678 | 678 | " WHERE id=%d AND vid=%d", zNewName, idv, vid |
| 679 | 679 | ); |
| 680 | 680 | if( !dryRunFlag ){ |
| @@ -726,8 +726,8 @@ | ||
| 726 | 726 | }else if( integrateFlag ){ |
| 727 | 727 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid); |
| 728 | 728 | }else{ |
| 729 | 729 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid); |
| 730 | 730 | } |
| 731 | - undo_finish(); | |
| 731 | + if( !dryRunFlag ) undo_finish(); | |
| 732 | 732 | db_end_transaction(dryRunFlag); |
| 733 | 733 | } |
| 734 | 734 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -592,11 +592,11 @@ | |
| 592 | } |
| 593 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 594 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 595 | nConflict++; |
| 596 | }else{ |
| 597 | undo_save(zName); |
| 598 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 599 | content_get(ridp, &p); |
| 600 | content_get(ridm, &m); |
| 601 | if( isBinary ){ |
| 602 | rc = -1; |
| @@ -643,11 +643,11 @@ | |
| 643 | fossil_print("DELETE %s\n", zName); |
| 644 | if( chnged ){ |
| 645 | fossil_warning("WARNING: local edits lost for %s\n", zName); |
| 646 | nConflict++; |
| 647 | } |
| 648 | undo_save(zName); |
| 649 | db_multi_exec( |
| 650 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 651 | ); |
| 652 | if( !dryRunFlag ){ |
| 653 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -669,12 +669,12 @@ | |
| 669 | while( db_step(&q)==SQLITE_ROW ){ |
| 670 | int idv = db_column_int(&q, 0); |
| 671 | const char *zOldName = db_column_text(&q, 1); |
| 672 | const char *zNewName = db_column_text(&q, 2); |
| 673 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 674 | undo_save(zOldName); |
| 675 | undo_save(zNewName); |
| 676 | db_multi_exec( |
| 677 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 678 | " WHERE id=%d AND vid=%d", zNewName, idv, vid |
| 679 | ); |
| 680 | if( !dryRunFlag ){ |
| @@ -726,8 +726,8 @@ | |
| 726 | }else if( integrateFlag ){ |
| 727 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid); |
| 728 | }else{ |
| 729 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid); |
| 730 | } |
| 731 | undo_finish(); |
| 732 | db_end_transaction(dryRunFlag); |
| 733 | } |
| 734 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -592,11 +592,11 @@ | |
| 592 | } |
| 593 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 594 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 595 | nConflict++; |
| 596 | }else{ |
| 597 | if( !dryRunFlag ) undo_save(zName); |
| 598 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 599 | content_get(ridp, &p); |
| 600 | content_get(ridm, &m); |
| 601 | if( isBinary ){ |
| 602 | rc = -1; |
| @@ -643,11 +643,11 @@ | |
| 643 | fossil_print("DELETE %s\n", zName); |
| 644 | if( chnged ){ |
| 645 | fossil_warning("WARNING: local edits lost for %s\n", zName); |
| 646 | nConflict++; |
| 647 | } |
| 648 | if( !dryRunFlag ) undo_save(zName); |
| 649 | db_multi_exec( |
| 650 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 651 | ); |
| 652 | if( !dryRunFlag ){ |
| 653 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -669,12 +669,12 @@ | |
| 669 | while( db_step(&q)==SQLITE_ROW ){ |
| 670 | int idv = db_column_int(&q, 0); |
| 671 | const char *zOldName = db_column_text(&q, 1); |
| 672 | const char *zNewName = db_column_text(&q, 2); |
| 673 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 674 | if( !dryRunFlag ) undo_save(zOldName); |
| 675 | if( !dryRunFlag ) undo_save(zNewName); |
| 676 | db_multi_exec( |
| 677 | "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)" |
| 678 | " WHERE id=%d AND vid=%d", zNewName, idv, vid |
| 679 | ); |
| 680 | if( !dryRunFlag ){ |
| @@ -726,8 +726,8 @@ | |
| 726 | }else if( integrateFlag ){ |
| 727 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid); |
| 728 | }else{ |
| 729 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid); |
| 730 | } |
| 731 | if( !dryRunFlag ) undo_finish(); |
| 732 | db_end_transaction(dryRunFlag); |
| 733 | } |
| 734 |
+233
-92
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -325,11 +325,11 @@ | ||
| 325 | 325 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 326 | 326 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 327 | 327 | */ |
| 328 | 328 | #define SQLITE_VERSION "3.8.11" |
| 329 | 329 | #define SQLITE_VERSION_NUMBER 3008011 |
| 330 | -#define SQLITE_SOURCE_ID "2015-07-03 17:54:49 030f60a7ba171650ce8c0ac32dc166eab80aca32" | |
| 330 | +#define SQLITE_SOURCE_ID "2015-07-08 16:22:42 5348ffc3fda5168c1e9e14aa88b0c6aedbda7c94" | |
| 331 | 331 | |
| 332 | 332 | /* |
| 333 | 333 | ** CAPI3REF: Run-Time Library Version Numbers |
| 334 | 334 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 335 | 335 | ** |
| @@ -6503,10 +6503,13 @@ | ||
| 6503 | 6503 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6504 | 6504 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6505 | 6505 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6506 | 6506 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6507 | 6507 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6508 | +#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ | |
| 6509 | +#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ | |
| 6510 | +#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ | |
| 6508 | 6511 | |
| 6509 | 6512 | /* |
| 6510 | 6513 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6511 | 6514 | ** METHOD: sqlite3 |
| 6512 | 6515 | ** |
| @@ -8941,10 +8944,20 @@ | ||
| 8941 | 8944 | #if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS |
| 8942 | 8945 | # undef SQLITE_MAX_WORKER_THREADS |
| 8943 | 8946 | # define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS |
| 8944 | 8947 | #endif |
| 8945 | 8948 | |
| 8949 | +/* | |
| 8950 | +** The default initial allocation for the pagecache when using separate | |
| 8951 | +** pagecaches for each database connection. A positive number is the | |
| 8952 | +** number of pages. A negative number N translations means that a buffer | |
| 8953 | +** of -1024*N bytes is allocated and used for as many pages as it will hold. | |
| 8954 | +*/ | |
| 8955 | +#ifndef SQLITE_DEFAULT_PCACHE_INITSZ | |
| 8956 | +# define SQLITE_DEFAULT_PCACHE_INITSZ 100 | |
| 8957 | +#endif | |
| 8958 | + | |
| 8946 | 8959 | |
| 8947 | 8960 | /* |
| 8948 | 8961 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 8949 | 8962 | ** ourselves. |
| 8950 | 8963 | */ |
| @@ -14043,11 +14056,11 @@ | ||
| 14043 | 14056 | (void*)0, /* pScratch */ |
| 14044 | 14057 | 0, /* szScratch */ |
| 14045 | 14058 | 0, /* nScratch */ |
| 14046 | 14059 | (void*)0, /* pPage */ |
| 14047 | 14060 | 0, /* szPage */ |
| 14048 | - 0, /* nPage */ | |
| 14061 | + SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ | |
| 14049 | 14062 | 0, /* mxParserStack */ |
| 14050 | 14063 | 0, /* sharedCacheEnabled */ |
| 14051 | 14064 | SQLITE_SORTER_PMASZ, /* szPma */ |
| 14052 | 14065 | /* All the rest should always be initialized to zero */ |
| 14053 | 14066 | 0, /* isInit */ |
| @@ -19454,11 +19467,11 @@ | ||
| 19454 | 19467 | ** The sqlite3_mutex_alloc() routine allocates a new |
| 19455 | 19468 | ** mutex and returns a pointer to it. If it returns NULL |
| 19456 | 19469 | ** that means that a mutex could not be allocated. |
| 19457 | 19470 | */ |
| 19458 | 19471 | static sqlite3_mutex *debugMutexAlloc(int id){ |
| 19459 | - static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1]; | |
| 19472 | + static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1]; | |
| 19460 | 19473 | sqlite3_debug_mutex *pNew = 0; |
| 19461 | 19474 | switch( id ){ |
| 19462 | 19475 | case SQLITE_MUTEX_FAST: |
| 19463 | 19476 | case SQLITE_MUTEX_RECURSIVE: { |
| 19464 | 19477 | pNew = sqlite3Malloc(sizeof(*pNew)); |
| @@ -19669,10 +19682,13 @@ | ||
| 19669 | 19682 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 19670 | 19683 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 19671 | 19684 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 19672 | 19685 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 19673 | 19686 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 19687 | +** <li> SQLITE_MUTEX_STATIC_VFS1 | |
| 19688 | +** <li> SQLITE_MUTEX_STATIC_VFS2 | |
| 19689 | +** <li> SQLITE_MUTEX_STATIC_VFS3 | |
| 19674 | 19690 | ** </ul> |
| 19675 | 19691 | ** |
| 19676 | 19692 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 19677 | 19693 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 19678 | 19694 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -19697,10 +19713,13 @@ | ||
| 19697 | 19713 | ** mutex types, the same mutex is returned on every call that has |
| 19698 | 19714 | ** the same type number. |
| 19699 | 19715 | */ |
| 19700 | 19716 | static sqlite3_mutex *pthreadMutexAlloc(int iType){ |
| 19701 | 19717 | static sqlite3_mutex staticMutexes[] = { |
| 19718 | + SQLITE3_MUTEX_INITIALIZER, | |
| 19719 | + SQLITE3_MUTEX_INITIALIZER, | |
| 19720 | + SQLITE3_MUTEX_INITIALIZER, | |
| 19702 | 19721 | SQLITE3_MUTEX_INITIALIZER, |
| 19703 | 19722 | SQLITE3_MUTEX_INITIALIZER, |
| 19704 | 19723 | SQLITE3_MUTEX_INITIALIZER, |
| 19705 | 19724 | SQLITE3_MUTEX_INITIALIZER, |
| 19706 | 19725 | SQLITE3_MUTEX_INITIALIZER, |
| @@ -20311,10 +20330,13 @@ | ||
| 20311 | 20330 | SQLITE3_MUTEX_INITIALIZER, |
| 20312 | 20331 | SQLITE3_MUTEX_INITIALIZER, |
| 20313 | 20332 | SQLITE3_MUTEX_INITIALIZER, |
| 20314 | 20333 | SQLITE3_MUTEX_INITIALIZER, |
| 20315 | 20334 | SQLITE3_MUTEX_INITIALIZER, |
| 20335 | + SQLITE3_MUTEX_INITIALIZER, | |
| 20336 | + SQLITE3_MUTEX_INITIALIZER, | |
| 20337 | + SQLITE3_MUTEX_INITIALIZER, | |
| 20316 | 20338 | SQLITE3_MUTEX_INITIALIZER |
| 20317 | 20339 | }; |
| 20318 | 20340 | |
| 20319 | 20341 | static int winMutex_isInit = 0; |
| 20320 | 20342 | static int winMutex_isNt = -1; /* <0 means "need to query" */ |
| @@ -20382,10 +20404,13 @@ | ||
| 20382 | 20404 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 20383 | 20405 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 20384 | 20406 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 20385 | 20407 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 20386 | 20408 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 20409 | +** <li> SQLITE_MUTEX_STATIC_VFS1 | |
| 20410 | +** <li> SQLITE_MUTEX_STATIC_VFS2 | |
| 20411 | +** <li> SQLITE_MUTEX_STATIC_VFS3 | |
| 20387 | 20412 | ** </ul> |
| 20388 | 20413 | ** |
| 20389 | 20414 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 20390 | 20415 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 20391 | 20416 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -20793,14 +20818,13 @@ | ||
| 20793 | 20818 | sqlite3GlobalConfig.pScratch = 0; |
| 20794 | 20819 | sqlite3GlobalConfig.szScratch = 0; |
| 20795 | 20820 | sqlite3GlobalConfig.nScratch = 0; |
| 20796 | 20821 | } |
| 20797 | 20822 | if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 |
| 20798 | - || sqlite3GlobalConfig.nPage<1 ){ | |
| 20823 | + || sqlite3GlobalConfig.nPage<=0 ){ | |
| 20799 | 20824 | sqlite3GlobalConfig.pPage = 0; |
| 20800 | 20825 | sqlite3GlobalConfig.szPage = 0; |
| 20801 | - sqlite3GlobalConfig.nPage = 0; | |
| 20802 | 20826 | } |
| 20803 | 20827 | rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); |
| 20804 | 20828 | if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); |
| 20805 | 20829 | return rc; |
| 20806 | 20830 | } |
| @@ -26522,18 +26546,18 @@ | ||
| 26522 | 26546 | ** unixEnterMutex() |
| 26523 | 26547 | ** assert( unixMutexHeld() ); |
| 26524 | 26548 | ** unixEnterLeave() |
| 26525 | 26549 | */ |
| 26526 | 26550 | static void unixEnterMutex(void){ |
| 26527 | - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 26551 | + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 26528 | 26552 | } |
| 26529 | 26553 | static void unixLeaveMutex(void){ |
| 26530 | - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 26554 | + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 26531 | 26555 | } |
| 26532 | 26556 | #ifdef SQLITE_DEBUG |
| 26533 | 26557 | static int unixMutexHeld(void) { |
| 26534 | - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 26558 | + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 26535 | 26559 | } |
| 26536 | 26560 | #endif |
| 26537 | 26561 | |
| 26538 | 26562 | |
| 26539 | 26563 | #ifdef SQLITE_HAVE_OS_TRACE |
| @@ -37047,18 +37071,18 @@ | ||
| 37047 | 37071 | ** winShmEnterMutex() |
| 37048 | 37072 | ** assert( winShmMutexHeld() ); |
| 37049 | 37073 | ** winShmLeaveMutex() |
| 37050 | 37074 | */ |
| 37051 | 37075 | static void winShmEnterMutex(void){ |
| 37052 | - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 37076 | + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 37053 | 37077 | } |
| 37054 | 37078 | static void winShmLeaveMutex(void){ |
| 37055 | - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 37079 | + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 37056 | 37080 | } |
| 37057 | 37081 | #ifndef NDEBUG |
| 37058 | 37082 | static int winShmMutexHeld(void) { |
| 37059 | - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); | |
| 37083 | + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); | |
| 37060 | 37084 | } |
| 37061 | 37085 | #endif |
| 37062 | 37086 | |
| 37063 | 37087 | /* |
| 37064 | 37088 | ** Object used to represent a single file opened and mmapped to provide |
| @@ -40399,12 +40423,75 @@ | ||
| 40399 | 40423 | ** This file implements the default page cache implementation (the |
| 40400 | 40424 | ** sqlite3_pcache interface). It also contains part of the implementation |
| 40401 | 40425 | ** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. |
| 40402 | 40426 | ** If the default page cache implementation is overridden, then neither of |
| 40403 | 40427 | ** these two features are available. |
| 40428 | +** | |
| 40429 | +** A Page cache line looks like this: | |
| 40430 | +** | |
| 40431 | +** ------------------------------------------------------------- | |
| 40432 | +** | database page content | PgHdr1 | MemPage | PgHdr | | |
| 40433 | +** ------------------------------------------------------------- | |
| 40434 | +** | |
| 40435 | +** The database page content is up front (so that buffer overreads tend to | |
| 40436 | +** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage | |
| 40437 | +** is the extension added by the btree.c module containing information such | |
| 40438 | +** as the database page number and how that database page is used. PgHdr | |
| 40439 | +** is added by the pcache.c layer and contains information used to keep track | |
| 40440 | +** of which pages are "dirty". PgHdr1 is an extension added by this | |
| 40441 | +** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page. | |
| 40442 | +** PgHdr1 contains information needed to look up a page by its page number. | |
| 40443 | +** The superclass sqlite3_pcache_page.pBuf points to the start of the | |
| 40444 | +** database page content and sqlite3_pcache_page.pExtra points to PgHdr. | |
| 40445 | +** | |
| 40446 | +** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at | |
| 40447 | +** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The | |
| 40448 | +** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this | |
| 40449 | +** size can vary according to architecture, compile-time options, and | |
| 40450 | +** SQLite library version number. | |
| 40451 | +** | |
| 40452 | +** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained | |
| 40453 | +** using a separate memory allocation from the database page content. This | |
| 40454 | +** seeks to overcome the "clownshoe" problem (also called "internal | |
| 40455 | +** fragmentation" in academic literature) of allocating a few bytes more | |
| 40456 | +** than a power of two with the memory allocator rounding up to the next | |
| 40457 | +** power of two, and leaving the rounded-up space unused. | |
| 40458 | +** | |
| 40459 | +** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates | |
| 40460 | +** with this module. Information is passed back and forth as PgHdr1 pointers. | |
| 40461 | +** | |
| 40462 | +** The pcache.c and pager.c modules deal pointers to PgHdr objects. | |
| 40463 | +** The btree.c module deals with pointers to MemPage objects. | |
| 40464 | +** | |
| 40465 | +** SOURCE OF PAGE CACHE MEMORY: | |
| 40466 | +** | |
| 40467 | +** Memory for a page might come from any of three sources: | |
| 40468 | +** | |
| 40469 | +** (1) The general-purpose memory allocator - sqlite3Malloc() | |
| 40470 | +** (2) Global page-cache memory provided using sqlite3_config() with | |
| 40471 | +** SQLITE_CONFIG_PAGECACHE. | |
| 40472 | +** (3) PCache-local bulk allocation. | |
| 40473 | +** | |
| 40474 | +** The third case is a chunk of heap memory (defaulting to 100 pages worth) | |
| 40475 | +** that is allocated when the page cache is created. The size of the local | |
| 40476 | +** bulk allocation can be adjusted using | |
| 40477 | +** | |
| 40478 | +** sqlite3_config(SQLITE_CONFIG_PCACHE, 0, 0, N). | |
| 40479 | +** | |
| 40480 | +** If N is positive, then N pages worth of memory are allocated using a single | |
| 40481 | +** sqlite3Malloc() call and that memory is used for the first N pages allocated. | |
| 40482 | +** Or if N is negative, then -1024*N bytes of memory are allocated and used | |
| 40483 | +** for as many pages as can be accomodated. | |
| 40484 | +** | |
| 40485 | +** Only one of (2) or (3) can be used. Once the memory available to (2) or | |
| 40486 | +** (3) is exhausted, subsequent allocations fail over to the general-purpose | |
| 40487 | +** memory allocator (1). | |
| 40488 | +** | |
| 40489 | +** Earlier versions of SQLite used only methods (1) and (2). But experiments | |
| 40490 | +** show that method (3) with N==100 provides about a 5% performance boost for | |
| 40491 | +** common workloads. | |
| 40404 | 40492 | */ |
| 40405 | - | |
| 40406 | 40493 | |
| 40407 | 40494 | typedef struct PCache1 PCache1; |
| 40408 | 40495 | typedef struct PgHdr1 PgHdr1; |
| 40409 | 40496 | typedef struct PgFreeslot PgFreeslot; |
| 40410 | 40497 | typedef struct PGroup PGroup; |
| @@ -40453,12 +40540,13 @@ | ||
| 40453 | 40540 | ** flag (bPurgeable) are set when the cache is created. nMax may be |
| 40454 | 40541 | ** modified at any time by a call to the pcache1Cachesize() method. |
| 40455 | 40542 | ** The PGroup mutex must be held when accessing nMax. |
| 40456 | 40543 | */ |
| 40457 | 40544 | PGroup *pGroup; /* PGroup this cache belongs to */ |
| 40458 | - int szPage; /* Size of allocated pages in bytes */ | |
| 40459 | - int szExtra; /* Size of extra space in bytes */ | |
| 40545 | + int szPage; /* Size of database content section */ | |
| 40546 | + int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ | |
| 40547 | + int szAlloc; /* Total size of one pcache line */ | |
| 40460 | 40548 | int bPurgeable; /* True if cache is purgeable */ |
| 40461 | 40549 | unsigned int nMin; /* Minimum number of pages reserved */ |
| 40462 | 40550 | unsigned int nMax; /* Configured "cache_size" value */ |
| 40463 | 40551 | unsigned int n90pct; /* nMax*9/10 */ |
| 40464 | 40552 | unsigned int iMaxKey; /* Largest key seen since xTruncate() */ |
| @@ -40468,10 +40556,12 @@ | ||
| 40468 | 40556 | */ |
| 40469 | 40557 | unsigned int nRecyclable; /* Number of pages in the LRU list */ |
| 40470 | 40558 | unsigned int nPage; /* Total number of pages in apHash */ |
| 40471 | 40559 | unsigned int nHash; /* Number of slots in apHash[] */ |
| 40472 | 40560 | PgHdr1 **apHash; /* Hash table for fast lookup by key */ |
| 40561 | + PgHdr1 *pFree; /* List of unused pcache-local pages */ | |
| 40562 | + void *pBulk; /* Bulk memory used by pcache-local */ | |
| 40473 | 40563 | }; |
| 40474 | 40564 | |
| 40475 | 40565 | /* |
| 40476 | 40566 | ** Each cache entry is represented by an instance of the following |
| 40477 | 40567 | ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of |
| @@ -40480,19 +40570,20 @@ | ||
| 40480 | 40570 | */ |
| 40481 | 40571 | struct PgHdr1 { |
| 40482 | 40572 | sqlite3_pcache_page page; |
| 40483 | 40573 | unsigned int iKey; /* Key value (page number) */ |
| 40484 | 40574 | u8 isPinned; /* Page in use, not on the LRU list */ |
| 40575 | + u8 isBulkLocal; /* This page from bulk local storage */ | |
| 40485 | 40576 | PgHdr1 *pNext; /* Next in hash table chain */ |
| 40486 | 40577 | PCache1 *pCache; /* Cache that currently owns this page */ |
| 40487 | 40578 | PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ |
| 40488 | 40579 | PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ |
| 40489 | 40580 | }; |
| 40490 | 40581 | |
| 40491 | 40582 | /* |
| 40492 | -** Free slots in the allocator used to divide up the buffer provided using | |
| 40493 | -** the SQLITE_CONFIG_PAGECACHE mechanism. | |
| 40583 | +** Free slots in the allocator used to divide up the global page cache | |
| 40584 | +** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. | |
| 40494 | 40585 | */ |
| 40495 | 40586 | struct PgFreeslot { |
| 40496 | 40587 | PgFreeslot *pNext; /* Next free slot */ |
| 40497 | 40588 | }; |
| 40498 | 40589 | |
| @@ -40506,14 +40597,15 @@ | ||
| 40506 | 40597 | ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all |
| 40507 | 40598 | ** fixed at sqlite3_initialize() time and do not require mutex protection. |
| 40508 | 40599 | ** The nFreeSlot and pFree values do require mutex protection. |
| 40509 | 40600 | */ |
| 40510 | 40601 | int isInit; /* True if initialized */ |
| 40602 | + int separateCache; /* Use a new PGroup for each PCache */ | |
| 40511 | 40603 | int szSlot; /* Size of each free slot */ |
| 40512 | 40604 | int nSlot; /* The number of pcache slots */ |
| 40513 | 40605 | int nReserve; /* Try to keep nFreeSlot above this */ |
| 40514 | - void *pStart, *pEnd; /* Bounds of pagecache malloc range */ | |
| 40606 | + void *pStart, *pEnd; /* Bounds of global page cache memory */ | |
| 40515 | 40607 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 40516 | 40608 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 40517 | 40609 | PgFreeslot *pFree; /* Free page blocks */ |
| 40518 | 40610 | int nFreeSlot; /* Number of unused pcache slots */ |
| 40519 | 40611 | /* The following value requires a mutex to change. We skip the mutex on |
| @@ -40556,10 +40648,11 @@ | ||
| 40556 | 40648 | ** to be serialized already. There is no need for further mutexing. |
| 40557 | 40649 | */ |
| 40558 | 40650 | SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ |
| 40559 | 40651 | if( pcache1.isInit ){ |
| 40560 | 40652 | PgFreeslot *p; |
| 40653 | + if( pBuf==0 ) sz = n = 0; | |
| 40561 | 40654 | sz = ROUNDDOWN8(sz); |
| 40562 | 40655 | pcache1.szSlot = sz; |
| 40563 | 40656 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 40564 | 40657 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 40565 | 40658 | pcache1.pStart = pBuf; |
| @@ -40620,13 +40713,13 @@ | ||
| 40620 | 40713 | } |
| 40621 | 40714 | |
| 40622 | 40715 | /* |
| 40623 | 40716 | ** Free an allocated buffer obtained from pcache1Alloc(). |
| 40624 | 40717 | */ |
| 40625 | -static int pcache1Free(void *p){ | |
| 40718 | +static void pcache1Free(void *p){ | |
| 40626 | 40719 | int nFreed = 0; |
| 40627 | - if( p==0 ) return 0; | |
| 40720 | + if( p==0 ) return; | |
| 40628 | 40721 | if( p>=pcache1.pStart && p<pcache1.pEnd ){ |
| 40629 | 40722 | PgFreeslot *pSlot; |
| 40630 | 40723 | sqlite3_mutex_enter(pcache1.mutex); |
| 40631 | 40724 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 40632 | 40725 | pSlot = (PgFreeslot*)p; |
| @@ -40637,19 +40730,18 @@ | ||
| 40637 | 40730 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 40638 | 40731 | sqlite3_mutex_leave(pcache1.mutex); |
| 40639 | 40732 | }else{ |
| 40640 | 40733 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 40641 | 40734 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 40642 | - nFreed = sqlite3MallocSize(p); | |
| 40643 | 40735 | #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS |
| 40736 | + nFreed = sqlite3MallocSize(p); | |
| 40644 | 40737 | sqlite3_mutex_enter(pcache1.mutex); |
| 40645 | 40738 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); |
| 40646 | 40739 | sqlite3_mutex_leave(pcache1.mutex); |
| 40647 | 40740 | #endif |
| 40648 | 40741 | sqlite3_free(p); |
| 40649 | 40742 | } |
| 40650 | - return nFreed; | |
| 40651 | 40743 | } |
| 40652 | 40744 | |
| 40653 | 40745 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40654 | 40746 | /* |
| 40655 | 40747 | ** Return the size of a pcache allocation |
| @@ -40673,58 +40765,69 @@ | ||
| 40673 | 40765 | */ |
| 40674 | 40766 | static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ |
| 40675 | 40767 | PgHdr1 *p = 0; |
| 40676 | 40768 | void *pPg; |
| 40677 | 40769 | |
| 40678 | - /* The group mutex must be released before pcache1Alloc() is called. This | |
| 40679 | - ** is because it may call sqlite3_release_memory(), which assumes that | |
| 40680 | - ** this mutex is not held. */ | |
| 40681 | 40770 | assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
| 40682 | - pcache1LeaveMutex(pCache->pGroup); | |
| 40771 | + if( pCache->pFree ){ | |
| 40772 | + p = pCache->pFree; | |
| 40773 | + pCache->pFree = p->pNext; | |
| 40774 | + p->pNext = 0; | |
| 40775 | + }else{ | |
| 40776 | +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT | |
| 40777 | + /* The group mutex must be released before pcache1Alloc() is called. This | |
| 40778 | + ** is because it might call sqlite3_release_memory(), which assumes that | |
| 40779 | + ** this mutex is not held. */ | |
| 40780 | + assert( pcache1.separateCache==0 ); | |
| 40781 | + assert( pCache->pGroup==&pcache1.grp ); | |
| 40782 | + pcache1LeaveMutex(pCache->pGroup); | |
| 40783 | +#endif | |
| 40683 | 40784 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40684 | - pPg = pcache1Alloc(pCache->szPage); | |
| 40685 | - p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); | |
| 40686 | - if( !pPg || !p ){ | |
| 40687 | - pcache1Free(pPg); | |
| 40688 | - sqlite3_free(p); | |
| 40689 | - pPg = 0; | |
| 40690 | - } | |
| 40785 | + pPg = pcache1Alloc(pCache->szPage); | |
| 40786 | + p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); | |
| 40787 | + if( !pPg || !p ){ | |
| 40788 | + pcache1Free(pPg); | |
| 40789 | + sqlite3_free(p); | |
| 40790 | + pPg = 0; | |
| 40791 | + } | |
| 40691 | 40792 | #else |
| 40692 | - pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra); | |
| 40693 | - p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; | |
| 40793 | + pPg = pcache1Alloc(pCache->szAlloc); | |
| 40794 | + p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; | |
| 40694 | 40795 | #endif |
| 40695 | - pcache1EnterMutex(pCache->pGroup); | |
| 40696 | - | |
| 40697 | - if( pPg ){ | |
| 40796 | +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT | |
| 40797 | + pcache1EnterMutex(pCache->pGroup); | |
| 40798 | +#endif | |
| 40799 | + if( pPg==0 ) return 0; | |
| 40698 | 40800 | p->page.pBuf = pPg; |
| 40699 | 40801 | p->page.pExtra = &p[1]; |
| 40700 | - if( pCache->bPurgeable ){ | |
| 40701 | - pCache->pGroup->nCurrentPage++; | |
| 40702 | - } | |
| 40703 | - return p; | |
| 40802 | + p->isBulkLocal = 0; | |
| 40704 | 40803 | } |
| 40705 | - return 0; | |
| 40804 | + if( pCache->bPurgeable ){ | |
| 40805 | + pCache->pGroup->nCurrentPage++; | |
| 40806 | + } | |
| 40807 | + return p; | |
| 40706 | 40808 | } |
| 40707 | 40809 | |
| 40708 | 40810 | /* |
| 40709 | 40811 | ** Free a page object allocated by pcache1AllocPage(). |
| 40710 | -** | |
| 40711 | -** The pointer is allowed to be NULL, which is prudent. But it turns out | |
| 40712 | -** that the current implementation happens to never call this routine | |
| 40713 | -** with a NULL pointer, so we mark the NULL test with ALWAYS(). | |
| 40714 | 40812 | */ |
| 40715 | 40813 | static void pcache1FreePage(PgHdr1 *p){ |
| 40716 | - if( ALWAYS(p) ){ | |
| 40717 | - PCache1 *pCache = p->pCache; | |
| 40718 | - assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); | |
| 40814 | + PCache1 *pCache; | |
| 40815 | + assert( p!=0 ); | |
| 40816 | + pCache = p->pCache; | |
| 40817 | + assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); | |
| 40818 | + if( p->isBulkLocal ){ | |
| 40819 | + p->pNext = pCache->pFree; | |
| 40820 | + pCache->pFree = p; | |
| 40821 | + }else{ | |
| 40719 | 40822 | pcache1Free(p->page.pBuf); |
| 40720 | 40823 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40721 | 40824 | sqlite3_free(p); |
| 40722 | 40825 | #endif |
| 40723 | - if( pCache->bPurgeable ){ | |
| 40724 | - pCache->pGroup->nCurrentPage--; | |
| 40725 | - } | |
| 40826 | + } | |
| 40827 | + if( pCache->bPurgeable ){ | |
| 40828 | + pCache->pGroup->nCurrentPage--; | |
| 40726 | 40829 | } |
| 40727 | 40830 | } |
| 40728 | 40831 | |
| 40729 | 40832 | /* |
| 40730 | 40833 | ** Malloc function used by SQLite to obtain space from the buffer configured |
| @@ -40920,10 +41023,35 @@ | ||
| 40920 | 41023 | */ |
| 40921 | 41024 | static int pcache1Init(void *NotUsed){ |
| 40922 | 41025 | UNUSED_PARAMETER(NotUsed); |
| 40923 | 41026 | assert( pcache1.isInit==0 ); |
| 40924 | 41027 | memset(&pcache1, 0, sizeof(pcache1)); |
| 41028 | + | |
| 41029 | + | |
| 41030 | + /* | |
| 41031 | + ** The pcache1.separateCache variable is true if each PCache has its own | |
| 41032 | + ** private PGroup (mode-1). pcache1.separateCache is false if the single | |
| 41033 | + ** PGroup in pcache1.grp is used for all page caches (mode-2). | |
| 41034 | + ** | |
| 41035 | + ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT | |
| 41036 | + ** | |
| 41037 | + ** * Use a unified cache in single-threaded applications that have | |
| 41038 | + ** configured a start-time buffer for use as page-cache memory using | |
| 41039 | + ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL | |
| 41040 | + ** pBuf argument. | |
| 41041 | + ** | |
| 41042 | + ** * Otherwise use separate caches (mode-1) | |
| 41043 | + */ | |
| 41044 | +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) | |
| 41045 | + pcache1.separateCache = 0; | |
| 41046 | +#elif SQLITE_THREADSAFE | |
| 41047 | + pcache1.separateCache = sqlite3GlobalConfig.pPage==0 | |
| 41048 | + || sqlite3GlobalConfig.bCoreMutex>0; | |
| 41049 | +#else | |
| 41050 | + pcache1.separateCache = sqlite3GlobalConfig.pPage==0; | |
| 41051 | +#endif | |
| 41052 | + | |
| 40925 | 41053 | #if SQLITE_THREADSAFE |
| 40926 | 41054 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 40927 | 41055 | pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); |
| 40928 | 41056 | pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); |
| 40929 | 41057 | } |
| @@ -40955,52 +41083,65 @@ | ||
| 40955 | 41083 | static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ |
| 40956 | 41084 | PCache1 *pCache; /* The newly created page cache */ |
| 40957 | 41085 | PGroup *pGroup; /* The group the new page cache will belong to */ |
| 40958 | 41086 | int sz; /* Bytes of memory required to allocate the new cache */ |
| 40959 | 41087 | |
| 40960 | - /* | |
| 40961 | - ** The separateCache variable is true if each PCache has its own private | |
| 40962 | - ** PGroup. In other words, separateCache is true for mode (1) where no | |
| 40963 | - ** mutexing is required. | |
| 40964 | - ** | |
| 40965 | - ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT | |
| 40966 | - ** | |
| 40967 | - ** * Always use a unified cache in single-threaded applications | |
| 40968 | - ** | |
| 40969 | - ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off) | |
| 40970 | - ** use separate caches (mode-1) | |
| 40971 | - */ | |
| 40972 | -#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 | |
| 40973 | - const int separateCache = 0; | |
| 40974 | -#else | |
| 40975 | - int separateCache = sqlite3GlobalConfig.bCoreMutex>0; | |
| 40976 | -#endif | |
| 40977 | - | |
| 40978 | 41088 | assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); |
| 40979 | 41089 | assert( szExtra < 300 ); |
| 40980 | 41090 | |
| 40981 | - sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; | |
| 41091 | + sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; | |
| 40982 | 41092 | pCache = (PCache1 *)sqlite3MallocZero(sz); |
| 40983 | 41093 | if( pCache ){ |
| 40984 | - if( separateCache ){ | |
| 41094 | + if( pcache1.separateCache ){ | |
| 40985 | 41095 | pGroup = (PGroup*)&pCache[1]; |
| 40986 | 41096 | pGroup->mxPinned = 10; |
| 40987 | 41097 | }else{ |
| 40988 | 41098 | pGroup = &pcache1.grp; |
| 40989 | 41099 | } |
| 40990 | 41100 | pCache->pGroup = pGroup; |
| 40991 | 41101 | pCache->szPage = szPage; |
| 40992 | 41102 | pCache->szExtra = szExtra; |
| 41103 | + pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); | |
| 40993 | 41104 | pCache->bPurgeable = (bPurgeable ? 1 : 0); |
| 40994 | 41105 | pcache1EnterMutex(pGroup); |
| 40995 | 41106 | pcache1ResizeHash(pCache); |
| 40996 | 41107 | if( bPurgeable ){ |
| 40997 | 41108 | pCache->nMin = 10; |
| 40998 | 41109 | pGroup->nMinPage += pCache->nMin; |
| 40999 | 41110 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41000 | 41111 | } |
| 41001 | 41112 | pcache1LeaveMutex(pGroup); |
| 41113 | + /* Try to initialize the local bulk pagecache line allocation if using | |
| 41114 | + ** separate caches and if nPage!=0 */ | |
| 41115 | + if( pcache1.separateCache | |
| 41116 | + && sqlite3GlobalConfig.nPage!=0 | |
| 41117 | + && sqlite3GlobalConfig.pPage==0 | |
| 41118 | + ){ | |
| 41119 | + int szBulk; | |
| 41120 | + char *zBulk; | |
| 41121 | + sqlite3BeginBenignMalloc(); | |
| 41122 | + if( sqlite3GlobalConfig.nPage>0 ){ | |
| 41123 | + szBulk = pCache->szAlloc * sqlite3GlobalConfig.nPage; | |
| 41124 | + }else{ | |
| 41125 | + szBulk = -1024*sqlite3GlobalConfig.nPage; | |
| 41126 | + } | |
| 41127 | + zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); | |
| 41128 | + sqlite3EndBenignMalloc(); | |
| 41129 | + if( zBulk ){ | |
| 41130 | + int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; | |
| 41131 | + int i; | |
| 41132 | + for(i=0; i<nBulk; i++){ | |
| 41133 | + PgHdr1 *pX = (PgHdr1*)&zBulk[szPage]; | |
| 41134 | + pX->page.pBuf = zBulk; | |
| 41135 | + pX->page.pExtra = &pX[1]; | |
| 41136 | + pX->isBulkLocal = 1; | |
| 41137 | + pX->pNext = pCache->pFree; | |
| 41138 | + pCache->pFree = pX; | |
| 41139 | + zBulk += pCache->szAlloc; | |
| 41140 | + } | |
| 41141 | + } | |
| 41142 | + } | |
| 41002 | 41143 | if( pCache->nHash==0 ){ |
| 41003 | 41144 | pcache1Destroy((sqlite3_pcache*)pCache); |
| 41004 | 41145 | pCache = 0; |
| 41005 | 41146 | } |
| 41006 | 41147 | } |
| @@ -41090,30 +41231,21 @@ | ||
| 41090 | 41231 | |
| 41091 | 41232 | if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); |
| 41092 | 41233 | assert( pCache->nHash>0 && pCache->apHash ); |
| 41093 | 41234 | |
| 41094 | 41235 | /* Step 4. Try to recycle a page. */ |
| 41095 | - if( pCache->bPurgeable && pGroup->pLruTail && ( | |
| 41096 | - (pCache->nPage+1>=pCache->nMax) | |
| 41097 | - || pGroup->nCurrentPage>=pGroup->nMaxPage | |
| 41098 | - || pcache1UnderMemoryPressure(pCache) | |
| 41099 | - )){ | |
| 41236 | + if( pCache->bPurgeable | |
| 41237 | + && pGroup->pLruTail | |
| 41238 | + && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) | |
| 41239 | + ){ | |
| 41100 | 41240 | PCache1 *pOther; |
| 41101 | 41241 | pPage = pGroup->pLruTail; |
| 41102 | 41242 | assert( pPage->isPinned==0 ); |
| 41103 | 41243 | pcache1RemoveFromHash(pPage, 0); |
| 41104 | 41244 | pcache1PinPage(pPage); |
| 41105 | 41245 | pOther = pPage->pCache; |
| 41106 | - | |
| 41107 | - /* We want to verify that szPage and szExtra are the same for pOther | |
| 41108 | - ** and pCache. Assert that we can verify this by comparing sums. */ | |
| 41109 | - assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 ); | |
| 41110 | - assert( pCache->szExtra<512 ); | |
| 41111 | - assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 ); | |
| 41112 | - assert( pOther->szExtra<512 ); | |
| 41113 | - | |
| 41114 | - if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){ | |
| 41246 | + if( pOther->szAlloc != pCache->szAlloc ){ | |
| 41115 | 41247 | pcache1FreePage(pPage); |
| 41116 | 41248 | pPage = 0; |
| 41117 | 41249 | }else{ |
| 41118 | 41250 | pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); |
| 41119 | 41251 | } |
| @@ -41385,10 +41517,11 @@ | ||
| 41385 | 41517 | assert( pGroup->nMinPage >= pCache->nMin ); |
| 41386 | 41518 | pGroup->nMinPage -= pCache->nMin; |
| 41387 | 41519 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41388 | 41520 | pcache1EnforceMaxPage(pGroup); |
| 41389 | 41521 | pcache1LeaveMutex(pGroup); |
| 41522 | + sqlite3_free(pCache->pBulk); | |
| 41390 | 41523 | sqlite3_free(pCache->apHash); |
| 41391 | 41524 | sqlite3_free(pCache); |
| 41392 | 41525 | } |
| 41393 | 41526 | |
| 41394 | 41527 | /* |
| @@ -41440,11 +41573,11 @@ | ||
| 41440 | 41573 | */ |
| 41441 | 41574 | SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ |
| 41442 | 41575 | int nFree = 0; |
| 41443 | 41576 | assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); |
| 41444 | 41577 | assert( sqlite3_mutex_notheld(pcache1.mutex) ); |
| 41445 | - if( pcache1.pStart==0 ){ | |
| 41578 | + if( sqlite3GlobalConfig.nPage==0 ){ | |
| 41446 | 41579 | PgHdr1 *p; |
| 41447 | 41580 | pcache1EnterMutex(&pcache1.grp); |
| 41448 | 41581 | while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){ |
| 41449 | 41582 | nFree += pcache1MemSize(p->page.pBuf); |
| 41450 | 41583 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| @@ -62542,10 +62675,11 @@ | ||
| 62542 | 62675 | u32 *heap = 0; /* Min-heap used for checking cell coverage */ |
| 62543 | 62676 | u32 x, prev = 0; /* Next and previous entry on the min-heap */ |
| 62544 | 62677 | const char *saved_zPfx = pCheck->zPfx; |
| 62545 | 62678 | int saved_v1 = pCheck->v1; |
| 62546 | 62679 | int saved_v2 = pCheck->v2; |
| 62680 | + u8 savedIsInit; | |
| 62547 | 62681 | |
| 62548 | 62682 | /* Check that the page exists |
| 62549 | 62683 | */ |
| 62550 | 62684 | pBt = pCheck->pBt; |
| 62551 | 62685 | usableSize = pBt->usableSize; |
| @@ -62559,10 +62693,11 @@ | ||
| 62559 | 62693 | goto end_of_check; |
| 62560 | 62694 | } |
| 62561 | 62695 | |
| 62562 | 62696 | /* Clear MemPage.isInit to make sure the corruption detection code in |
| 62563 | 62697 | ** btreeInitPage() is executed. */ |
| 62698 | + savedIsInit = pPage->isInit; | |
| 62564 | 62699 | pPage->isInit = 0; |
| 62565 | 62700 | if( (rc = btreeInitPage(pPage))!=0 ){ |
| 62566 | 62701 | assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ |
| 62567 | 62702 | checkAppendMsg(pCheck, |
| 62568 | 62703 | "btreeInitPage() returns error code %d", rc); |
| @@ -62700,11 +62835,11 @@ | ||
| 62700 | 62835 | while( i>0 ){ |
| 62701 | 62836 | int size, j; |
| 62702 | 62837 | assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */ |
| 62703 | 62838 | size = get2byte(&data[i+2]); |
| 62704 | 62839 | assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */ |
| 62705 | - btreeHeapInsert(heap, (i<<16)|(i+size-1)); | |
| 62840 | + btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); | |
| 62706 | 62841 | /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a |
| 62707 | 62842 | ** big-endian integer which is the offset in the b-tree page of the next |
| 62708 | 62843 | ** freeblock in the chain, or zero if the freeblock is the last on the |
| 62709 | 62844 | ** chain. */ |
| 62710 | 62845 | j = get2byte(&data[i]); |
| @@ -62751,10 +62886,11 @@ | ||
| 62751 | 62886 | nFrag, data[hdr+7], iPage); |
| 62752 | 62887 | } |
| 62753 | 62888 | } |
| 62754 | 62889 | |
| 62755 | 62890 | end_of_check: |
| 62891 | + if( !doCoverageCheck ) pPage->isInit = savedIsInit; | |
| 62756 | 62892 | releasePage(pPage); |
| 62757 | 62893 | pCheck->zPfx = saved_zPfx; |
| 62758 | 62894 | pCheck->v1 = saved_v1; |
| 62759 | 62895 | pCheck->v2 = saved_v2; |
| 62760 | 62896 | return depth+1; |
| @@ -69020,10 +69156,11 @@ | ||
| 69020 | 69156 | ** to ignore the compiler warnings and leave this variable uninitialized. |
| 69021 | 69157 | */ |
| 69022 | 69158 | /* mem1.u.i = 0; // not needed, here to silence compiler warning */ |
| 69023 | 69159 | |
| 69024 | 69160 | idx1 = getVarint32(aKey1, szHdr1); |
| 69161 | + if( szHdr1>98307 ) return SQLITE_CORRUPT; | |
| 69025 | 69162 | d1 = szHdr1; |
| 69026 | 69163 | assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB ); |
| 69027 | 69164 | assert( pKeyInfo->aSortOrder!=0 ); |
| 69028 | 69165 | assert( pKeyInfo->nField>0 ); |
| 69029 | 69166 | assert( idx1<=szHdr1 || CORRUPT_DB ); |
| @@ -109379,14 +109516,18 @@ | ||
| 109379 | 109516 | sqlite3VdbeResolveLabel(v, addrCont); |
| 109380 | 109517 | |
| 109381 | 109518 | /* Execute the recursive SELECT taking the single row in Current as |
| 109382 | 109519 | ** the value for the recursive-table. Store the results in the Queue. |
| 109383 | 109520 | */ |
| 109384 | - p->pPrior = 0; | |
| 109385 | - sqlite3Select(pParse, p, &destQueue); | |
| 109386 | - assert( p->pPrior==0 ); | |
| 109387 | - p->pPrior = pSetup; | |
| 109521 | + if( p->selFlags & SF_Aggregate ){ | |
| 109522 | + sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); | |
| 109523 | + }else{ | |
| 109524 | + p->pPrior = 0; | |
| 109525 | + sqlite3Select(pParse, p, &destQueue); | |
| 109526 | + assert( p->pPrior==0 ); | |
| 109527 | + p->pPrior = pSetup; | |
| 109528 | + } | |
| 109388 | 109529 | |
| 109389 | 109530 | /* Keep running the loop until the Queue is empty */ |
| 109390 | 109531 | sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); |
| 109391 | 109532 | sqlite3VdbeResolveLabel(v, addrBreak); |
| 109392 | 109533 | |
| 109393 | 109534 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 326 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 327 | */ |
| 328 | #define SQLITE_VERSION "3.8.11" |
| 329 | #define SQLITE_VERSION_NUMBER 3008011 |
| 330 | #define SQLITE_SOURCE_ID "2015-07-03 17:54:49 030f60a7ba171650ce8c0ac32dc166eab80aca32" |
| 331 | |
| 332 | /* |
| 333 | ** CAPI3REF: Run-Time Library Version Numbers |
| 334 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 335 | ** |
| @@ -6503,10 +6503,13 @@ | |
| 6503 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6504 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6505 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6506 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6507 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6508 | |
| 6509 | /* |
| 6510 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6511 | ** METHOD: sqlite3 |
| 6512 | ** |
| @@ -8941,10 +8944,20 @@ | |
| 8941 | #if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS |
| 8942 | # undef SQLITE_MAX_WORKER_THREADS |
| 8943 | # define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS |
| 8944 | #endif |
| 8945 | |
| 8946 | |
| 8947 | /* |
| 8948 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 8949 | ** ourselves. |
| 8950 | */ |
| @@ -14043,11 +14056,11 @@ | |
| 14043 | (void*)0, /* pScratch */ |
| 14044 | 0, /* szScratch */ |
| 14045 | 0, /* nScratch */ |
| 14046 | (void*)0, /* pPage */ |
| 14047 | 0, /* szPage */ |
| 14048 | 0, /* nPage */ |
| 14049 | 0, /* mxParserStack */ |
| 14050 | 0, /* sharedCacheEnabled */ |
| 14051 | SQLITE_SORTER_PMASZ, /* szPma */ |
| 14052 | /* All the rest should always be initialized to zero */ |
| 14053 | 0, /* isInit */ |
| @@ -19454,11 +19467,11 @@ | |
| 19454 | ** The sqlite3_mutex_alloc() routine allocates a new |
| 19455 | ** mutex and returns a pointer to it. If it returns NULL |
| 19456 | ** that means that a mutex could not be allocated. |
| 19457 | */ |
| 19458 | static sqlite3_mutex *debugMutexAlloc(int id){ |
| 19459 | static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1]; |
| 19460 | sqlite3_debug_mutex *pNew = 0; |
| 19461 | switch( id ){ |
| 19462 | case SQLITE_MUTEX_FAST: |
| 19463 | case SQLITE_MUTEX_RECURSIVE: { |
| 19464 | pNew = sqlite3Malloc(sizeof(*pNew)); |
| @@ -19669,10 +19682,13 @@ | |
| 19669 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 19670 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 19671 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 19672 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 19673 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 19674 | ** </ul> |
| 19675 | ** |
| 19676 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 19677 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 19678 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -19697,10 +19713,13 @@ | |
| 19697 | ** mutex types, the same mutex is returned on every call that has |
| 19698 | ** the same type number. |
| 19699 | */ |
| 19700 | static sqlite3_mutex *pthreadMutexAlloc(int iType){ |
| 19701 | static sqlite3_mutex staticMutexes[] = { |
| 19702 | SQLITE3_MUTEX_INITIALIZER, |
| 19703 | SQLITE3_MUTEX_INITIALIZER, |
| 19704 | SQLITE3_MUTEX_INITIALIZER, |
| 19705 | SQLITE3_MUTEX_INITIALIZER, |
| 19706 | SQLITE3_MUTEX_INITIALIZER, |
| @@ -20311,10 +20330,13 @@ | |
| 20311 | SQLITE3_MUTEX_INITIALIZER, |
| 20312 | SQLITE3_MUTEX_INITIALIZER, |
| 20313 | SQLITE3_MUTEX_INITIALIZER, |
| 20314 | SQLITE3_MUTEX_INITIALIZER, |
| 20315 | SQLITE3_MUTEX_INITIALIZER, |
| 20316 | SQLITE3_MUTEX_INITIALIZER |
| 20317 | }; |
| 20318 | |
| 20319 | static int winMutex_isInit = 0; |
| 20320 | static int winMutex_isNt = -1; /* <0 means "need to query" */ |
| @@ -20382,10 +20404,13 @@ | |
| 20382 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 20383 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 20384 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 20385 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 20386 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 20387 | ** </ul> |
| 20388 | ** |
| 20389 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 20390 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 20391 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -20793,14 +20818,13 @@ | |
| 20793 | sqlite3GlobalConfig.pScratch = 0; |
| 20794 | sqlite3GlobalConfig.szScratch = 0; |
| 20795 | sqlite3GlobalConfig.nScratch = 0; |
| 20796 | } |
| 20797 | if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 |
| 20798 | || sqlite3GlobalConfig.nPage<1 ){ |
| 20799 | sqlite3GlobalConfig.pPage = 0; |
| 20800 | sqlite3GlobalConfig.szPage = 0; |
| 20801 | sqlite3GlobalConfig.nPage = 0; |
| 20802 | } |
| 20803 | rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); |
| 20804 | if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); |
| 20805 | return rc; |
| 20806 | } |
| @@ -26522,18 +26546,18 @@ | |
| 26522 | ** unixEnterMutex() |
| 26523 | ** assert( unixMutexHeld() ); |
| 26524 | ** unixEnterLeave() |
| 26525 | */ |
| 26526 | static void unixEnterMutex(void){ |
| 26527 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 26528 | } |
| 26529 | static void unixLeaveMutex(void){ |
| 26530 | sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 26531 | } |
| 26532 | #ifdef SQLITE_DEBUG |
| 26533 | static int unixMutexHeld(void) { |
| 26534 | return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 26535 | } |
| 26536 | #endif |
| 26537 | |
| 26538 | |
| 26539 | #ifdef SQLITE_HAVE_OS_TRACE |
| @@ -37047,18 +37071,18 @@ | |
| 37047 | ** winShmEnterMutex() |
| 37048 | ** assert( winShmMutexHeld() ); |
| 37049 | ** winShmLeaveMutex() |
| 37050 | */ |
| 37051 | static void winShmEnterMutex(void){ |
| 37052 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 37053 | } |
| 37054 | static void winShmLeaveMutex(void){ |
| 37055 | sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 37056 | } |
| 37057 | #ifndef NDEBUG |
| 37058 | static int winShmMutexHeld(void) { |
| 37059 | return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 37060 | } |
| 37061 | #endif |
| 37062 | |
| 37063 | /* |
| 37064 | ** Object used to represent a single file opened and mmapped to provide |
| @@ -40399,12 +40423,75 @@ | |
| 40399 | ** This file implements the default page cache implementation (the |
| 40400 | ** sqlite3_pcache interface). It also contains part of the implementation |
| 40401 | ** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. |
| 40402 | ** If the default page cache implementation is overridden, then neither of |
| 40403 | ** these two features are available. |
| 40404 | */ |
| 40405 | |
| 40406 | |
| 40407 | typedef struct PCache1 PCache1; |
| 40408 | typedef struct PgHdr1 PgHdr1; |
| 40409 | typedef struct PgFreeslot PgFreeslot; |
| 40410 | typedef struct PGroup PGroup; |
| @@ -40453,12 +40540,13 @@ | |
| 40453 | ** flag (bPurgeable) are set when the cache is created. nMax may be |
| 40454 | ** modified at any time by a call to the pcache1Cachesize() method. |
| 40455 | ** The PGroup mutex must be held when accessing nMax. |
| 40456 | */ |
| 40457 | PGroup *pGroup; /* PGroup this cache belongs to */ |
| 40458 | int szPage; /* Size of allocated pages in bytes */ |
| 40459 | int szExtra; /* Size of extra space in bytes */ |
| 40460 | int bPurgeable; /* True if cache is purgeable */ |
| 40461 | unsigned int nMin; /* Minimum number of pages reserved */ |
| 40462 | unsigned int nMax; /* Configured "cache_size" value */ |
| 40463 | unsigned int n90pct; /* nMax*9/10 */ |
| 40464 | unsigned int iMaxKey; /* Largest key seen since xTruncate() */ |
| @@ -40468,10 +40556,12 @@ | |
| 40468 | */ |
| 40469 | unsigned int nRecyclable; /* Number of pages in the LRU list */ |
| 40470 | unsigned int nPage; /* Total number of pages in apHash */ |
| 40471 | unsigned int nHash; /* Number of slots in apHash[] */ |
| 40472 | PgHdr1 **apHash; /* Hash table for fast lookup by key */ |
| 40473 | }; |
| 40474 | |
| 40475 | /* |
| 40476 | ** Each cache entry is represented by an instance of the following |
| 40477 | ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of |
| @@ -40480,19 +40570,20 @@ | |
| 40480 | */ |
| 40481 | struct PgHdr1 { |
| 40482 | sqlite3_pcache_page page; |
| 40483 | unsigned int iKey; /* Key value (page number) */ |
| 40484 | u8 isPinned; /* Page in use, not on the LRU list */ |
| 40485 | PgHdr1 *pNext; /* Next in hash table chain */ |
| 40486 | PCache1 *pCache; /* Cache that currently owns this page */ |
| 40487 | PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ |
| 40488 | PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ |
| 40489 | }; |
| 40490 | |
| 40491 | /* |
| 40492 | ** Free slots in the allocator used to divide up the buffer provided using |
| 40493 | ** the SQLITE_CONFIG_PAGECACHE mechanism. |
| 40494 | */ |
| 40495 | struct PgFreeslot { |
| 40496 | PgFreeslot *pNext; /* Next free slot */ |
| 40497 | }; |
| 40498 | |
| @@ -40506,14 +40597,15 @@ | |
| 40506 | ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all |
| 40507 | ** fixed at sqlite3_initialize() time and do not require mutex protection. |
| 40508 | ** The nFreeSlot and pFree values do require mutex protection. |
| 40509 | */ |
| 40510 | int isInit; /* True if initialized */ |
| 40511 | int szSlot; /* Size of each free slot */ |
| 40512 | int nSlot; /* The number of pcache slots */ |
| 40513 | int nReserve; /* Try to keep nFreeSlot above this */ |
| 40514 | void *pStart, *pEnd; /* Bounds of pagecache malloc range */ |
| 40515 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 40516 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 40517 | PgFreeslot *pFree; /* Free page blocks */ |
| 40518 | int nFreeSlot; /* Number of unused pcache slots */ |
| 40519 | /* The following value requires a mutex to change. We skip the mutex on |
| @@ -40556,10 +40648,11 @@ | |
| 40556 | ** to be serialized already. There is no need for further mutexing. |
| 40557 | */ |
| 40558 | SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ |
| 40559 | if( pcache1.isInit ){ |
| 40560 | PgFreeslot *p; |
| 40561 | sz = ROUNDDOWN8(sz); |
| 40562 | pcache1.szSlot = sz; |
| 40563 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 40564 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 40565 | pcache1.pStart = pBuf; |
| @@ -40620,13 +40713,13 @@ | |
| 40620 | } |
| 40621 | |
| 40622 | /* |
| 40623 | ** Free an allocated buffer obtained from pcache1Alloc(). |
| 40624 | */ |
| 40625 | static int pcache1Free(void *p){ |
| 40626 | int nFreed = 0; |
| 40627 | if( p==0 ) return 0; |
| 40628 | if( p>=pcache1.pStart && p<pcache1.pEnd ){ |
| 40629 | PgFreeslot *pSlot; |
| 40630 | sqlite3_mutex_enter(pcache1.mutex); |
| 40631 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 40632 | pSlot = (PgFreeslot*)p; |
| @@ -40637,19 +40730,18 @@ | |
| 40637 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 40638 | sqlite3_mutex_leave(pcache1.mutex); |
| 40639 | }else{ |
| 40640 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 40641 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 40642 | nFreed = sqlite3MallocSize(p); |
| 40643 | #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS |
| 40644 | sqlite3_mutex_enter(pcache1.mutex); |
| 40645 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); |
| 40646 | sqlite3_mutex_leave(pcache1.mutex); |
| 40647 | #endif |
| 40648 | sqlite3_free(p); |
| 40649 | } |
| 40650 | return nFreed; |
| 40651 | } |
| 40652 | |
| 40653 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40654 | /* |
| 40655 | ** Return the size of a pcache allocation |
| @@ -40673,58 +40765,69 @@ | |
| 40673 | */ |
| 40674 | static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ |
| 40675 | PgHdr1 *p = 0; |
| 40676 | void *pPg; |
| 40677 | |
| 40678 | /* The group mutex must be released before pcache1Alloc() is called. This |
| 40679 | ** is because it may call sqlite3_release_memory(), which assumes that |
| 40680 | ** this mutex is not held. */ |
| 40681 | assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
| 40682 | pcache1LeaveMutex(pCache->pGroup); |
| 40683 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40684 | pPg = pcache1Alloc(pCache->szPage); |
| 40685 | p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); |
| 40686 | if( !pPg || !p ){ |
| 40687 | pcache1Free(pPg); |
| 40688 | sqlite3_free(p); |
| 40689 | pPg = 0; |
| 40690 | } |
| 40691 | #else |
| 40692 | pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra); |
| 40693 | p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
| 40694 | #endif |
| 40695 | pcache1EnterMutex(pCache->pGroup); |
| 40696 | |
| 40697 | if( pPg ){ |
| 40698 | p->page.pBuf = pPg; |
| 40699 | p->page.pExtra = &p[1]; |
| 40700 | if( pCache->bPurgeable ){ |
| 40701 | pCache->pGroup->nCurrentPage++; |
| 40702 | } |
| 40703 | return p; |
| 40704 | } |
| 40705 | return 0; |
| 40706 | } |
| 40707 | |
| 40708 | /* |
| 40709 | ** Free a page object allocated by pcache1AllocPage(). |
| 40710 | ** |
| 40711 | ** The pointer is allowed to be NULL, which is prudent. But it turns out |
| 40712 | ** that the current implementation happens to never call this routine |
| 40713 | ** with a NULL pointer, so we mark the NULL test with ALWAYS(). |
| 40714 | */ |
| 40715 | static void pcache1FreePage(PgHdr1 *p){ |
| 40716 | if( ALWAYS(p) ){ |
| 40717 | PCache1 *pCache = p->pCache; |
| 40718 | assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); |
| 40719 | pcache1Free(p->page.pBuf); |
| 40720 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40721 | sqlite3_free(p); |
| 40722 | #endif |
| 40723 | if( pCache->bPurgeable ){ |
| 40724 | pCache->pGroup->nCurrentPage--; |
| 40725 | } |
| 40726 | } |
| 40727 | } |
| 40728 | |
| 40729 | /* |
| 40730 | ** Malloc function used by SQLite to obtain space from the buffer configured |
| @@ -40920,10 +41023,35 @@ | |
| 40920 | */ |
| 40921 | static int pcache1Init(void *NotUsed){ |
| 40922 | UNUSED_PARAMETER(NotUsed); |
| 40923 | assert( pcache1.isInit==0 ); |
| 40924 | memset(&pcache1, 0, sizeof(pcache1)); |
| 40925 | #if SQLITE_THREADSAFE |
| 40926 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 40927 | pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); |
| 40928 | pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); |
| 40929 | } |
| @@ -40955,52 +41083,65 @@ | |
| 40955 | static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ |
| 40956 | PCache1 *pCache; /* The newly created page cache */ |
| 40957 | PGroup *pGroup; /* The group the new page cache will belong to */ |
| 40958 | int sz; /* Bytes of memory required to allocate the new cache */ |
| 40959 | |
| 40960 | /* |
| 40961 | ** The separateCache variable is true if each PCache has its own private |
| 40962 | ** PGroup. In other words, separateCache is true for mode (1) where no |
| 40963 | ** mutexing is required. |
| 40964 | ** |
| 40965 | ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT |
| 40966 | ** |
| 40967 | ** * Always use a unified cache in single-threaded applications |
| 40968 | ** |
| 40969 | ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off) |
| 40970 | ** use separate caches (mode-1) |
| 40971 | */ |
| 40972 | #if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 |
| 40973 | const int separateCache = 0; |
| 40974 | #else |
| 40975 | int separateCache = sqlite3GlobalConfig.bCoreMutex>0; |
| 40976 | #endif |
| 40977 | |
| 40978 | assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); |
| 40979 | assert( szExtra < 300 ); |
| 40980 | |
| 40981 | sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; |
| 40982 | pCache = (PCache1 *)sqlite3MallocZero(sz); |
| 40983 | if( pCache ){ |
| 40984 | if( separateCache ){ |
| 40985 | pGroup = (PGroup*)&pCache[1]; |
| 40986 | pGroup->mxPinned = 10; |
| 40987 | }else{ |
| 40988 | pGroup = &pcache1.grp; |
| 40989 | } |
| 40990 | pCache->pGroup = pGroup; |
| 40991 | pCache->szPage = szPage; |
| 40992 | pCache->szExtra = szExtra; |
| 40993 | pCache->bPurgeable = (bPurgeable ? 1 : 0); |
| 40994 | pcache1EnterMutex(pGroup); |
| 40995 | pcache1ResizeHash(pCache); |
| 40996 | if( bPurgeable ){ |
| 40997 | pCache->nMin = 10; |
| 40998 | pGroup->nMinPage += pCache->nMin; |
| 40999 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41000 | } |
| 41001 | pcache1LeaveMutex(pGroup); |
| 41002 | if( pCache->nHash==0 ){ |
| 41003 | pcache1Destroy((sqlite3_pcache*)pCache); |
| 41004 | pCache = 0; |
| 41005 | } |
| 41006 | } |
| @@ -41090,30 +41231,21 @@ | |
| 41090 | |
| 41091 | if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); |
| 41092 | assert( pCache->nHash>0 && pCache->apHash ); |
| 41093 | |
| 41094 | /* Step 4. Try to recycle a page. */ |
| 41095 | if( pCache->bPurgeable && pGroup->pLruTail && ( |
| 41096 | (pCache->nPage+1>=pCache->nMax) |
| 41097 | || pGroup->nCurrentPage>=pGroup->nMaxPage |
| 41098 | || pcache1UnderMemoryPressure(pCache) |
| 41099 | )){ |
| 41100 | PCache1 *pOther; |
| 41101 | pPage = pGroup->pLruTail; |
| 41102 | assert( pPage->isPinned==0 ); |
| 41103 | pcache1RemoveFromHash(pPage, 0); |
| 41104 | pcache1PinPage(pPage); |
| 41105 | pOther = pPage->pCache; |
| 41106 | |
| 41107 | /* We want to verify that szPage and szExtra are the same for pOther |
| 41108 | ** and pCache. Assert that we can verify this by comparing sums. */ |
| 41109 | assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 ); |
| 41110 | assert( pCache->szExtra<512 ); |
| 41111 | assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 ); |
| 41112 | assert( pOther->szExtra<512 ); |
| 41113 | |
| 41114 | if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){ |
| 41115 | pcache1FreePage(pPage); |
| 41116 | pPage = 0; |
| 41117 | }else{ |
| 41118 | pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); |
| 41119 | } |
| @@ -41385,10 +41517,11 @@ | |
| 41385 | assert( pGroup->nMinPage >= pCache->nMin ); |
| 41386 | pGroup->nMinPage -= pCache->nMin; |
| 41387 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41388 | pcache1EnforceMaxPage(pGroup); |
| 41389 | pcache1LeaveMutex(pGroup); |
| 41390 | sqlite3_free(pCache->apHash); |
| 41391 | sqlite3_free(pCache); |
| 41392 | } |
| 41393 | |
| 41394 | /* |
| @@ -41440,11 +41573,11 @@ | |
| 41440 | */ |
| 41441 | SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ |
| 41442 | int nFree = 0; |
| 41443 | assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); |
| 41444 | assert( sqlite3_mutex_notheld(pcache1.mutex) ); |
| 41445 | if( pcache1.pStart==0 ){ |
| 41446 | PgHdr1 *p; |
| 41447 | pcache1EnterMutex(&pcache1.grp); |
| 41448 | while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){ |
| 41449 | nFree += pcache1MemSize(p->page.pBuf); |
| 41450 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| @@ -62542,10 +62675,11 @@ | |
| 62542 | u32 *heap = 0; /* Min-heap used for checking cell coverage */ |
| 62543 | u32 x, prev = 0; /* Next and previous entry on the min-heap */ |
| 62544 | const char *saved_zPfx = pCheck->zPfx; |
| 62545 | int saved_v1 = pCheck->v1; |
| 62546 | int saved_v2 = pCheck->v2; |
| 62547 | |
| 62548 | /* Check that the page exists |
| 62549 | */ |
| 62550 | pBt = pCheck->pBt; |
| 62551 | usableSize = pBt->usableSize; |
| @@ -62559,10 +62693,11 @@ | |
| 62559 | goto end_of_check; |
| 62560 | } |
| 62561 | |
| 62562 | /* Clear MemPage.isInit to make sure the corruption detection code in |
| 62563 | ** btreeInitPage() is executed. */ |
| 62564 | pPage->isInit = 0; |
| 62565 | if( (rc = btreeInitPage(pPage))!=0 ){ |
| 62566 | assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ |
| 62567 | checkAppendMsg(pCheck, |
| 62568 | "btreeInitPage() returns error code %d", rc); |
| @@ -62700,11 +62835,11 @@ | |
| 62700 | while( i>0 ){ |
| 62701 | int size, j; |
| 62702 | assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */ |
| 62703 | size = get2byte(&data[i+2]); |
| 62704 | assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */ |
| 62705 | btreeHeapInsert(heap, (i<<16)|(i+size-1)); |
| 62706 | /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a |
| 62707 | ** big-endian integer which is the offset in the b-tree page of the next |
| 62708 | ** freeblock in the chain, or zero if the freeblock is the last on the |
| 62709 | ** chain. */ |
| 62710 | j = get2byte(&data[i]); |
| @@ -62751,10 +62886,11 @@ | |
| 62751 | nFrag, data[hdr+7], iPage); |
| 62752 | } |
| 62753 | } |
| 62754 | |
| 62755 | end_of_check: |
| 62756 | releasePage(pPage); |
| 62757 | pCheck->zPfx = saved_zPfx; |
| 62758 | pCheck->v1 = saved_v1; |
| 62759 | pCheck->v2 = saved_v2; |
| 62760 | return depth+1; |
| @@ -69020,10 +69156,11 @@ | |
| 69020 | ** to ignore the compiler warnings and leave this variable uninitialized. |
| 69021 | */ |
| 69022 | /* mem1.u.i = 0; // not needed, here to silence compiler warning */ |
| 69023 | |
| 69024 | idx1 = getVarint32(aKey1, szHdr1); |
| 69025 | d1 = szHdr1; |
| 69026 | assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB ); |
| 69027 | assert( pKeyInfo->aSortOrder!=0 ); |
| 69028 | assert( pKeyInfo->nField>0 ); |
| 69029 | assert( idx1<=szHdr1 || CORRUPT_DB ); |
| @@ -109379,14 +109516,18 @@ | |
| 109379 | sqlite3VdbeResolveLabel(v, addrCont); |
| 109380 | |
| 109381 | /* Execute the recursive SELECT taking the single row in Current as |
| 109382 | ** the value for the recursive-table. Store the results in the Queue. |
| 109383 | */ |
| 109384 | p->pPrior = 0; |
| 109385 | sqlite3Select(pParse, p, &destQueue); |
| 109386 | assert( p->pPrior==0 ); |
| 109387 | p->pPrior = pSetup; |
| 109388 | |
| 109389 | /* Keep running the loop until the Queue is empty */ |
| 109390 | sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); |
| 109391 | sqlite3VdbeResolveLabel(v, addrBreak); |
| 109392 | |
| 109393 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -325,11 +325,11 @@ | |
| 325 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 326 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 327 | */ |
| 328 | #define SQLITE_VERSION "3.8.11" |
| 329 | #define SQLITE_VERSION_NUMBER 3008011 |
| 330 | #define SQLITE_SOURCE_ID "2015-07-08 16:22:42 5348ffc3fda5168c1e9e14aa88b0c6aedbda7c94" |
| 331 | |
| 332 | /* |
| 333 | ** CAPI3REF: Run-Time Library Version Numbers |
| 334 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 335 | ** |
| @@ -6503,10 +6503,13 @@ | |
| 6503 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6504 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6505 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6506 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6507 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6508 | #define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ |
| 6509 | #define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ |
| 6510 | #define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ |
| 6511 | |
| 6512 | /* |
| 6513 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6514 | ** METHOD: sqlite3 |
| 6515 | ** |
| @@ -8941,10 +8944,20 @@ | |
| 8944 | #if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS |
| 8945 | # undef SQLITE_MAX_WORKER_THREADS |
| 8946 | # define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS |
| 8947 | #endif |
| 8948 | |
| 8949 | /* |
| 8950 | ** The default initial allocation for the pagecache when using separate |
| 8951 | ** pagecaches for each database connection. A positive number is the |
| 8952 | ** number of pages. A negative number N translations means that a buffer |
| 8953 | ** of -1024*N bytes is allocated and used for as many pages as it will hold. |
| 8954 | */ |
| 8955 | #ifndef SQLITE_DEFAULT_PCACHE_INITSZ |
| 8956 | # define SQLITE_DEFAULT_PCACHE_INITSZ 100 |
| 8957 | #endif |
| 8958 | |
| 8959 | |
| 8960 | /* |
| 8961 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 8962 | ** ourselves. |
| 8963 | */ |
| @@ -14043,11 +14056,11 @@ | |
| 14056 | (void*)0, /* pScratch */ |
| 14057 | 0, /* szScratch */ |
| 14058 | 0, /* nScratch */ |
| 14059 | (void*)0, /* pPage */ |
| 14060 | 0, /* szPage */ |
| 14061 | SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */ |
| 14062 | 0, /* mxParserStack */ |
| 14063 | 0, /* sharedCacheEnabled */ |
| 14064 | SQLITE_SORTER_PMASZ, /* szPma */ |
| 14065 | /* All the rest should always be initialized to zero */ |
| 14066 | 0, /* isInit */ |
| @@ -19454,11 +19467,11 @@ | |
| 19467 | ** The sqlite3_mutex_alloc() routine allocates a new |
| 19468 | ** mutex and returns a pointer to it. If it returns NULL |
| 19469 | ** that means that a mutex could not be allocated. |
| 19470 | */ |
| 19471 | static sqlite3_mutex *debugMutexAlloc(int id){ |
| 19472 | static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1]; |
| 19473 | sqlite3_debug_mutex *pNew = 0; |
| 19474 | switch( id ){ |
| 19475 | case SQLITE_MUTEX_FAST: |
| 19476 | case SQLITE_MUTEX_RECURSIVE: { |
| 19477 | pNew = sqlite3Malloc(sizeof(*pNew)); |
| @@ -19669,10 +19682,13 @@ | |
| 19682 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 19683 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 19684 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 19685 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 19686 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 19687 | ** <li> SQLITE_MUTEX_STATIC_VFS1 |
| 19688 | ** <li> SQLITE_MUTEX_STATIC_VFS2 |
| 19689 | ** <li> SQLITE_MUTEX_STATIC_VFS3 |
| 19690 | ** </ul> |
| 19691 | ** |
| 19692 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 19693 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 19694 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -19697,10 +19713,13 @@ | |
| 19713 | ** mutex types, the same mutex is returned on every call that has |
| 19714 | ** the same type number. |
| 19715 | */ |
| 19716 | static sqlite3_mutex *pthreadMutexAlloc(int iType){ |
| 19717 | static sqlite3_mutex staticMutexes[] = { |
| 19718 | SQLITE3_MUTEX_INITIALIZER, |
| 19719 | SQLITE3_MUTEX_INITIALIZER, |
| 19720 | SQLITE3_MUTEX_INITIALIZER, |
| 19721 | SQLITE3_MUTEX_INITIALIZER, |
| 19722 | SQLITE3_MUTEX_INITIALIZER, |
| 19723 | SQLITE3_MUTEX_INITIALIZER, |
| 19724 | SQLITE3_MUTEX_INITIALIZER, |
| 19725 | SQLITE3_MUTEX_INITIALIZER, |
| @@ -20311,10 +20330,13 @@ | |
| 20330 | SQLITE3_MUTEX_INITIALIZER, |
| 20331 | SQLITE3_MUTEX_INITIALIZER, |
| 20332 | SQLITE3_MUTEX_INITIALIZER, |
| 20333 | SQLITE3_MUTEX_INITIALIZER, |
| 20334 | SQLITE3_MUTEX_INITIALIZER, |
| 20335 | SQLITE3_MUTEX_INITIALIZER, |
| 20336 | SQLITE3_MUTEX_INITIALIZER, |
| 20337 | SQLITE3_MUTEX_INITIALIZER, |
| 20338 | SQLITE3_MUTEX_INITIALIZER |
| 20339 | }; |
| 20340 | |
| 20341 | static int winMutex_isInit = 0; |
| 20342 | static int winMutex_isNt = -1; /* <0 means "need to query" */ |
| @@ -20382,10 +20404,13 @@ | |
| 20404 | ** <li> SQLITE_MUTEX_STATIC_LRU |
| 20405 | ** <li> SQLITE_MUTEX_STATIC_PMEM |
| 20406 | ** <li> SQLITE_MUTEX_STATIC_APP1 |
| 20407 | ** <li> SQLITE_MUTEX_STATIC_APP2 |
| 20408 | ** <li> SQLITE_MUTEX_STATIC_APP3 |
| 20409 | ** <li> SQLITE_MUTEX_STATIC_VFS1 |
| 20410 | ** <li> SQLITE_MUTEX_STATIC_VFS2 |
| 20411 | ** <li> SQLITE_MUTEX_STATIC_VFS3 |
| 20412 | ** </ul> |
| 20413 | ** |
| 20414 | ** The first two constants cause sqlite3_mutex_alloc() to create |
| 20415 | ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
| 20416 | ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
| @@ -20793,14 +20818,13 @@ | |
| 20818 | sqlite3GlobalConfig.pScratch = 0; |
| 20819 | sqlite3GlobalConfig.szScratch = 0; |
| 20820 | sqlite3GlobalConfig.nScratch = 0; |
| 20821 | } |
| 20822 | if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 |
| 20823 | || sqlite3GlobalConfig.nPage<=0 ){ |
| 20824 | sqlite3GlobalConfig.pPage = 0; |
| 20825 | sqlite3GlobalConfig.szPage = 0; |
| 20826 | } |
| 20827 | rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); |
| 20828 | if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); |
| 20829 | return rc; |
| 20830 | } |
| @@ -26522,18 +26546,18 @@ | |
| 26546 | ** unixEnterMutex() |
| 26547 | ** assert( unixMutexHeld() ); |
| 26548 | ** unixEnterLeave() |
| 26549 | */ |
| 26550 | static void unixEnterMutex(void){ |
| 26551 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 26552 | } |
| 26553 | static void unixLeaveMutex(void){ |
| 26554 | sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 26555 | } |
| 26556 | #ifdef SQLITE_DEBUG |
| 26557 | static int unixMutexHeld(void) { |
| 26558 | return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 26559 | } |
| 26560 | #endif |
| 26561 | |
| 26562 | |
| 26563 | #ifdef SQLITE_HAVE_OS_TRACE |
| @@ -37047,18 +37071,18 @@ | |
| 37071 | ** winShmEnterMutex() |
| 37072 | ** assert( winShmMutexHeld() ); |
| 37073 | ** winShmLeaveMutex() |
| 37074 | */ |
| 37075 | static void winShmEnterMutex(void){ |
| 37076 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 37077 | } |
| 37078 | static void winShmLeaveMutex(void){ |
| 37079 | sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 37080 | } |
| 37081 | #ifndef NDEBUG |
| 37082 | static int winShmMutexHeld(void) { |
| 37083 | return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
| 37084 | } |
| 37085 | #endif |
| 37086 | |
| 37087 | /* |
| 37088 | ** Object used to represent a single file opened and mmapped to provide |
| @@ -40399,12 +40423,75 @@ | |
| 40423 | ** This file implements the default page cache implementation (the |
| 40424 | ** sqlite3_pcache interface). It also contains part of the implementation |
| 40425 | ** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. |
| 40426 | ** If the default page cache implementation is overridden, then neither of |
| 40427 | ** these two features are available. |
| 40428 | ** |
| 40429 | ** A Page cache line looks like this: |
| 40430 | ** |
| 40431 | ** ------------------------------------------------------------- |
| 40432 | ** | database page content | PgHdr1 | MemPage | PgHdr | |
| 40433 | ** ------------------------------------------------------------- |
| 40434 | ** |
| 40435 | ** The database page content is up front (so that buffer overreads tend to |
| 40436 | ** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage |
| 40437 | ** is the extension added by the btree.c module containing information such |
| 40438 | ** as the database page number and how that database page is used. PgHdr |
| 40439 | ** is added by the pcache.c layer and contains information used to keep track |
| 40440 | ** of which pages are "dirty". PgHdr1 is an extension added by this |
| 40441 | ** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page. |
| 40442 | ** PgHdr1 contains information needed to look up a page by its page number. |
| 40443 | ** The superclass sqlite3_pcache_page.pBuf points to the start of the |
| 40444 | ** database page content and sqlite3_pcache_page.pExtra points to PgHdr. |
| 40445 | ** |
| 40446 | ** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at |
| 40447 | ** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The |
| 40448 | ** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this |
| 40449 | ** size can vary according to architecture, compile-time options, and |
| 40450 | ** SQLite library version number. |
| 40451 | ** |
| 40452 | ** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained |
| 40453 | ** using a separate memory allocation from the database page content. This |
| 40454 | ** seeks to overcome the "clownshoe" problem (also called "internal |
| 40455 | ** fragmentation" in academic literature) of allocating a few bytes more |
| 40456 | ** than a power of two with the memory allocator rounding up to the next |
| 40457 | ** power of two, and leaving the rounded-up space unused. |
| 40458 | ** |
| 40459 | ** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates |
| 40460 | ** with this module. Information is passed back and forth as PgHdr1 pointers. |
| 40461 | ** |
| 40462 | ** The pcache.c and pager.c modules deal pointers to PgHdr objects. |
| 40463 | ** The btree.c module deals with pointers to MemPage objects. |
| 40464 | ** |
| 40465 | ** SOURCE OF PAGE CACHE MEMORY: |
| 40466 | ** |
| 40467 | ** Memory for a page might come from any of three sources: |
| 40468 | ** |
| 40469 | ** (1) The general-purpose memory allocator - sqlite3Malloc() |
| 40470 | ** (2) Global page-cache memory provided using sqlite3_config() with |
| 40471 | ** SQLITE_CONFIG_PAGECACHE. |
| 40472 | ** (3) PCache-local bulk allocation. |
| 40473 | ** |
| 40474 | ** The third case is a chunk of heap memory (defaulting to 100 pages worth) |
| 40475 | ** that is allocated when the page cache is created. The size of the local |
| 40476 | ** bulk allocation can be adjusted using |
| 40477 | ** |
| 40478 | ** sqlite3_config(SQLITE_CONFIG_PCACHE, 0, 0, N). |
| 40479 | ** |
| 40480 | ** If N is positive, then N pages worth of memory are allocated using a single |
| 40481 | ** sqlite3Malloc() call and that memory is used for the first N pages allocated. |
| 40482 | ** Or if N is negative, then -1024*N bytes of memory are allocated and used |
| 40483 | ** for as many pages as can be accomodated. |
| 40484 | ** |
| 40485 | ** Only one of (2) or (3) can be used. Once the memory available to (2) or |
| 40486 | ** (3) is exhausted, subsequent allocations fail over to the general-purpose |
| 40487 | ** memory allocator (1). |
| 40488 | ** |
| 40489 | ** Earlier versions of SQLite used only methods (1) and (2). But experiments |
| 40490 | ** show that method (3) with N==100 provides about a 5% performance boost for |
| 40491 | ** common workloads. |
| 40492 | */ |
| 40493 | |
| 40494 | typedef struct PCache1 PCache1; |
| 40495 | typedef struct PgHdr1 PgHdr1; |
| 40496 | typedef struct PgFreeslot PgFreeslot; |
| 40497 | typedef struct PGroup PGroup; |
| @@ -40453,12 +40540,13 @@ | |
| 40540 | ** flag (bPurgeable) are set when the cache is created. nMax may be |
| 40541 | ** modified at any time by a call to the pcache1Cachesize() method. |
| 40542 | ** The PGroup mutex must be held when accessing nMax. |
| 40543 | */ |
| 40544 | PGroup *pGroup; /* PGroup this cache belongs to */ |
| 40545 | int szPage; /* Size of database content section */ |
| 40546 | int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ |
| 40547 | int szAlloc; /* Total size of one pcache line */ |
| 40548 | int bPurgeable; /* True if cache is purgeable */ |
| 40549 | unsigned int nMin; /* Minimum number of pages reserved */ |
| 40550 | unsigned int nMax; /* Configured "cache_size" value */ |
| 40551 | unsigned int n90pct; /* nMax*9/10 */ |
| 40552 | unsigned int iMaxKey; /* Largest key seen since xTruncate() */ |
| @@ -40468,10 +40556,12 @@ | |
| 40556 | */ |
| 40557 | unsigned int nRecyclable; /* Number of pages in the LRU list */ |
| 40558 | unsigned int nPage; /* Total number of pages in apHash */ |
| 40559 | unsigned int nHash; /* Number of slots in apHash[] */ |
| 40560 | PgHdr1 **apHash; /* Hash table for fast lookup by key */ |
| 40561 | PgHdr1 *pFree; /* List of unused pcache-local pages */ |
| 40562 | void *pBulk; /* Bulk memory used by pcache-local */ |
| 40563 | }; |
| 40564 | |
| 40565 | /* |
| 40566 | ** Each cache entry is represented by an instance of the following |
| 40567 | ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of |
| @@ -40480,19 +40570,20 @@ | |
| 40570 | */ |
| 40571 | struct PgHdr1 { |
| 40572 | sqlite3_pcache_page page; |
| 40573 | unsigned int iKey; /* Key value (page number) */ |
| 40574 | u8 isPinned; /* Page in use, not on the LRU list */ |
| 40575 | u8 isBulkLocal; /* This page from bulk local storage */ |
| 40576 | PgHdr1 *pNext; /* Next in hash table chain */ |
| 40577 | PCache1 *pCache; /* Cache that currently owns this page */ |
| 40578 | PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ |
| 40579 | PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ |
| 40580 | }; |
| 40581 | |
| 40582 | /* |
| 40583 | ** Free slots in the allocator used to divide up the global page cache |
| 40584 | ** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. |
| 40585 | */ |
| 40586 | struct PgFreeslot { |
| 40587 | PgFreeslot *pNext; /* Next free slot */ |
| 40588 | }; |
| 40589 | |
| @@ -40506,14 +40597,15 @@ | |
| 40597 | ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all |
| 40598 | ** fixed at sqlite3_initialize() time and do not require mutex protection. |
| 40599 | ** The nFreeSlot and pFree values do require mutex protection. |
| 40600 | */ |
| 40601 | int isInit; /* True if initialized */ |
| 40602 | int separateCache; /* Use a new PGroup for each PCache */ |
| 40603 | int szSlot; /* Size of each free slot */ |
| 40604 | int nSlot; /* The number of pcache slots */ |
| 40605 | int nReserve; /* Try to keep nFreeSlot above this */ |
| 40606 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 40607 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 40608 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 40609 | PgFreeslot *pFree; /* Free page blocks */ |
| 40610 | int nFreeSlot; /* Number of unused pcache slots */ |
| 40611 | /* The following value requires a mutex to change. We skip the mutex on |
| @@ -40556,10 +40648,11 @@ | |
| 40648 | ** to be serialized already. There is no need for further mutexing. |
| 40649 | */ |
| 40650 | SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ |
| 40651 | if( pcache1.isInit ){ |
| 40652 | PgFreeslot *p; |
| 40653 | if( pBuf==0 ) sz = n = 0; |
| 40654 | sz = ROUNDDOWN8(sz); |
| 40655 | pcache1.szSlot = sz; |
| 40656 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 40657 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 40658 | pcache1.pStart = pBuf; |
| @@ -40620,13 +40713,13 @@ | |
| 40713 | } |
| 40714 | |
| 40715 | /* |
| 40716 | ** Free an allocated buffer obtained from pcache1Alloc(). |
| 40717 | */ |
| 40718 | static void pcache1Free(void *p){ |
| 40719 | int nFreed = 0; |
| 40720 | if( p==0 ) return; |
| 40721 | if( p>=pcache1.pStart && p<pcache1.pEnd ){ |
| 40722 | PgFreeslot *pSlot; |
| 40723 | sqlite3_mutex_enter(pcache1.mutex); |
| 40724 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 40725 | pSlot = (PgFreeslot*)p; |
| @@ -40637,19 +40730,18 @@ | |
| 40730 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 40731 | sqlite3_mutex_leave(pcache1.mutex); |
| 40732 | }else{ |
| 40733 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 40734 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| 40735 | #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS |
| 40736 | nFreed = sqlite3MallocSize(p); |
| 40737 | sqlite3_mutex_enter(pcache1.mutex); |
| 40738 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); |
| 40739 | sqlite3_mutex_leave(pcache1.mutex); |
| 40740 | #endif |
| 40741 | sqlite3_free(p); |
| 40742 | } |
| 40743 | } |
| 40744 | |
| 40745 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40746 | /* |
| 40747 | ** Return the size of a pcache allocation |
| @@ -40673,58 +40765,69 @@ | |
| 40765 | */ |
| 40766 | static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ |
| 40767 | PgHdr1 *p = 0; |
| 40768 | void *pPg; |
| 40769 | |
| 40770 | assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
| 40771 | if( pCache->pFree ){ |
| 40772 | p = pCache->pFree; |
| 40773 | pCache->pFree = p->pNext; |
| 40774 | p->pNext = 0; |
| 40775 | }else{ |
| 40776 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40777 | /* The group mutex must be released before pcache1Alloc() is called. This |
| 40778 | ** is because it might call sqlite3_release_memory(), which assumes that |
| 40779 | ** this mutex is not held. */ |
| 40780 | assert( pcache1.separateCache==0 ); |
| 40781 | assert( pCache->pGroup==&pcache1.grp ); |
| 40782 | pcache1LeaveMutex(pCache->pGroup); |
| 40783 | #endif |
| 40784 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40785 | pPg = pcache1Alloc(pCache->szPage); |
| 40786 | p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); |
| 40787 | if( !pPg || !p ){ |
| 40788 | pcache1Free(pPg); |
| 40789 | sqlite3_free(p); |
| 40790 | pPg = 0; |
| 40791 | } |
| 40792 | #else |
| 40793 | pPg = pcache1Alloc(pCache->szAlloc); |
| 40794 | p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
| 40795 | #endif |
| 40796 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40797 | pcache1EnterMutex(pCache->pGroup); |
| 40798 | #endif |
| 40799 | if( pPg==0 ) return 0; |
| 40800 | p->page.pBuf = pPg; |
| 40801 | p->page.pExtra = &p[1]; |
| 40802 | p->isBulkLocal = 0; |
| 40803 | } |
| 40804 | if( pCache->bPurgeable ){ |
| 40805 | pCache->pGroup->nCurrentPage++; |
| 40806 | } |
| 40807 | return p; |
| 40808 | } |
| 40809 | |
| 40810 | /* |
| 40811 | ** Free a page object allocated by pcache1AllocPage(). |
| 40812 | */ |
| 40813 | static void pcache1FreePage(PgHdr1 *p){ |
| 40814 | PCache1 *pCache; |
| 40815 | assert( p!=0 ); |
| 40816 | pCache = p->pCache; |
| 40817 | assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); |
| 40818 | if( p->isBulkLocal ){ |
| 40819 | p->pNext = pCache->pFree; |
| 40820 | pCache->pFree = p; |
| 40821 | }else{ |
| 40822 | pcache1Free(p->page.pBuf); |
| 40823 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| 40824 | sqlite3_free(p); |
| 40825 | #endif |
| 40826 | } |
| 40827 | if( pCache->bPurgeable ){ |
| 40828 | pCache->pGroup->nCurrentPage--; |
| 40829 | } |
| 40830 | } |
| 40831 | |
| 40832 | /* |
| 40833 | ** Malloc function used by SQLite to obtain space from the buffer configured |
| @@ -40920,10 +41023,35 @@ | |
| 41023 | */ |
| 41024 | static int pcache1Init(void *NotUsed){ |
| 41025 | UNUSED_PARAMETER(NotUsed); |
| 41026 | assert( pcache1.isInit==0 ); |
| 41027 | memset(&pcache1, 0, sizeof(pcache1)); |
| 41028 | |
| 41029 | |
| 41030 | /* |
| 41031 | ** The pcache1.separateCache variable is true if each PCache has its own |
| 41032 | ** private PGroup (mode-1). pcache1.separateCache is false if the single |
| 41033 | ** PGroup in pcache1.grp is used for all page caches (mode-2). |
| 41034 | ** |
| 41035 | ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT |
| 41036 | ** |
| 41037 | ** * Use a unified cache in single-threaded applications that have |
| 41038 | ** configured a start-time buffer for use as page-cache memory using |
| 41039 | ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL |
| 41040 | ** pBuf argument. |
| 41041 | ** |
| 41042 | ** * Otherwise use separate caches (mode-1) |
| 41043 | */ |
| 41044 | #if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) |
| 41045 | pcache1.separateCache = 0; |
| 41046 | #elif SQLITE_THREADSAFE |
| 41047 | pcache1.separateCache = sqlite3GlobalConfig.pPage==0 |
| 41048 | || sqlite3GlobalConfig.bCoreMutex>0; |
| 41049 | #else |
| 41050 | pcache1.separateCache = sqlite3GlobalConfig.pPage==0; |
| 41051 | #endif |
| 41052 | |
| 41053 | #if SQLITE_THREADSAFE |
| 41054 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 41055 | pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); |
| 41056 | pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); |
| 41057 | } |
| @@ -40955,52 +41083,65 @@ | |
| 41083 | static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ |
| 41084 | PCache1 *pCache; /* The newly created page cache */ |
| 41085 | PGroup *pGroup; /* The group the new page cache will belong to */ |
| 41086 | int sz; /* Bytes of memory required to allocate the new cache */ |
| 41087 | |
| 41088 | assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); |
| 41089 | assert( szExtra < 300 ); |
| 41090 | |
| 41091 | sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; |
| 41092 | pCache = (PCache1 *)sqlite3MallocZero(sz); |
| 41093 | if( pCache ){ |
| 41094 | if( pcache1.separateCache ){ |
| 41095 | pGroup = (PGroup*)&pCache[1]; |
| 41096 | pGroup->mxPinned = 10; |
| 41097 | }else{ |
| 41098 | pGroup = &pcache1.grp; |
| 41099 | } |
| 41100 | pCache->pGroup = pGroup; |
| 41101 | pCache->szPage = szPage; |
| 41102 | pCache->szExtra = szExtra; |
| 41103 | pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); |
| 41104 | pCache->bPurgeable = (bPurgeable ? 1 : 0); |
| 41105 | pcache1EnterMutex(pGroup); |
| 41106 | pcache1ResizeHash(pCache); |
| 41107 | if( bPurgeable ){ |
| 41108 | pCache->nMin = 10; |
| 41109 | pGroup->nMinPage += pCache->nMin; |
| 41110 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41111 | } |
| 41112 | pcache1LeaveMutex(pGroup); |
| 41113 | /* Try to initialize the local bulk pagecache line allocation if using |
| 41114 | ** separate caches and if nPage!=0 */ |
| 41115 | if( pcache1.separateCache |
| 41116 | && sqlite3GlobalConfig.nPage!=0 |
| 41117 | && sqlite3GlobalConfig.pPage==0 |
| 41118 | ){ |
| 41119 | int szBulk; |
| 41120 | char *zBulk; |
| 41121 | sqlite3BeginBenignMalloc(); |
| 41122 | if( sqlite3GlobalConfig.nPage>0 ){ |
| 41123 | szBulk = pCache->szAlloc * sqlite3GlobalConfig.nPage; |
| 41124 | }else{ |
| 41125 | szBulk = -1024*sqlite3GlobalConfig.nPage; |
| 41126 | } |
| 41127 | zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); |
| 41128 | sqlite3EndBenignMalloc(); |
| 41129 | if( zBulk ){ |
| 41130 | int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; |
| 41131 | int i; |
| 41132 | for(i=0; i<nBulk; i++){ |
| 41133 | PgHdr1 *pX = (PgHdr1*)&zBulk[szPage]; |
| 41134 | pX->page.pBuf = zBulk; |
| 41135 | pX->page.pExtra = &pX[1]; |
| 41136 | pX->isBulkLocal = 1; |
| 41137 | pX->pNext = pCache->pFree; |
| 41138 | pCache->pFree = pX; |
| 41139 | zBulk += pCache->szAlloc; |
| 41140 | } |
| 41141 | } |
| 41142 | } |
| 41143 | if( pCache->nHash==0 ){ |
| 41144 | pcache1Destroy((sqlite3_pcache*)pCache); |
| 41145 | pCache = 0; |
| 41146 | } |
| 41147 | } |
| @@ -41090,30 +41231,21 @@ | |
| 41231 | |
| 41232 | if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); |
| 41233 | assert( pCache->nHash>0 && pCache->apHash ); |
| 41234 | |
| 41235 | /* Step 4. Try to recycle a page. */ |
| 41236 | if( pCache->bPurgeable |
| 41237 | && pGroup->pLruTail |
| 41238 | && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) |
| 41239 | ){ |
| 41240 | PCache1 *pOther; |
| 41241 | pPage = pGroup->pLruTail; |
| 41242 | assert( pPage->isPinned==0 ); |
| 41243 | pcache1RemoveFromHash(pPage, 0); |
| 41244 | pcache1PinPage(pPage); |
| 41245 | pOther = pPage->pCache; |
| 41246 | if( pOther->szAlloc != pCache->szAlloc ){ |
| 41247 | pcache1FreePage(pPage); |
| 41248 | pPage = 0; |
| 41249 | }else{ |
| 41250 | pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); |
| 41251 | } |
| @@ -41385,10 +41517,11 @@ | |
| 41517 | assert( pGroup->nMinPage >= pCache->nMin ); |
| 41518 | pGroup->nMinPage -= pCache->nMin; |
| 41519 | pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
| 41520 | pcache1EnforceMaxPage(pGroup); |
| 41521 | pcache1LeaveMutex(pGroup); |
| 41522 | sqlite3_free(pCache->pBulk); |
| 41523 | sqlite3_free(pCache->apHash); |
| 41524 | sqlite3_free(pCache); |
| 41525 | } |
| 41526 | |
| 41527 | /* |
| @@ -41440,11 +41573,11 @@ | |
| 41573 | */ |
| 41574 | SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ |
| 41575 | int nFree = 0; |
| 41576 | assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); |
| 41577 | assert( sqlite3_mutex_notheld(pcache1.mutex) ); |
| 41578 | if( sqlite3GlobalConfig.nPage==0 ){ |
| 41579 | PgHdr1 *p; |
| 41580 | pcache1EnterMutex(&pcache1.grp); |
| 41581 | while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){ |
| 41582 | nFree += pcache1MemSize(p->page.pBuf); |
| 41583 | #ifdef SQLITE_PCACHE_SEPARATE_HEADER |
| @@ -62542,10 +62675,11 @@ | |
| 62675 | u32 *heap = 0; /* Min-heap used for checking cell coverage */ |
| 62676 | u32 x, prev = 0; /* Next and previous entry on the min-heap */ |
| 62677 | const char *saved_zPfx = pCheck->zPfx; |
| 62678 | int saved_v1 = pCheck->v1; |
| 62679 | int saved_v2 = pCheck->v2; |
| 62680 | u8 savedIsInit; |
| 62681 | |
| 62682 | /* Check that the page exists |
| 62683 | */ |
| 62684 | pBt = pCheck->pBt; |
| 62685 | usableSize = pBt->usableSize; |
| @@ -62559,10 +62693,11 @@ | |
| 62693 | goto end_of_check; |
| 62694 | } |
| 62695 | |
| 62696 | /* Clear MemPage.isInit to make sure the corruption detection code in |
| 62697 | ** btreeInitPage() is executed. */ |
| 62698 | savedIsInit = pPage->isInit; |
| 62699 | pPage->isInit = 0; |
| 62700 | if( (rc = btreeInitPage(pPage))!=0 ){ |
| 62701 | assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ |
| 62702 | checkAppendMsg(pCheck, |
| 62703 | "btreeInitPage() returns error code %d", rc); |
| @@ -62700,11 +62835,11 @@ | |
| 62835 | while( i>0 ){ |
| 62836 | int size, j; |
| 62837 | assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */ |
| 62838 | size = get2byte(&data[i+2]); |
| 62839 | assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */ |
| 62840 | btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); |
| 62841 | /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a |
| 62842 | ** big-endian integer which is the offset in the b-tree page of the next |
| 62843 | ** freeblock in the chain, or zero if the freeblock is the last on the |
| 62844 | ** chain. */ |
| 62845 | j = get2byte(&data[i]); |
| @@ -62751,10 +62886,11 @@ | |
| 62886 | nFrag, data[hdr+7], iPage); |
| 62887 | } |
| 62888 | } |
| 62889 | |
| 62890 | end_of_check: |
| 62891 | if( !doCoverageCheck ) pPage->isInit = savedIsInit; |
| 62892 | releasePage(pPage); |
| 62893 | pCheck->zPfx = saved_zPfx; |
| 62894 | pCheck->v1 = saved_v1; |
| 62895 | pCheck->v2 = saved_v2; |
| 62896 | return depth+1; |
| @@ -69020,10 +69156,11 @@ | |
| 69156 | ** to ignore the compiler warnings and leave this variable uninitialized. |
| 69157 | */ |
| 69158 | /* mem1.u.i = 0; // not needed, here to silence compiler warning */ |
| 69159 | |
| 69160 | idx1 = getVarint32(aKey1, szHdr1); |
| 69161 | if( szHdr1>98307 ) return SQLITE_CORRUPT; |
| 69162 | d1 = szHdr1; |
| 69163 | assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB ); |
| 69164 | assert( pKeyInfo->aSortOrder!=0 ); |
| 69165 | assert( pKeyInfo->nField>0 ); |
| 69166 | assert( idx1<=szHdr1 || CORRUPT_DB ); |
| @@ -109379,14 +109516,18 @@ | |
| 109516 | sqlite3VdbeResolveLabel(v, addrCont); |
| 109517 | |
| 109518 | /* Execute the recursive SELECT taking the single row in Current as |
| 109519 | ** the value for the recursive-table. Store the results in the Queue. |
| 109520 | */ |
| 109521 | if( p->selFlags & SF_Aggregate ){ |
| 109522 | sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported"); |
| 109523 | }else{ |
| 109524 | p->pPrior = 0; |
| 109525 | sqlite3Select(pParse, p, &destQueue); |
| 109526 | assert( p->pPrior==0 ); |
| 109527 | p->pPrior = pSetup; |
| 109528 | } |
| 109529 | |
| 109530 | /* Keep running the loop until the Queue is empty */ |
| 109531 | sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); |
| 109532 | sqlite3VdbeResolveLabel(v, addrBreak); |
| 109533 | |
| 109534 |
+4
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -111,11 +111,11 @@ | ||
| 111 | 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | 113 | */ |
| 114 | 114 | #define SQLITE_VERSION "3.8.11" |
| 115 | 115 | #define SQLITE_VERSION_NUMBER 3008011 |
| 116 | -#define SQLITE_SOURCE_ID "2015-07-03 17:54:49 030f60a7ba171650ce8c0ac32dc166eab80aca32" | |
| 116 | +#define SQLITE_SOURCE_ID "2015-07-08 16:22:42 5348ffc3fda5168c1e9e14aa88b0c6aedbda7c94" | |
| 117 | 117 | |
| 118 | 118 | /* |
| 119 | 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | 121 | ** |
| @@ -6289,10 +6289,13 @@ | ||
| 6289 | 6289 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6290 | 6290 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6291 | 6291 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6292 | 6292 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6293 | 6293 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6294 | +#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ | |
| 6295 | +#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ | |
| 6296 | +#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ | |
| 6294 | 6297 | |
| 6295 | 6298 | /* |
| 6296 | 6299 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6297 | 6300 | ** METHOD: sqlite3 |
| 6298 | 6301 | ** |
| 6299 | 6302 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -111,11 +111,11 @@ | |
| 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | */ |
| 114 | #define SQLITE_VERSION "3.8.11" |
| 115 | #define SQLITE_VERSION_NUMBER 3008011 |
| 116 | #define SQLITE_SOURCE_ID "2015-07-03 17:54:49 030f60a7ba171650ce8c0ac32dc166eab80aca32" |
| 117 | |
| 118 | /* |
| 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | ** |
| @@ -6289,10 +6289,13 @@ | |
| 6289 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6290 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6291 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6292 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6293 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6294 | |
| 6295 | /* |
| 6296 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6297 | ** METHOD: sqlite3 |
| 6298 | ** |
| 6299 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -111,11 +111,11 @@ | |
| 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | */ |
| 114 | #define SQLITE_VERSION "3.8.11" |
| 115 | #define SQLITE_VERSION_NUMBER 3008011 |
| 116 | #define SQLITE_SOURCE_ID "2015-07-08 16:22:42 5348ffc3fda5168c1e9e14aa88b0c6aedbda7c94" |
| 117 | |
| 118 | /* |
| 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | ** |
| @@ -6289,10 +6289,13 @@ | |
| 6289 | #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ |
| 6290 | #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ |
| 6291 | #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ |
| 6292 | #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ |
| 6293 | #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ |
| 6294 | #define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */ |
| 6295 | #define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */ |
| 6296 | #define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */ |
| 6297 | |
| 6298 | /* |
| 6299 | ** CAPI3REF: Retrieve the mutex for a database connection |
| 6300 | ** METHOD: sqlite3 |
| 6301 | ** |
| 6302 |
+1
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -503,10 +503,11 @@ | ||
| 503 | 503 | while( db_step(&q)==SQLITE_ROW ){ |
| 504 | 504 | newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0)); |
| 505 | 505 | } |
| 506 | 506 | db_finalize(&q); |
| 507 | 507 | newArgv[0] = g.argv[0]; |
| 508 | + newArgv[1] = 0; | |
| 508 | 509 | g.argv = newArgv; |
| 509 | 510 | g.argc = nFile+2; |
| 510 | 511 | if( nFile==0 ) return; |
| 511 | 512 | } |
| 512 | 513 | g.argv[1] = "revert"; |
| 513 | 514 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -503,10 +503,11 @@ | |
| 503 | while( db_step(&q)==SQLITE_ROW ){ |
| 504 | newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0)); |
| 505 | } |
| 506 | db_finalize(&q); |
| 507 | newArgv[0] = g.argv[0]; |
| 508 | g.argv = newArgv; |
| 509 | g.argc = nFile+2; |
| 510 | if( nFile==0 ) return; |
| 511 | } |
| 512 | g.argv[1] = "revert"; |
| 513 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -503,10 +503,11 @@ | |
| 503 | while( db_step(&q)==SQLITE_ROW ){ |
| 504 | newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0)); |
| 505 | } |
| 506 | db_finalize(&q); |
| 507 | newArgv[0] = g.argv[0]; |
| 508 | newArgv[1] = 0; |
| 509 | g.argv = newArgv; |
| 510 | g.argc = nFile+2; |
| 511 | if( nFile==0 ) return; |
| 512 | } |
| 513 | g.argv[1] = "revert"; |
| 514 |
+31
-12
| --- src/th_lang.c | ||
| +++ src/th_lang.c | ||
| @@ -722,27 +722,46 @@ | ||
| 722 | 722 | ** string is CLASS STRING |
| 723 | 723 | */ |
| 724 | 724 | static int string_is_command( |
| 725 | 725 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 726 | 726 | ){ |
| 727 | - int i; | |
| 728 | - int iRes = 1; | |
| 729 | 727 | if( argc!=4 ){ |
| 730 | 728 | return Th_WrongNumArgs(interp, "string is class string"); |
| 731 | 729 | } |
| 732 | - if( argl[2]!=5 || 0!=memcmp(argv[2], "alnum", 5) ){ | |
| 733 | - Th_ErrorMessage(interp, "Expected alnum, got: ", argv[2], argl[2]); | |
| 730 | + if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){ | |
| 731 | + int i; | |
| 732 | + int iRes = 1; | |
| 733 | + | |
| 734 | + for(i=0; i<argl[3]; i++){ | |
| 735 | + if( !th_isalnum(argv[3][i]) ){ | |
| 736 | + iRes = 0; | |
| 737 | + } | |
| 738 | + } | |
| 739 | + | |
| 740 | + return Th_SetResultInt(interp, iRes); | |
| 741 | + }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){ | |
| 742 | + double fVal; | |
| 743 | + if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ | |
| 744 | + return Th_SetResultInt(interp, 1); | |
| 745 | + } | |
| 746 | + return Th_SetResultInt(interp, 0); | |
| 747 | + }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){ | |
| 748 | + int iVal; | |
| 749 | + if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ | |
| 750 | + return Th_SetResultInt(interp, 1); | |
| 751 | + } | |
| 752 | + return Th_SetResultInt(interp, 0); | |
| 753 | + }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){ | |
| 754 | + if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ | |
| 755 | + return Th_SetResultInt(interp, 1); | |
| 756 | + } | |
| 757 | + return Th_SetResultInt(interp, 0); | |
| 758 | + }else{ | |
| 759 | + Th_ErrorMessage(interp, | |
| 760 | + "Expected alnum, double, integer, or list, got:", argv[2], argl[2]); | |
| 734 | 761 | return TH_ERROR; |
| 735 | 762 | } |
| 736 | - | |
| 737 | - for(i=0; i<argl[3]; i++){ | |
| 738 | - if( !th_isalnum(argv[3][i]) ){ | |
| 739 | - iRes = 0; | |
| 740 | - } | |
| 741 | - } | |
| 742 | - | |
| 743 | - return Th_SetResultInt(interp, iRes); | |
| 744 | 763 | } |
| 745 | 764 | |
| 746 | 765 | /* |
| 747 | 766 | ** TH Syntax: |
| 748 | 767 | ** |
| 749 | 768 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -722,27 +722,46 @@ | |
| 722 | ** string is CLASS STRING |
| 723 | */ |
| 724 | static int string_is_command( |
| 725 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 726 | ){ |
| 727 | int i; |
| 728 | int iRes = 1; |
| 729 | if( argc!=4 ){ |
| 730 | return Th_WrongNumArgs(interp, "string is class string"); |
| 731 | } |
| 732 | if( argl[2]!=5 || 0!=memcmp(argv[2], "alnum", 5) ){ |
| 733 | Th_ErrorMessage(interp, "Expected alnum, got: ", argv[2], argl[2]); |
| 734 | return TH_ERROR; |
| 735 | } |
| 736 | |
| 737 | for(i=0; i<argl[3]; i++){ |
| 738 | if( !th_isalnum(argv[3][i]) ){ |
| 739 | iRes = 0; |
| 740 | } |
| 741 | } |
| 742 | |
| 743 | return Th_SetResultInt(interp, iRes); |
| 744 | } |
| 745 | |
| 746 | /* |
| 747 | ** TH Syntax: |
| 748 | ** |
| 749 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -722,27 +722,46 @@ | |
| 722 | ** string is CLASS STRING |
| 723 | */ |
| 724 | static int string_is_command( |
| 725 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 726 | ){ |
| 727 | if( argc!=4 ){ |
| 728 | return Th_WrongNumArgs(interp, "string is class string"); |
| 729 | } |
| 730 | if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){ |
| 731 | int i; |
| 732 | int iRes = 1; |
| 733 | |
| 734 | for(i=0; i<argl[3]; i++){ |
| 735 | if( !th_isalnum(argv[3][i]) ){ |
| 736 | iRes = 0; |
| 737 | } |
| 738 | } |
| 739 | |
| 740 | return Th_SetResultInt(interp, iRes); |
| 741 | }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){ |
| 742 | double fVal; |
| 743 | if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ |
| 744 | return Th_SetResultInt(interp, 1); |
| 745 | } |
| 746 | return Th_SetResultInt(interp, 0); |
| 747 | }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){ |
| 748 | int iVal; |
| 749 | if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ |
| 750 | return Th_SetResultInt(interp, 1); |
| 751 | } |
| 752 | return Th_SetResultInt(interp, 0); |
| 753 | }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){ |
| 754 | if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ |
| 755 | return Th_SetResultInt(interp, 1); |
| 756 | } |
| 757 | return Th_SetResultInt(interp, 0); |
| 758 | }else{ |
| 759 | Th_ErrorMessage(interp, |
| 760 | "Expected alnum, double, integer, or list, got:", argv[2], argl[2]); |
| 761 | return TH_ERROR; |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | ** TH Syntax: |
| 767 | ** |
| 768 |
+106
-11
| --- src/th_tcl.c | ||
| +++ src/th_tcl.c | ||
| @@ -82,10 +82,11 @@ | ||
| 82 | 82 | # if defined(_WIN32) |
| 83 | 83 | # if !defined(WIN32_LEAN_AND_MEAN) |
| 84 | 84 | # define WIN32_LEAN_AND_MEAN |
| 85 | 85 | # endif |
| 86 | 86 | # if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0502) |
| 87 | +# undef _WIN32_WINNT | |
| 87 | 88 | # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ |
| 88 | 89 | # endif |
| 89 | 90 | # include <windows.h> |
| 90 | 91 | # ifndef TCL_DIRECTORY_SEP |
| 91 | 92 | # define TCL_DIRECTORY_SEP '\\' |
| @@ -342,11 +343,11 @@ | ||
| 342 | 343 | */ |
| 343 | 344 | static const char *getTclReturnCodeName( |
| 344 | 345 | int rc, |
| 345 | 346 | int nullIfOk |
| 346 | 347 | ){ |
| 347 | - static char zRc[32]; | |
| 348 | + static char zRc[TCL_INTEGER_SPACE + 17]; /* "Tcl return code\0" */ | |
| 348 | 349 | |
| 349 | 350 | switch( rc ){ |
| 350 | 351 | case TCL_OK: return nullIfOk ? 0 : "TCL_OK"; |
| 351 | 352 | case TCL_ERROR: return "TCL_ERROR"; |
| 352 | 353 | case TCL_RETURN: return "TCL_RETURN"; |
| @@ -395,11 +396,11 @@ | ||
| 395 | 396 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 396 | 397 | tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ |
| 397 | 398 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 398 | 399 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 399 | 400 | int useTip285; /* Non-zero if TIP #285 is available. */ |
| 400 | - char *setup; /* The optional Tcl setup script. */ | |
| 401 | + const char *setup; /* The optional Tcl setup script. */ | |
| 401 | 402 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 402 | 403 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 403 | 404 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 404 | 405 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| 405 | 406 | }; |
| @@ -438,12 +439,12 @@ | ||
| 438 | 439 | |
| 439 | 440 | /* |
| 440 | 441 | ** TH1 command: tclEval arg ?arg ...? |
| 441 | 442 | ** |
| 442 | 443 | ** Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 443 | -** error is generated, it will be transformed into a TH1 script error. A Tcl | |
| 444 | -** interpreter will be created automatically if it has not been already. | |
| 444 | +** error is generated, it will be transformed into a TH1 script error. The | |
| 445 | +** Tcl interpreter will be created automatically if it has not been already. | |
| 445 | 446 | */ |
| 446 | 447 | static int tclEval_command( |
| 447 | 448 | Th_Interp *interp, |
| 448 | 449 | void *ctx, |
| 449 | 450 | int argc, |
| @@ -497,11 +498,12 @@ | ||
| 497 | 498 | /* |
| 498 | 499 | ** TH1 command: tclExpr arg ?arg ...? |
| 499 | 500 | ** |
| 500 | 501 | ** Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 501 | 502 | ** script error is generated, it will be transformed into a TH1 script error. |
| 502 | -** A Tcl interpreter will be created automatically if it has not been already. | |
| 503 | +** The Tcl interpreter will be created automatically if it has not been | |
| 504 | +** already. | |
| 503 | 505 | */ |
| 504 | 506 | static int tclExpr_command( |
| 505 | 507 | Th_Interp *interp, |
| 506 | 508 | void *ctx, |
| 507 | 509 | int argc, |
| @@ -562,12 +564,12 @@ | ||
| 562 | 564 | |
| 563 | 565 | /* |
| 564 | 566 | ** TH1 command: tclInvoke command ?arg ...? |
| 565 | 567 | ** |
| 566 | 568 | ** Invokes the Tcl command using the supplied arguments. No additional |
| 567 | -** substitutions are performed on the arguments. A Tcl interpreter will | |
| 568 | -** be created automatically if it has not been already. | |
| 569 | +** substitutions are performed on the arguments. The Tcl interpreter | |
| 570 | +** will be created automatically if it has not been already. | |
| 569 | 571 | */ |
| 570 | 572 | static int tclInvoke_command( |
| 571 | 573 | Th_Interp *interp, |
| 572 | 574 | void *ctx, |
| 573 | 575 | int argc, |
| @@ -632,10 +634,100 @@ | ||
| 632 | 634 | Tcl_Release((ClientData)tclInterp); |
| 633 | 635 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 634 | 636 | getTh1ReturnCode(rc)); |
| 635 | 637 | return rc; |
| 636 | 638 | } |
| 639 | + | |
| 640 | +/* | |
| 641 | +** TH1 command: tclIsSafe | |
| 642 | +** | |
| 643 | +** Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter | |
| 644 | +** will be created automatically if it has not been already. | |
| 645 | +*/ | |
| 646 | +static int tclIsSafe_command( | |
| 647 | + Th_Interp *interp, | |
| 648 | + void *ctx, | |
| 649 | + int argc, | |
| 650 | + const char **argv, | |
| 651 | + int *argl | |
| 652 | +){ | |
| 653 | + Tcl_Interp *tclInterp; | |
| 654 | + | |
| 655 | + if( createTclInterp(interp, ctx)!=TH_OK ){ | |
| 656 | + return TH_ERROR; | |
| 657 | + } | |
| 658 | + if( argc!=1 ){ | |
| 659 | + return Th_WrongNumArgs(interp, "tclIsSafe"); | |
| 660 | + } | |
| 661 | + tclInterp = GET_CTX_TCL_INTERP(ctx); | |
| 662 | + if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ | |
| 663 | + Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); | |
| 664 | + return TH_ERROR; | |
| 665 | + } | |
| 666 | + Th_SetResultInt(interp, Tcl_IsSafe(tclInterp)); | |
| 667 | + return TH_OK; | |
| 668 | +} | |
| 669 | + | |
| 670 | +/* | |
| 671 | +** TH1 command: tclMakeSafe | |
| 672 | +** | |
| 673 | +** Forces the Tcl interpreter into "safe" mode by removing all "unsafe" | |
| 674 | +** commands and variables. This operation cannot be undone. The Tcl | |
| 675 | +** interpreter will remain "safe" until the process terminates. | |
| 676 | +*/ | |
| 677 | +static int tclMakeSafe_command( | |
| 678 | + Th_Interp *interp, | |
| 679 | + void *ctx, | |
| 680 | + int argc, | |
| 681 | + const char **argv, | |
| 682 | + int *argl | |
| 683 | +){ | |
| 684 | + static int registerChans = 1; | |
| 685 | + Tcl_Interp *tclInterp; | |
| 686 | + int rc = TH_OK; | |
| 687 | + | |
| 688 | + if( createTclInterp(interp, ctx)!=TH_OK ){ | |
| 689 | + return TH_ERROR; | |
| 690 | + } | |
| 691 | + if( argc!=1 ){ | |
| 692 | + return Th_WrongNumArgs(interp, "tclMakeSafe"); | |
| 693 | + } | |
| 694 | + tclInterp = GET_CTX_TCL_INTERP(ctx); | |
| 695 | + if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ | |
| 696 | + Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); | |
| 697 | + return TH_ERROR; | |
| 698 | + } | |
| 699 | + if( Tcl_IsSafe(tclInterp) ){ | |
| 700 | + Th_ErrorMessage(interp, | |
| 701 | + "Tcl interpreter is already 'safe'", (const char *)"", 0); | |
| 702 | + return TH_ERROR; | |
| 703 | + } | |
| 704 | + if( registerChans ){ | |
| 705 | + /* | |
| 706 | + ** HACK: Prevent the call to Tcl_MakeSafe() from actually closing the | |
| 707 | + ** standard channels instead of simply unregistering them from | |
| 708 | + ** the Tcl interpreter. This should only need to be done once | |
| 709 | + ** per thread (process?). | |
| 710 | + */ | |
| 711 | + registerChans = 0; | |
| 712 | + Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); | |
| 713 | + Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); | |
| 714 | + Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); | |
| 715 | + } | |
| 716 | + Tcl_Preserve((ClientData)tclInterp); | |
| 717 | + if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ | |
| 718 | + int nResult; | |
| 719 | + const char *zResult = getTclResult(tclInterp, &nResult); | |
| 720 | + Th_ErrorMessage(interp, | |
| 721 | + "could not make Tcl interpreter 'safe':", zResult, nResult); | |
| 722 | + rc = TH_ERROR; | |
| 723 | + }else{ | |
| 724 | + Th_SetResult(interp, 0, 0); | |
| 725 | + } | |
| 726 | + Tcl_Release((ClientData)tclInterp); | |
| 727 | + return rc; | |
| 728 | +} | |
| 637 | 729 | |
| 638 | 730 | /* |
| 639 | 731 | ** Tcl command: th1Eval arg |
| 640 | 732 | ** |
| 641 | 733 | ** Evaluates the TH1 script and returns its result verbatim. If a TH1 script |
| @@ -708,13 +800,15 @@ | ||
| 708 | 800 | static struct _Command { |
| 709 | 801 | const char *zName; |
| 710 | 802 | Th_CommandProc xProc; |
| 711 | 803 | void *pContext; |
| 712 | 804 | } aCommand[] = { |
| 713 | - {"tclEval", tclEval_command, 0}, | |
| 714 | - {"tclExpr", tclExpr_command, 0}, | |
| 715 | - {"tclInvoke", tclInvoke_command, 0}, | |
| 805 | + {"tclEval", tclEval_command, 0}, | |
| 806 | + {"tclExpr", tclExpr_command, 0}, | |
| 807 | + {"tclInvoke", tclInvoke_command, 0}, | |
| 808 | + {"tclIsSafe", tclIsSafe_command, 0}, | |
| 809 | + {"tclMakeSafe", tclMakeSafe_command, 0}, | |
| 716 | 810 | {0, 0, 0} |
| 717 | 811 | }; |
| 718 | 812 | |
| 719 | 813 | /* |
| 720 | 814 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| @@ -791,10 +885,11 @@ | ||
| 791 | 885 | if( zDirName ){ |
| 792 | 886 | fossil_free(zDirName); zDirName = 0; |
| 793 | 887 | } |
| 794 | 888 | #endif /* TCL_USE_SET_DLL_DIRECTORY */ |
| 795 | 889 | } |
| 890 | + if( !zFileName ) break; | |
| 796 | 891 | hLibrary = dlopen(zFileName, RTLD_NOW | RTLD_GLOBAL); |
| 797 | 892 | /* NOTE: If the file name was allocated, free it now. */ |
| 798 | 893 | if( zFileName!=aFileName ){ |
| 799 | 894 | sqlite3_free(zFileName); zFileName = 0; |
| 800 | 895 | } |
| @@ -991,11 +1086,11 @@ | ||
| 991 | 1086 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 992 | 1087 | int argc; |
| 993 | 1088 | char **argv; |
| 994 | 1089 | char *argv0 = 0; |
| 995 | 1090 | Tcl_Interp *tclInterp; |
| 996 | - char *setup; | |
| 1091 | + const char *setup; | |
| 997 | 1092 | |
| 998 | 1093 | if( !tclContext ){ |
| 999 | 1094 | Th_ErrorMessage(interp, |
| 1000 | 1095 | "invalid Tcl context", (const char *)"", 0); |
| 1001 | 1096 | return TH_ERROR; |
| 1002 | 1097 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -82,10 +82,11 @@ | |
| 82 | # if defined(_WIN32) |
| 83 | # if !defined(WIN32_LEAN_AND_MEAN) |
| 84 | # define WIN32_LEAN_AND_MEAN |
| 85 | # endif |
| 86 | # if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0502) |
| 87 | # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ |
| 88 | # endif |
| 89 | # include <windows.h> |
| 90 | # ifndef TCL_DIRECTORY_SEP |
| 91 | # define TCL_DIRECTORY_SEP '\\' |
| @@ -342,11 +343,11 @@ | |
| 342 | */ |
| 343 | static const char *getTclReturnCodeName( |
| 344 | int rc, |
| 345 | int nullIfOk |
| 346 | ){ |
| 347 | static char zRc[32]; |
| 348 | |
| 349 | switch( rc ){ |
| 350 | case TCL_OK: return nullIfOk ? 0 : "TCL_OK"; |
| 351 | case TCL_ERROR: return "TCL_ERROR"; |
| 352 | case TCL_RETURN: return "TCL_RETURN"; |
| @@ -395,11 +396,11 @@ | |
| 395 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 396 | tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ |
| 397 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 398 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 399 | int useTip285; /* Non-zero if TIP #285 is available. */ |
| 400 | char *setup; /* The optional Tcl setup script. */ |
| 401 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 402 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 403 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 404 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| 405 | }; |
| @@ -438,12 +439,12 @@ | |
| 438 | |
| 439 | /* |
| 440 | ** TH1 command: tclEval arg ?arg ...? |
| 441 | ** |
| 442 | ** Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 443 | ** error is generated, it will be transformed into a TH1 script error. A Tcl |
| 444 | ** interpreter will be created automatically if it has not been already. |
| 445 | */ |
| 446 | static int tclEval_command( |
| 447 | Th_Interp *interp, |
| 448 | void *ctx, |
| 449 | int argc, |
| @@ -497,11 +498,12 @@ | |
| 497 | /* |
| 498 | ** TH1 command: tclExpr arg ?arg ...? |
| 499 | ** |
| 500 | ** Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 501 | ** script error is generated, it will be transformed into a TH1 script error. |
| 502 | ** A Tcl interpreter will be created automatically if it has not been already. |
| 503 | */ |
| 504 | static int tclExpr_command( |
| 505 | Th_Interp *interp, |
| 506 | void *ctx, |
| 507 | int argc, |
| @@ -562,12 +564,12 @@ | |
| 562 | |
| 563 | /* |
| 564 | ** TH1 command: tclInvoke command ?arg ...? |
| 565 | ** |
| 566 | ** Invokes the Tcl command using the supplied arguments. No additional |
| 567 | ** substitutions are performed on the arguments. A Tcl interpreter will |
| 568 | ** be created automatically if it has not been already. |
| 569 | */ |
| 570 | static int tclInvoke_command( |
| 571 | Th_Interp *interp, |
| 572 | void *ctx, |
| 573 | int argc, |
| @@ -632,10 +634,100 @@ | |
| 632 | Tcl_Release((ClientData)tclInterp); |
| 633 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 634 | getTh1ReturnCode(rc)); |
| 635 | return rc; |
| 636 | } |
| 637 | |
| 638 | /* |
| 639 | ** Tcl command: th1Eval arg |
| 640 | ** |
| 641 | ** Evaluates the TH1 script and returns its result verbatim. If a TH1 script |
| @@ -708,13 +800,15 @@ | |
| 708 | static struct _Command { |
| 709 | const char *zName; |
| 710 | Th_CommandProc xProc; |
| 711 | void *pContext; |
| 712 | } aCommand[] = { |
| 713 | {"tclEval", tclEval_command, 0}, |
| 714 | {"tclExpr", tclExpr_command, 0}, |
| 715 | {"tclInvoke", tclInvoke_command, 0}, |
| 716 | {0, 0, 0} |
| 717 | }; |
| 718 | |
| 719 | /* |
| 720 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| @@ -791,10 +885,11 @@ | |
| 791 | if( zDirName ){ |
| 792 | fossil_free(zDirName); zDirName = 0; |
| 793 | } |
| 794 | #endif /* TCL_USE_SET_DLL_DIRECTORY */ |
| 795 | } |
| 796 | hLibrary = dlopen(zFileName, RTLD_NOW | RTLD_GLOBAL); |
| 797 | /* NOTE: If the file name was allocated, free it now. */ |
| 798 | if( zFileName!=aFileName ){ |
| 799 | sqlite3_free(zFileName); zFileName = 0; |
| 800 | } |
| @@ -991,11 +1086,11 @@ | |
| 991 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 992 | int argc; |
| 993 | char **argv; |
| 994 | char *argv0 = 0; |
| 995 | Tcl_Interp *tclInterp; |
| 996 | char *setup; |
| 997 | |
| 998 | if( !tclContext ){ |
| 999 | Th_ErrorMessage(interp, |
| 1000 | "invalid Tcl context", (const char *)"", 0); |
| 1001 | return TH_ERROR; |
| 1002 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -82,10 +82,11 @@ | |
| 82 | # if defined(_WIN32) |
| 83 | # if !defined(WIN32_LEAN_AND_MEAN) |
| 84 | # define WIN32_LEAN_AND_MEAN |
| 85 | # endif |
| 86 | # if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0502) |
| 87 | # undef _WIN32_WINNT |
| 88 | # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ |
| 89 | # endif |
| 90 | # include <windows.h> |
| 91 | # ifndef TCL_DIRECTORY_SEP |
| 92 | # define TCL_DIRECTORY_SEP '\\' |
| @@ -342,11 +343,11 @@ | |
| 343 | */ |
| 344 | static const char *getTclReturnCodeName( |
| 345 | int rc, |
| 346 | int nullIfOk |
| 347 | ){ |
| 348 | static char zRc[TCL_INTEGER_SPACE + 17]; /* "Tcl return code\0" */ |
| 349 | |
| 350 | switch( rc ){ |
| 351 | case TCL_OK: return nullIfOk ? 0 : "TCL_OK"; |
| 352 | case TCL_ERROR: return "TCL_ERROR"; |
| 353 | case TCL_RETURN: return "TCL_RETURN"; |
| @@ -395,11 +396,11 @@ | |
| 396 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 397 | tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ |
| 398 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 399 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 400 | int useTip285; /* Non-zero if TIP #285 is available. */ |
| 401 | const char *setup; /* The optional Tcl setup script. */ |
| 402 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 403 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 404 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 405 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| 406 | }; |
| @@ -438,12 +439,12 @@ | |
| 439 | |
| 440 | /* |
| 441 | ** TH1 command: tclEval arg ?arg ...? |
| 442 | ** |
| 443 | ** Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 444 | ** error is generated, it will be transformed into a TH1 script error. The |
| 445 | ** Tcl interpreter will be created automatically if it has not been already. |
| 446 | */ |
| 447 | static int tclEval_command( |
| 448 | Th_Interp *interp, |
| 449 | void *ctx, |
| 450 | int argc, |
| @@ -497,11 +498,12 @@ | |
| 498 | /* |
| 499 | ** TH1 command: tclExpr arg ?arg ...? |
| 500 | ** |
| 501 | ** Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 502 | ** script error is generated, it will be transformed into a TH1 script error. |
| 503 | ** The Tcl interpreter will be created automatically if it has not been |
| 504 | ** already. |
| 505 | */ |
| 506 | static int tclExpr_command( |
| 507 | Th_Interp *interp, |
| 508 | void *ctx, |
| 509 | int argc, |
| @@ -562,12 +564,12 @@ | |
| 564 | |
| 565 | /* |
| 566 | ** TH1 command: tclInvoke command ?arg ...? |
| 567 | ** |
| 568 | ** Invokes the Tcl command using the supplied arguments. No additional |
| 569 | ** substitutions are performed on the arguments. The Tcl interpreter |
| 570 | ** will be created automatically if it has not been already. |
| 571 | */ |
| 572 | static int tclInvoke_command( |
| 573 | Th_Interp *interp, |
| 574 | void *ctx, |
| 575 | int argc, |
| @@ -632,10 +634,100 @@ | |
| 634 | Tcl_Release((ClientData)tclInterp); |
| 635 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 636 | getTh1ReturnCode(rc)); |
| 637 | return rc; |
| 638 | } |
| 639 | |
| 640 | /* |
| 641 | ** TH1 command: tclIsSafe |
| 642 | ** |
| 643 | ** Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter |
| 644 | ** will be created automatically if it has not been already. |
| 645 | */ |
| 646 | static int tclIsSafe_command( |
| 647 | Th_Interp *interp, |
| 648 | void *ctx, |
| 649 | int argc, |
| 650 | const char **argv, |
| 651 | int *argl |
| 652 | ){ |
| 653 | Tcl_Interp *tclInterp; |
| 654 | |
| 655 | if( createTclInterp(interp, ctx)!=TH_OK ){ |
| 656 | return TH_ERROR; |
| 657 | } |
| 658 | if( argc!=1 ){ |
| 659 | return Th_WrongNumArgs(interp, "tclIsSafe"); |
| 660 | } |
| 661 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 662 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 663 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 664 | return TH_ERROR; |
| 665 | } |
| 666 | Th_SetResultInt(interp, Tcl_IsSafe(tclInterp)); |
| 667 | return TH_OK; |
| 668 | } |
| 669 | |
| 670 | /* |
| 671 | ** TH1 command: tclMakeSafe |
| 672 | ** |
| 673 | ** Forces the Tcl interpreter into "safe" mode by removing all "unsafe" |
| 674 | ** commands and variables. This operation cannot be undone. The Tcl |
| 675 | ** interpreter will remain "safe" until the process terminates. |
| 676 | */ |
| 677 | static int tclMakeSafe_command( |
| 678 | Th_Interp *interp, |
| 679 | void *ctx, |
| 680 | int argc, |
| 681 | const char **argv, |
| 682 | int *argl |
| 683 | ){ |
| 684 | static int registerChans = 1; |
| 685 | Tcl_Interp *tclInterp; |
| 686 | int rc = TH_OK; |
| 687 | |
| 688 | if( createTclInterp(interp, ctx)!=TH_OK ){ |
| 689 | return TH_ERROR; |
| 690 | } |
| 691 | if( argc!=1 ){ |
| 692 | return Th_WrongNumArgs(interp, "tclMakeSafe"); |
| 693 | } |
| 694 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 695 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 696 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 697 | return TH_ERROR; |
| 698 | } |
| 699 | if( Tcl_IsSafe(tclInterp) ){ |
| 700 | Th_ErrorMessage(interp, |
| 701 | "Tcl interpreter is already 'safe'", (const char *)"", 0); |
| 702 | return TH_ERROR; |
| 703 | } |
| 704 | if( registerChans ){ |
| 705 | /* |
| 706 | ** HACK: Prevent the call to Tcl_MakeSafe() from actually closing the |
| 707 | ** standard channels instead of simply unregistering them from |
| 708 | ** the Tcl interpreter. This should only need to be done once |
| 709 | ** per thread (process?). |
| 710 | */ |
| 711 | registerChans = 0; |
| 712 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDIN)); |
| 713 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDOUT)); |
| 714 | Tcl_RegisterChannel(NULL, Tcl_GetStdChannel(TCL_STDERR)); |
| 715 | } |
| 716 | Tcl_Preserve((ClientData)tclInterp); |
| 717 | if( Tcl_MakeSafe(tclInterp)!=TCL_OK ){ |
| 718 | int nResult; |
| 719 | const char *zResult = getTclResult(tclInterp, &nResult); |
| 720 | Th_ErrorMessage(interp, |
| 721 | "could not make Tcl interpreter 'safe':", zResult, nResult); |
| 722 | rc = TH_ERROR; |
| 723 | }else{ |
| 724 | Th_SetResult(interp, 0, 0); |
| 725 | } |
| 726 | Tcl_Release((ClientData)tclInterp); |
| 727 | return rc; |
| 728 | } |
| 729 | |
| 730 | /* |
| 731 | ** Tcl command: th1Eval arg |
| 732 | ** |
| 733 | ** Evaluates the TH1 script and returns its result verbatim. If a TH1 script |
| @@ -708,13 +800,15 @@ | |
| 800 | static struct _Command { |
| 801 | const char *zName; |
| 802 | Th_CommandProc xProc; |
| 803 | void *pContext; |
| 804 | } aCommand[] = { |
| 805 | {"tclEval", tclEval_command, 0}, |
| 806 | {"tclExpr", tclExpr_command, 0}, |
| 807 | {"tclInvoke", tclInvoke_command, 0}, |
| 808 | {"tclIsSafe", tclIsSafe_command, 0}, |
| 809 | {"tclMakeSafe", tclMakeSafe_command, 0}, |
| 810 | {0, 0, 0} |
| 811 | }; |
| 812 | |
| 813 | /* |
| 814 | ** Called if the Tcl interpreter is deleted. Removes the Tcl integration |
| @@ -791,10 +885,11 @@ | |
| 885 | if( zDirName ){ |
| 886 | fossil_free(zDirName); zDirName = 0; |
| 887 | } |
| 888 | #endif /* TCL_USE_SET_DLL_DIRECTORY */ |
| 889 | } |
| 890 | if( !zFileName ) break; |
| 891 | hLibrary = dlopen(zFileName, RTLD_NOW | RTLD_GLOBAL); |
| 892 | /* NOTE: If the file name was allocated, free it now. */ |
| 893 | if( zFileName!=aFileName ){ |
| 894 | sqlite3_free(zFileName); zFileName = 0; |
| 895 | } |
| @@ -991,11 +1086,11 @@ | |
| 1086 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 1087 | int argc; |
| 1088 | char **argv; |
| 1089 | char *argv0 = 0; |
| 1090 | Tcl_Interp *tclInterp; |
| 1091 | const char *setup; |
| 1092 | |
| 1093 | if( !tclContext ){ |
| 1094 | Th_ErrorMessage(interp, |
| 1095 | "invalid Tcl context", (const char *)"", 0); |
| 1096 | return TH_ERROR; |
| 1097 |
+108
-26
| --- src/undo.c | ||
| +++ src/undo.c | ||
| @@ -18,11 +18,20 @@ | ||
| 18 | 18 | ** This file implements the undo/redo functionality. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "undo.h" |
| 22 | 22 | |
| 23 | - | |
| 23 | +#if INTERFACE | |
| 24 | +/* | |
| 25 | +** Possible return values from the undo_maybe_save() routine. | |
| 26 | +*/ | |
| 27 | +#define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ | |
| 28 | +#define UNDO_SAVED_OK (1) /* The specified file was saved succesfully. */ | |
| 29 | +#define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ | |
| 30 | +#define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ | |
| 31 | +#define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ | |
| 32 | +#endif | |
| 24 | 33 | |
| 25 | 34 | /* |
| 26 | 35 | ** Undo the change to the file zPathname. zPathname is the pathname |
| 27 | 36 | ** of the file relative to the root of the repository. If redoFlag is |
| 28 | 37 | ** true the redo a change. If there is nothing to undo (or redo) then |
| @@ -261,41 +270,111 @@ | ||
| 261 | 270 | ** Save the current content of the file zPathname so that it |
| 262 | 271 | ** will be undoable. The name is relative to the root of the |
| 263 | 272 | ** tree. |
| 264 | 273 | */ |
| 265 | 274 | void undo_save(const char *zPathname){ |
| 275 | + if( undoDisable ) return; | |
| 276 | + if( undo_maybe_save(zPathname, -1)!=UNDO_SAVED_OK ){ | |
| 277 | + fossil_panic("failed to save undo information for path: %s", | |
| 278 | + zPathname); | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/* | |
| 283 | +** Possibly save the current content of the file zPathname so | |
| 284 | +** that it will be undoable. The name is relative to the root | |
| 285 | +** of the tree. The limit argument may be used to specify the | |
| 286 | +** maximum size for the file to be saved. If the size of the | |
| 287 | +** specified file exceeds this size limit (in bytes), it will | |
| 288 | +** not be saved and an appropriate code will be returned. | |
| 289 | +** | |
| 290 | +** WARNING: Please do NOT call this function with a limit | |
| 291 | +** value less than zero, call the undo_save() | |
| 292 | +** function instead. | |
| 293 | +** | |
| 294 | +** The return value of this function will always be one of the | |
| 295 | +** following codes: | |
| 296 | +** | |
| 297 | +** UNDO_SAVED_OK: The specified file was saved succesfully. | |
| 298 | +** | |
| 299 | +** UNDO_DISABLED: The specified file was NOT saved, because the | |
| 300 | +** "undo subsystem" is disabled. This error may | |
| 301 | +** indicate that a call to undo_disable() was | |
| 302 | +** issued. | |
| 303 | +** | |
| 304 | +** UNDO_INACTIVE: The specified file was NOT saved, because the | |
| 305 | +** "undo subsystem" is not active. This error | |
| 306 | +** may indicate that a call to undo_begin() is | |
| 307 | +** missing. | |
| 308 | +** | |
| 309 | +** UNDO_TOOBIG: The specified file was NOT saved, because it | |
| 310 | +** exceeded the specified size limit. It is | |
| 311 | +** impossible for this value to be returned if | |
| 312 | +** the specified size limit is less than zero | |
| 313 | +** (i.e. unlimited). | |
| 314 | +*/ | |
| 315 | +int undo_maybe_save(const char *zPathname, i64 limit){ | |
| 266 | 316 | char *zFullname; |
| 267 | - Blob content; | |
| 268 | - int existsFlag; | |
| 269 | - int isLink; | |
| 270 | - Stmt q; | |
| 317 | + i64 size; | |
| 318 | + int result; | |
| 271 | 319 | |
| 272 | - if( !undoActive ) return; | |
| 320 | + if( undoDisable ) return UNDO_DISABLED; | |
| 321 | + if( !undoActive ) return UNDO_INACTIVE; | |
| 273 | 322 | zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 274 | - existsFlag = file_wd_size(zFullname)>=0; | |
| 275 | - isLink = file_wd_islink(zFullname); | |
| 276 | - db_prepare(&q, | |
| 277 | - "INSERT OR IGNORE INTO" | |
| 278 | - " undo(pathname,redoflag,existsflag,isExe,isLink,content)" | |
| 279 | - " VALUES(%Q,0,%d,%d,%d,:c)", | |
| 280 | - zPathname, existsFlag, file_wd_isexe(zFullname), isLink | |
| 281 | - ); | |
| 282 | - if( existsFlag ){ | |
| 283 | - if( isLink ){ | |
| 284 | - blob_read_link(&content, zFullname); | |
| 285 | - }else{ | |
| 286 | - blob_read_from_file(&content, zFullname); | |
| 287 | - } | |
| 288 | - db_bind_blob(&q, ":c", &content); | |
| 323 | + size = file_wd_size(zFullname); | |
| 324 | + if( limit<0 || size<=limit ){ | |
| 325 | + int existsFlag = (size>=0); | |
| 326 | + int isLink = file_wd_islink(zFullname); | |
| 327 | + Stmt q; | |
| 328 | + Blob content; | |
| 329 | + db_prepare(&q, | |
| 330 | + "INSERT OR IGNORE INTO" | |
| 331 | + " undo(pathname,redoflag,existsflag,isExe,isLink,content)" | |
| 332 | + " VALUES(%Q,0,%d,%d,%d,:c)", | |
| 333 | + zPathname, existsFlag, file_wd_isexe(zFullname), isLink | |
| 334 | + ); | |
| 335 | + if( existsFlag ){ | |
| 336 | + if( isLink ){ | |
| 337 | + blob_read_link(&content, zFullname); | |
| 338 | + }else{ | |
| 339 | + blob_read_from_file(&content, zFullname); | |
| 340 | + } | |
| 341 | + db_bind_blob(&q, ":c", &content); | |
| 342 | + } | |
| 343 | + db_step(&q); | |
| 344 | + db_finalize(&q); | |
| 345 | + if( existsFlag ){ | |
| 346 | + blob_reset(&content); | |
| 347 | + } | |
| 348 | + undoNeedRollback = 1; | |
| 349 | + result = UNDO_SAVED_OK; | |
| 350 | + }else{ | |
| 351 | + result = UNDO_TOOBIG; | |
| 289 | 352 | } |
| 290 | 353 | free(zFullname); |
| 291 | - db_step(&q); | |
| 292 | - db_finalize(&q); | |
| 293 | - if( existsFlag ){ | |
| 294 | - blob_reset(&content); | |
| 354 | + return result; | |
| 355 | +} | |
| 356 | + | |
| 357 | +/* | |
| 358 | +** Returns an error message for the undo_maybe_save() return code. | |
| 359 | +** Currently, this function assumes that the caller is using the | |
| 360 | +** returned error message in a context prefixed with "because". | |
| 361 | +*/ | |
| 362 | +const char *undo_save_message(int rc){ | |
| 363 | + static char zRc[32]; | |
| 364 | + | |
| 365 | + switch( rc ){ | |
| 366 | + case UNDO_NONE: return "undo is disabled for this operation"; | |
| 367 | + case UNDO_SAVED_OK: return "the save operation was successful"; | |
| 368 | + case UNDO_DISABLED: return "the undo subsystem is disabled"; | |
| 369 | + case UNDO_INACTIVE: return "the undo subsystem is inactive"; | |
| 370 | + case UNDO_TOOBIG: return "the file is too big"; | |
| 371 | + default: { | |
| 372 | + sqlite3_snprintf(sizeof(zRc), zRc, "of error code %d", rc); | |
| 373 | + } | |
| 295 | 374 | } |
| 296 | - undoNeedRollback = 1; | |
| 375 | + return zRc; | |
| 297 | 376 | } |
| 298 | 377 | |
| 299 | 378 | /* |
| 300 | 379 | ** Make the current state of stashid undoable. |
| 301 | 380 | */ |
| @@ -361,10 +440,13 @@ | ||
| 361 | 440 | ** |
| 362 | 441 | ** (1) fossil update (5) fossil stash apply |
| 363 | 442 | ** (2) fossil merge (6) fossil stash drop |
| 364 | 443 | ** (3) fossil revert (7) fossil stash goto |
| 365 | 444 | ** (4) fossil stash pop |
| 445 | +** | |
| 446 | +** The "fossil clean" operation can also be undone; however, this is | |
| 447 | +** currently limited to files that are less than 10MiB in size. | |
| 366 | 448 | ** |
| 367 | 449 | ** If FILENAME is specified then restore the content of the named |
| 368 | 450 | ** file(s) but otherwise leave the update or merge or revert in effect. |
| 369 | 451 | ** The redo command undoes the effect of the most recent undo. |
| 370 | 452 | ** |
| 371 | 453 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -18,11 +18,20 @@ | |
| 18 | ** This file implements the undo/redo functionality. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "undo.h" |
| 22 | |
| 23 | |
| 24 | |
| 25 | /* |
| 26 | ** Undo the change to the file zPathname. zPathname is the pathname |
| 27 | ** of the file relative to the root of the repository. If redoFlag is |
| 28 | ** true the redo a change. If there is nothing to undo (or redo) then |
| @@ -261,41 +270,111 @@ | |
| 261 | ** Save the current content of the file zPathname so that it |
| 262 | ** will be undoable. The name is relative to the root of the |
| 263 | ** tree. |
| 264 | */ |
| 265 | void undo_save(const char *zPathname){ |
| 266 | char *zFullname; |
| 267 | Blob content; |
| 268 | int existsFlag; |
| 269 | int isLink; |
| 270 | Stmt q; |
| 271 | |
| 272 | if( !undoActive ) return; |
| 273 | zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 274 | existsFlag = file_wd_size(zFullname)>=0; |
| 275 | isLink = file_wd_islink(zFullname); |
| 276 | db_prepare(&q, |
| 277 | "INSERT OR IGNORE INTO" |
| 278 | " undo(pathname,redoflag,existsflag,isExe,isLink,content)" |
| 279 | " VALUES(%Q,0,%d,%d,%d,:c)", |
| 280 | zPathname, existsFlag, file_wd_isexe(zFullname), isLink |
| 281 | ); |
| 282 | if( existsFlag ){ |
| 283 | if( isLink ){ |
| 284 | blob_read_link(&content, zFullname); |
| 285 | }else{ |
| 286 | blob_read_from_file(&content, zFullname); |
| 287 | } |
| 288 | db_bind_blob(&q, ":c", &content); |
| 289 | } |
| 290 | free(zFullname); |
| 291 | db_step(&q); |
| 292 | db_finalize(&q); |
| 293 | if( existsFlag ){ |
| 294 | blob_reset(&content); |
| 295 | } |
| 296 | undoNeedRollback = 1; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | ** Make the current state of stashid undoable. |
| 301 | */ |
| @@ -361,10 +440,13 @@ | |
| 361 | ** |
| 362 | ** (1) fossil update (5) fossil stash apply |
| 363 | ** (2) fossil merge (6) fossil stash drop |
| 364 | ** (3) fossil revert (7) fossil stash goto |
| 365 | ** (4) fossil stash pop |
| 366 | ** |
| 367 | ** If FILENAME is specified then restore the content of the named |
| 368 | ** file(s) but otherwise leave the update or merge or revert in effect. |
| 369 | ** The redo command undoes the effect of the most recent undo. |
| 370 | ** |
| 371 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -18,11 +18,20 @@ | |
| 18 | ** This file implements the undo/redo functionality. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "undo.h" |
| 22 | |
| 23 | #if INTERFACE |
| 24 | /* |
| 25 | ** Possible return values from the undo_maybe_save() routine. |
| 26 | */ |
| 27 | #define UNDO_NONE (0) /* Placeholder only used to initialize vars. */ |
| 28 | #define UNDO_SAVED_OK (1) /* The specified file was saved succesfully. */ |
| 29 | #define UNDO_DISABLED (2) /* File not saved, subsystem is disabled. */ |
| 30 | #define UNDO_INACTIVE (3) /* File not saved, subsystem is not active. */ |
| 31 | #define UNDO_TOOBIG (4) /* File not saved, it exceeded a size limit. */ |
| 32 | #endif |
| 33 | |
| 34 | /* |
| 35 | ** Undo the change to the file zPathname. zPathname is the pathname |
| 36 | ** of the file relative to the root of the repository. If redoFlag is |
| 37 | ** true the redo a change. If there is nothing to undo (or redo) then |
| @@ -261,41 +270,111 @@ | |
| 270 | ** Save the current content of the file zPathname so that it |
| 271 | ** will be undoable. The name is relative to the root of the |
| 272 | ** tree. |
| 273 | */ |
| 274 | void undo_save(const char *zPathname){ |
| 275 | if( undoDisable ) return; |
| 276 | if( undo_maybe_save(zPathname, -1)!=UNDO_SAVED_OK ){ |
| 277 | fossil_panic("failed to save undo information for path: %s", |
| 278 | zPathname); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** Possibly save the current content of the file zPathname so |
| 284 | ** that it will be undoable. The name is relative to the root |
| 285 | ** of the tree. The limit argument may be used to specify the |
| 286 | ** maximum size for the file to be saved. If the size of the |
| 287 | ** specified file exceeds this size limit (in bytes), it will |
| 288 | ** not be saved and an appropriate code will be returned. |
| 289 | ** |
| 290 | ** WARNING: Please do NOT call this function with a limit |
| 291 | ** value less than zero, call the undo_save() |
| 292 | ** function instead. |
| 293 | ** |
| 294 | ** The return value of this function will always be one of the |
| 295 | ** following codes: |
| 296 | ** |
| 297 | ** UNDO_SAVED_OK: The specified file was saved succesfully. |
| 298 | ** |
| 299 | ** UNDO_DISABLED: The specified file was NOT saved, because the |
| 300 | ** "undo subsystem" is disabled. This error may |
| 301 | ** indicate that a call to undo_disable() was |
| 302 | ** issued. |
| 303 | ** |
| 304 | ** UNDO_INACTIVE: The specified file was NOT saved, because the |
| 305 | ** "undo subsystem" is not active. This error |
| 306 | ** may indicate that a call to undo_begin() is |
| 307 | ** missing. |
| 308 | ** |
| 309 | ** UNDO_TOOBIG: The specified file was NOT saved, because it |
| 310 | ** exceeded the specified size limit. It is |
| 311 | ** impossible for this value to be returned if |
| 312 | ** the specified size limit is less than zero |
| 313 | ** (i.e. unlimited). |
| 314 | */ |
| 315 | int undo_maybe_save(const char *zPathname, i64 limit){ |
| 316 | char *zFullname; |
| 317 | i64 size; |
| 318 | int result; |
| 319 | |
| 320 | if( undoDisable ) return UNDO_DISABLED; |
| 321 | if( !undoActive ) return UNDO_INACTIVE; |
| 322 | zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 323 | size = file_wd_size(zFullname); |
| 324 | if( limit<0 || size<=limit ){ |
| 325 | int existsFlag = (size>=0); |
| 326 | int isLink = file_wd_islink(zFullname); |
| 327 | Stmt q; |
| 328 | Blob content; |
| 329 | db_prepare(&q, |
| 330 | "INSERT OR IGNORE INTO" |
| 331 | " undo(pathname,redoflag,existsflag,isExe,isLink,content)" |
| 332 | " VALUES(%Q,0,%d,%d,%d,:c)", |
| 333 | zPathname, existsFlag, file_wd_isexe(zFullname), isLink |
| 334 | ); |
| 335 | if( existsFlag ){ |
| 336 | if( isLink ){ |
| 337 | blob_read_link(&content, zFullname); |
| 338 | }else{ |
| 339 | blob_read_from_file(&content, zFullname); |
| 340 | } |
| 341 | db_bind_blob(&q, ":c", &content); |
| 342 | } |
| 343 | db_step(&q); |
| 344 | db_finalize(&q); |
| 345 | if( existsFlag ){ |
| 346 | blob_reset(&content); |
| 347 | } |
| 348 | undoNeedRollback = 1; |
| 349 | result = UNDO_SAVED_OK; |
| 350 | }else{ |
| 351 | result = UNDO_TOOBIG; |
| 352 | } |
| 353 | free(zFullname); |
| 354 | return result; |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | ** Returns an error message for the undo_maybe_save() return code. |
| 359 | ** Currently, this function assumes that the caller is using the |
| 360 | ** returned error message in a context prefixed with "because". |
| 361 | */ |
| 362 | const char *undo_save_message(int rc){ |
| 363 | static char zRc[32]; |
| 364 | |
| 365 | switch( rc ){ |
| 366 | case UNDO_NONE: return "undo is disabled for this operation"; |
| 367 | case UNDO_SAVED_OK: return "the save operation was successful"; |
| 368 | case UNDO_DISABLED: return "the undo subsystem is disabled"; |
| 369 | case UNDO_INACTIVE: return "the undo subsystem is inactive"; |
| 370 | case UNDO_TOOBIG: return "the file is too big"; |
| 371 | default: { |
| 372 | sqlite3_snprintf(sizeof(zRc), zRc, "of error code %d", rc); |
| 373 | } |
| 374 | } |
| 375 | return zRc; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** Make the current state of stashid undoable. |
| 380 | */ |
| @@ -361,10 +440,13 @@ | |
| 440 | ** |
| 441 | ** (1) fossil update (5) fossil stash apply |
| 442 | ** (2) fossil merge (6) fossil stash drop |
| 443 | ** (3) fossil revert (7) fossil stash goto |
| 444 | ** (4) fossil stash pop |
| 445 | ** |
| 446 | ** The "fossil clean" operation can also be undone; however, this is |
| 447 | ** currently limited to files that are less than 10MiB in size. |
| 448 | ** |
| 449 | ** If FILENAME is specified then restore the content of the named |
| 450 | ** file(s) but otherwise leave the update or merge or revert in effect. |
| 451 | ** The redo command undoes the effect of the most recent undo. |
| 452 | ** |
| 453 |
+6
-5
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -424,26 +424,26 @@ | ||
| 424 | 424 | fossil_print("ADD %s - overwrites an unmanaged file\n", zName); |
| 425 | 425 | nOverwrite++; |
| 426 | 426 | }else{ |
| 427 | 427 | fossil_print("ADD %s\n", zName); |
| 428 | 428 | } |
| 429 | - undo_save(zName); | |
| 429 | + if( !dryRunFlag && !internalUpdate ) undo_save(zName); | |
| 430 | 430 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 431 | 431 | }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ |
| 432 | 432 | /* The file is unedited. Change it to the target version */ |
| 433 | - undo_save(zName); | |
| 434 | 433 | if( deleted ){ |
| 435 | 434 | fossil_print("UPDATE %s - change to unmanaged file\n", zName); |
| 436 | 435 | }else{ |
| 437 | 436 | fossil_print("UPDATE %s\n", zName); |
| 438 | 437 | } |
| 438 | + if( !dryRunFlag && !internalUpdate ) undo_save(zName); | |
| 439 | 439 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 440 | 440 | }else if( idt>0 && idv>0 && !deleted && file_wd_size(zFullPath)<0 ){ |
| 441 | 441 | /* The file missing from the local check-out. Restore it to the |
| 442 | 442 | ** version that appears in the target. */ |
| 443 | 443 | fossil_print("UPDATE %s\n", zName); |
| 444 | - undo_save(zName); | |
| 444 | + if( !dryRunFlag && !internalUpdate ) undo_save(zName); | |
| 445 | 445 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 446 | 446 | }else if( idt==0 && idv>0 ){ |
| 447 | 447 | if( ridv==0 ){ |
| 448 | 448 | /* Added in current checkout. Continue to hold the file as |
| 449 | 449 | ** as an addition */ |
| @@ -454,11 +454,11 @@ | ||
| 454 | 454 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 455 | 455 | zName); |
| 456 | 456 | nConflict++; |
| 457 | 457 | }else{ |
| 458 | 458 | fossil_print("REMOVE %s\n", zName); |
| 459 | - undo_save(zName); | |
| 459 | + if( !dryRunFlag && !internalUpdate ) undo_save(zName); | |
| 460 | 460 | if( !dryRunFlag ) file_delete(zFullPath); |
| 461 | 461 | } |
| 462 | 462 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 463 | 463 | /* Merge the changes in the current tree into the target version */ |
| 464 | 464 | Blob r, t, v; |
| @@ -471,11 +471,11 @@ | ||
| 471 | 471 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 472 | 472 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 473 | 473 | nConflict++; |
| 474 | 474 | }else{ |
| 475 | 475 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 476 | - undo_save(zName); | |
| 476 | + if( !dryRunFlag && !internalUpdate ) undo_save(zName); | |
| 477 | 477 | content_get(ridt, &t); |
| 478 | 478 | content_get(ridv, &v); |
| 479 | 479 | rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags); |
| 480 | 480 | if( rc>=0 ){ |
| 481 | 481 | if( !dryRunFlag ){ |
| @@ -738,10 +738,11 @@ | ||
| 738 | 738 | |
| 739 | 739 | if( g.argc>2 ){ |
| 740 | 740 | for(i=2; i<g.argc; i++){ |
| 741 | 741 | Blob fname; |
| 742 | 742 | zFile = mprintf("%/", g.argv[i]); |
| 743 | + blob_zero(&fname); | |
| 743 | 744 | file_tree_name(zFile, &fname, 0, 1); |
| 744 | 745 | db_multi_exec( |
| 745 | 746 | "REPLACE INTO torevert VALUES(%B);" |
| 746 | 747 | "INSERT OR IGNORE INTO torevert" |
| 747 | 748 | " SELECT pathname" |
| 748 | 749 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -424,26 +424,26 @@ | |
| 424 | fossil_print("ADD %s - overwrites an unmanaged file\n", zName); |
| 425 | nOverwrite++; |
| 426 | }else{ |
| 427 | fossil_print("ADD %s\n", zName); |
| 428 | } |
| 429 | undo_save(zName); |
| 430 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 431 | }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ |
| 432 | /* The file is unedited. Change it to the target version */ |
| 433 | undo_save(zName); |
| 434 | if( deleted ){ |
| 435 | fossil_print("UPDATE %s - change to unmanaged file\n", zName); |
| 436 | }else{ |
| 437 | fossil_print("UPDATE %s\n", zName); |
| 438 | } |
| 439 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 440 | }else if( idt>0 && idv>0 && !deleted && file_wd_size(zFullPath)<0 ){ |
| 441 | /* The file missing from the local check-out. Restore it to the |
| 442 | ** version that appears in the target. */ |
| 443 | fossil_print("UPDATE %s\n", zName); |
| 444 | undo_save(zName); |
| 445 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 446 | }else if( idt==0 && idv>0 ){ |
| 447 | if( ridv==0 ){ |
| 448 | /* Added in current checkout. Continue to hold the file as |
| 449 | ** as an addition */ |
| @@ -454,11 +454,11 @@ | |
| 454 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 455 | zName); |
| 456 | nConflict++; |
| 457 | }else{ |
| 458 | fossil_print("REMOVE %s\n", zName); |
| 459 | undo_save(zName); |
| 460 | if( !dryRunFlag ) file_delete(zFullPath); |
| 461 | } |
| 462 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 463 | /* Merge the changes in the current tree into the target version */ |
| 464 | Blob r, t, v; |
| @@ -471,11 +471,11 @@ | |
| 471 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 472 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 473 | nConflict++; |
| 474 | }else{ |
| 475 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 476 | undo_save(zName); |
| 477 | content_get(ridt, &t); |
| 478 | content_get(ridv, &v); |
| 479 | rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags); |
| 480 | if( rc>=0 ){ |
| 481 | if( !dryRunFlag ){ |
| @@ -738,10 +738,11 @@ | |
| 738 | |
| 739 | if( g.argc>2 ){ |
| 740 | for(i=2; i<g.argc; i++){ |
| 741 | Blob fname; |
| 742 | zFile = mprintf("%/", g.argv[i]); |
| 743 | file_tree_name(zFile, &fname, 0, 1); |
| 744 | db_multi_exec( |
| 745 | "REPLACE INTO torevert VALUES(%B);" |
| 746 | "INSERT OR IGNORE INTO torevert" |
| 747 | " SELECT pathname" |
| 748 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -424,26 +424,26 @@ | |
| 424 | fossil_print("ADD %s - overwrites an unmanaged file\n", zName); |
| 425 | nOverwrite++; |
| 426 | }else{ |
| 427 | fossil_print("ADD %s\n", zName); |
| 428 | } |
| 429 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 430 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 431 | }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ |
| 432 | /* The file is unedited. Change it to the target version */ |
| 433 | if( deleted ){ |
| 434 | fossil_print("UPDATE %s - change to unmanaged file\n", zName); |
| 435 | }else{ |
| 436 | fossil_print("UPDATE %s\n", zName); |
| 437 | } |
| 438 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 439 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 440 | }else if( idt>0 && idv>0 && !deleted && file_wd_size(zFullPath)<0 ){ |
| 441 | /* The file missing from the local check-out. Restore it to the |
| 442 | ** version that appears in the target. */ |
| 443 | fossil_print("UPDATE %s\n", zName); |
| 444 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 445 | if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0); |
| 446 | }else if( idt==0 && idv>0 ){ |
| 447 | if( ridv==0 ){ |
| 448 | /* Added in current checkout. Continue to hold the file as |
| 449 | ** as an addition */ |
| @@ -454,11 +454,11 @@ | |
| 454 | fossil_print("CONFLICT %s - edited locally but deleted by update\n", |
| 455 | zName); |
| 456 | nConflict++; |
| 457 | }else{ |
| 458 | fossil_print("REMOVE %s\n", zName); |
| 459 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 460 | if( !dryRunFlag ) file_delete(zFullPath); |
| 461 | } |
| 462 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ |
| 463 | /* Merge the changes in the current tree into the target version */ |
| 464 | Blob r, t, v; |
| @@ -471,11 +471,11 @@ | |
| 471 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 472 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 473 | nConflict++; |
| 474 | }else{ |
| 475 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 476 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 477 | content_get(ridt, &t); |
| 478 | content_get(ridv, &v); |
| 479 | rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags); |
| 480 | if( rc>=0 ){ |
| 481 | if( !dryRunFlag ){ |
| @@ -738,10 +738,11 @@ | |
| 738 | |
| 739 | if( g.argc>2 ){ |
| 740 | for(i=2; i<g.argc; i++){ |
| 741 | Blob fname; |
| 742 | zFile = mprintf("%/", g.argv[i]); |
| 743 | blob_zero(&fname); |
| 744 | file_tree_name(zFile, &fname, 0, 1); |
| 745 | db_multi_exec( |
| 746 | "REPLACE INTO torevert VALUES(%B);" |
| 747 | "INSERT OR IGNORE INTO torevert" |
| 748 | " SELECT pathname" |
| 749 |
+14
-9
| --- src/utf8.c | ||
| +++ src/utf8.c | ||
| @@ -77,11 +77,11 @@ | ||
| 77 | 77 | ** used to store the returned pointer when done. |
| 78 | 78 | */ |
| 79 | 79 | void *fossil_utf8_to_unicode(const char *zUtf8){ |
| 80 | 80 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 81 | 81 | int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 82 | - wchar_t *zUnicode = fossil_malloc( nByte * 2 ); | |
| 82 | + wchar_t *zUnicode = fossil_malloc( nByte*2 ); | |
| 83 | 83 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); |
| 84 | 84 | return zUnicode; |
| 85 | 85 | #else |
| 86 | 86 | assert( 0 ); /* Never used in unix */ |
| 87 | 87 | return fossil_strdup(zUtf8); /* TODO: implement for unix */ |
| @@ -304,11 +304,15 @@ | ||
| 304 | 304 | ** Display UTF-8 on the console. Return the number of |
| 305 | 305 | ** Characters written. If stdout or stderr is redirected |
| 306 | 306 | ** to a file, -1 is returned and nothing is written |
| 307 | 307 | ** to the console. |
| 308 | 308 | */ |
| 309 | -int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){ | |
| 309 | +int fossil_utf8_to_console( | |
| 310 | + const char *zUtf8, | |
| 311 | + int nByte, | |
| 312 | + int toStdErr | |
| 313 | +){ | |
| 310 | 314 | #ifdef _WIN32 |
| 311 | 315 | int nChar, written = 0; |
| 312 | 316 | wchar_t *zUnicode; /* Unicode version of zUtf8 */ |
| 313 | 317 | DWORD dummy; |
| 314 | 318 | Blob blob; |
| @@ -327,27 +331,28 @@ | ||
| 327 | 331 | */ |
| 328 | 332 | blob_init(&blob, zUtf8, nByte); |
| 329 | 333 | blob_to_utf8_no_bom(&blob, 1); |
| 330 | 334 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 331 | 335 | blob_size(&blob), NULL, 0); |
| 332 | - zUnicode = malloc( (nChar + 1) *sizeof(zUnicode[0]) ); | |
| 336 | + zUnicode = fossil_malloc( (nChar+1)*sizeof(zUnicode[0]) ); | |
| 333 | 337 | if( zUnicode==0 ){ |
| 334 | 338 | return 0; |
| 335 | 339 | } |
| 336 | 340 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 337 | 341 | blob_size(&blob), zUnicode, nChar); |
| 338 | 342 | blob_reset(&blob); |
| 339 | - /* Split WriteConsoleW call into multiple chunks, if necessary. See: | |
| 343 | + /* Split WriteConsoleW output into multiple chunks, if necessary. See: | |
| 340 | 344 | * <https://connect.microsoft.com/VisualStudio/feedback/details/635230> */ |
| 341 | - while( written < nChar ){ | |
| 345 | + while( written<nChar ){ | |
| 342 | 346 | int size = nChar-written; |
| 343 | - if( size > 26000 ) size = 26000; | |
| 344 | - WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE - toStdErr), zUnicode+written, | |
| 345 | - size, &dummy, 0); | |
| 347 | + if( size>26000 ) size = 26000; | |
| 348 | + WriteConsoleW(GetStdHandle( | |
| 349 | + toStdErr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), | |
| 350 | + zUnicode + written, size, &dummy, 0); | |
| 346 | 351 | written += size; |
| 347 | 352 | } |
| 348 | - free(zUnicode); | |
| 353 | + fossil_free(zUnicode); | |
| 349 | 354 | return nChar; |
| 350 | 355 | #else |
| 351 | 356 | return -1; /* No-op on unix */ |
| 352 | 357 | #endif |
| 353 | 358 | } |
| 354 | 359 | |
| 355 | 360 | ADDED test/clean.test |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -77,11 +77,11 @@ | |
| 77 | ** used to store the returned pointer when done. |
| 78 | */ |
| 79 | void *fossil_utf8_to_unicode(const char *zUtf8){ |
| 80 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 81 | int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 82 | wchar_t *zUnicode = fossil_malloc( nByte * 2 ); |
| 83 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); |
| 84 | return zUnicode; |
| 85 | #else |
| 86 | assert( 0 ); /* Never used in unix */ |
| 87 | return fossil_strdup(zUtf8); /* TODO: implement for unix */ |
| @@ -304,11 +304,15 @@ | |
| 304 | ** Display UTF-8 on the console. Return the number of |
| 305 | ** Characters written. If stdout or stderr is redirected |
| 306 | ** to a file, -1 is returned and nothing is written |
| 307 | ** to the console. |
| 308 | */ |
| 309 | int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){ |
| 310 | #ifdef _WIN32 |
| 311 | int nChar, written = 0; |
| 312 | wchar_t *zUnicode; /* Unicode version of zUtf8 */ |
| 313 | DWORD dummy; |
| 314 | Blob blob; |
| @@ -327,27 +331,28 @@ | |
| 327 | */ |
| 328 | blob_init(&blob, zUtf8, nByte); |
| 329 | blob_to_utf8_no_bom(&blob, 1); |
| 330 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 331 | blob_size(&blob), NULL, 0); |
| 332 | zUnicode = malloc( (nChar + 1) *sizeof(zUnicode[0]) ); |
| 333 | if( zUnicode==0 ){ |
| 334 | return 0; |
| 335 | } |
| 336 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 337 | blob_size(&blob), zUnicode, nChar); |
| 338 | blob_reset(&blob); |
| 339 | /* Split WriteConsoleW call into multiple chunks, if necessary. See: |
| 340 | * <https://connect.microsoft.com/VisualStudio/feedback/details/635230> */ |
| 341 | while( written < nChar ){ |
| 342 | int size = nChar-written; |
| 343 | if( size > 26000 ) size = 26000; |
| 344 | WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE - toStdErr), zUnicode+written, |
| 345 | size, &dummy, 0); |
| 346 | written += size; |
| 347 | } |
| 348 | free(zUnicode); |
| 349 | return nChar; |
| 350 | #else |
| 351 | return -1; /* No-op on unix */ |
| 352 | #endif |
| 353 | } |
| 354 | |
| 355 | DDED test/clean.test |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -77,11 +77,11 @@ | |
| 77 | ** used to store the returned pointer when done. |
| 78 | */ |
| 79 | void *fossil_utf8_to_unicode(const char *zUtf8){ |
| 80 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 81 | int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 82 | wchar_t *zUnicode = fossil_malloc( nByte*2 ); |
| 83 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); |
| 84 | return zUnicode; |
| 85 | #else |
| 86 | assert( 0 ); /* Never used in unix */ |
| 87 | return fossil_strdup(zUtf8); /* TODO: implement for unix */ |
| @@ -304,11 +304,15 @@ | |
| 304 | ** Display UTF-8 on the console. Return the number of |
| 305 | ** Characters written. If stdout or stderr is redirected |
| 306 | ** to a file, -1 is returned and nothing is written |
| 307 | ** to the console. |
| 308 | */ |
| 309 | int fossil_utf8_to_console( |
| 310 | const char *zUtf8, |
| 311 | int nByte, |
| 312 | int toStdErr |
| 313 | ){ |
| 314 | #ifdef _WIN32 |
| 315 | int nChar, written = 0; |
| 316 | wchar_t *zUnicode; /* Unicode version of zUtf8 */ |
| 317 | DWORD dummy; |
| 318 | Blob blob; |
| @@ -327,27 +331,28 @@ | |
| 331 | */ |
| 332 | blob_init(&blob, zUtf8, nByte); |
| 333 | blob_to_utf8_no_bom(&blob, 1); |
| 334 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 335 | blob_size(&blob), NULL, 0); |
| 336 | zUnicode = fossil_malloc( (nChar+1)*sizeof(zUnicode[0]) ); |
| 337 | if( zUnicode==0 ){ |
| 338 | return 0; |
| 339 | } |
| 340 | nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), |
| 341 | blob_size(&blob), zUnicode, nChar); |
| 342 | blob_reset(&blob); |
| 343 | /* Split WriteConsoleW output into multiple chunks, if necessary. See: |
| 344 | * <https://connect.microsoft.com/VisualStudio/feedback/details/635230> */ |
| 345 | while( written<nChar ){ |
| 346 | int size = nChar-written; |
| 347 | if( size>26000 ) size = 26000; |
| 348 | WriteConsoleW(GetStdHandle( |
| 349 | toStdErr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE), |
| 350 | zUnicode + written, size, &dummy, 0); |
| 351 | written += size; |
| 352 | } |
| 353 | fossil_free(zUnicode); |
| 354 | return nChar; |
| 355 | #else |
| 356 | return -1; /* No-op on unix */ |
| 357 | #endif |
| 358 | } |
| 359 | |
| 360 | DDED test/clean.test |
+157
| --- a/test/clean.test | ||
| +++ b/test/clean.test | ||
| @@ -0,0 +1,157 @@ | ||
| 1 | +# | |
| 2 | +# Copyright (c) 2015 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 | +# Tests of the "clean" command, including the ability to undo it. | |
| 19 | +# | |
| 20 | + | |
| 21 | +repo_init########### | |
| 22 | + | |
| 23 | +fossil extra | |
| 24 | +test clean-0 {[normalize_result] eq {}} | |
| 25 | + | |
| 26 | +#############0bute it and/or | |
| 27 | +# modify it under the terms of the Simplified BSD License (also | |
| 28 | +# kn# | |
| 29 | +# Copyright (c) 2015 D. Richard Hipp | |
| 30 | +# | |
| 31 | +# This program is free software; you can redistribute it and/or | |
| 32 | +# modify it under the terms of the Simplified BSD License (also | |
| 33 | +# known as the "2-Clause License" or "FreeBSD License".) | |
| 34 | +# | |
| 35 | +# This program is distributed in the hope that it will be useful, | |
| 36 | +# but without any warranty; without even the implied warranty of | |
| 37 | +# merchantability or fitness for a particular purpose. | |
| 38 | +# | |
| 39 | +# Author contact information: | |
| 40 | +# [email protected] | |
| 41 | +# http://www.hwaci.com/drh/ | |
| 42 | +# | |
| 43 | +############################################################################ | |
| 44 | +# | |
| 45 | +# Tests of the "clean" command, including the ability to undo it. | |
| 46 | +# | |
| 47 | + | |
| 48 | +test_setup | |
| 49 | + | |
| 50 | +############################################################################### | |
| 51 | + | |
| 52 | +fossil extra | |
| 53 | +test clean-0 {[normalize_result] eq {}} | |
| 54 | + | |
| 55 | +############################################################################### | |
| 56 | + | |
| 57 | +write_file f1 "f1 line" | |
| 58 | +fossil add f1 | |
| 59 | +fossil comq {NEW f2}} | |
| 60 | +te3q {NEW f2}} | |
| 61 | +test clean-7 {[r#################### | |
| 62 | + | |
| 63 | +fossil extra | |
| 64 | +test clean-2 {[normalize_result] eq {}} | |
| 65 | + | |
| 66 | +############################################################################### | |
| 67 | + | |
| 68 | +write_file f2 "f2 line" | |
| 69 | +fossil extra | |
| 70 | +test clean-3 {[normalize_result] eq {f2}} | |
| 71 | + | |
| 72 | +############################################################################### | |
| 73 | + | |
| 74 | +# clean w/undo enabled, should not prompt, 1 file < 10MiB | |
| 75 | +fossil clean | |
| 76 | +test clean-4 {[normalize_result] eq \ | |
| 77 | +{"fossil undo" is available to undo changes to the working checkout.}} | |
| 78 | + | |
| 79 | +############################################################################### | |
| 80 | + | |
| 81 | +fossil extra | |
| 82 | +test clean-5 {[normalize_result] eq {}} | |
| 83 | + | |
| 84 | +############################################################################### | |
| 85 | + | |
| 86 | +fossil undo | |
| 87 | +testq {NEW f2}} | |
| 88 | +test clean-7 {[read_file f2] eq "f2 line"} | |
| 89 | + | |
| 90 | +############################################################################### | |
| 91 | + | |
| 92 | +fossil extra | |
| 93 | +test clean-8 {[normalize_result] eq {f2}} | |
| 94 | + | |
| 95 | +############################################################################### | |
| 96 | + | |
| 97 | +write_file f3 [string repeat ABCDEFGHIJK 1048576] | |
| 98 | +fossil extra | |
| 99 | +test clean-9 {[normalize_result] eq {f2 | |
| 100 | +f3}} | |
| 101 | + | |
| 102 | +############################################################################### | |
| 103 | + | |
| 104 | +# clean w/undo enabled, no prompt, 1 file < 10MiB, 1 file > 10MiB | |
| 105 | +fossil clean --no-prompt | |
| 106 | +test clean-10 {[normalize_result] eq \ | |
| 107 | +{"fossil undo" is available to undo changes to the working checkout.}} | |
| 108 | + | |
| 109 | +#################################### | |
| 110 | + | |
| 111 | +fossil undo -expectError | |
| 112 | +test clean-23 {[normalize_result] eq {nothing to undo}} | |
| 113 | + | |
| 114 | +############################################################################### | |
| 115 | + | |
| 116 | +# clean w/undo disabled, force, 1 file < 10MiB, 1 file > 10MiB | |
| 117 | +fossil clean --disable-undo --force | |
| 118 | +test clean-24 {[normalize_result] eq {}} | |
| 119 | + | |
| 120 | +############################################################################### | |
| 121 | + | |
| 122 | +fossil extra | |
| 123 | +test clean-25 {[normalize_result] eq {}} | |
| 124 | + | |
| 125 | +###################################### | |
| 126 | +test clean-26 {[normalize_result] eq {nothing to undo}} | |
| 127 | + | |
| 128 | +############################################################################### | |
| 129 | + | |
| 130 | +write_file f5 "f5 line" | |
| 131 | +fossil extra | |
| 132 | +test clean-27 {[normalize_result] eq {f5}} | |
| 133 | + | |
| 134 | +############################################################################### | |
| 135 | + | |
| 136 | +# clean w/undo disabled, should prompt, 1 file < 10MiB | |
| 137 | +fossil_maybe_answer Y clean --disable-undo | |
| 138 | +test clean-28 {[normalize_result] eq \ | |
| 139 | +{WARNING: Deletion of this file will not be undoable via the 'undo' | |
| 140 | + command because undo is disabled for this operation. | |
| 141 | + | |
| 142 | +Remove unmanaged file "f5" (a=all/y/N)?}} | |
| 143 | + | |
| 144 | +############################################################################### | |
| 145 | + | |
| 146 | +fossil extra | |
| 147 | +test clean-29 {[normalize_result] eq {}} | |
| 148 | + | |
| 149 | +############################################################################### | |
| 150 | + | |
| 151 | +fossil undo | |
| 152 | +test clean-30 {[normalize_result] eq {nothing to undo}} | |
| 153 | + | |
| 154 | +############################################################################### | |
| 155 | + | |
| 156 | +fossil extra | |
| 157 | +test clean-31 {[normalize_result] eq {}} |
| --- a/test/clean.test | |
| +++ b/test/clean.test | |
| @@ -0,0 +1,157 @@ | |
| --- a/test/clean.test | |
| +++ b/test/clean.test | |
| @@ -0,0 +1,157 @@ | |
| 1 | # |
| 2 | # Copyright (c) 2015 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 | # Tests of the "clean" command, including the ability to undo it. |
| 19 | # |
| 20 | |
| 21 | repo_init########### |
| 22 | |
| 23 | fossil extra |
| 24 | test clean-0 {[normalize_result] eq {}} |
| 25 | |
| 26 | #############0bute it and/or |
| 27 | # modify it under the terms of the Simplified BSD License (also |
| 28 | # kn# |
| 29 | # Copyright (c) 2015 D. Richard Hipp |
| 30 | # |
| 31 | # This program is free software; you can redistribute it and/or |
| 32 | # modify it under the terms of the Simplified BSD License (also |
| 33 | # known as the "2-Clause License" or "FreeBSD License".) |
| 34 | # |
| 35 | # This program is distributed in the hope that it will be useful, |
| 36 | # but without any warranty; without even the implied warranty of |
| 37 | # merchantability or fitness for a particular purpose. |
| 38 | # |
| 39 | # Author contact information: |
| 40 | # [email protected] |
| 41 | # http://www.hwaci.com/drh/ |
| 42 | # |
| 43 | ############################################################################ |
| 44 | # |
| 45 | # Tests of the "clean" command, including the ability to undo it. |
| 46 | # |
| 47 | |
| 48 | test_setup |
| 49 | |
| 50 | ############################################################################### |
| 51 | |
| 52 | fossil extra |
| 53 | test clean-0 {[normalize_result] eq {}} |
| 54 | |
| 55 | ############################################################################### |
| 56 | |
| 57 | write_file f1 "f1 line" |
| 58 | fossil add f1 |
| 59 | fossil comq {NEW f2}} |
| 60 | te3q {NEW f2}} |
| 61 | test clean-7 {[r#################### |
| 62 | |
| 63 | fossil extra |
| 64 | test clean-2 {[normalize_result] eq {}} |
| 65 | |
| 66 | ############################################################################### |
| 67 | |
| 68 | write_file f2 "f2 line" |
| 69 | fossil extra |
| 70 | test clean-3 {[normalize_result] eq {f2}} |
| 71 | |
| 72 | ############################################################################### |
| 73 | |
| 74 | # clean w/undo enabled, should not prompt, 1 file < 10MiB |
| 75 | fossil clean |
| 76 | test clean-4 {[normalize_result] eq \ |
| 77 | {"fossil undo" is available to undo changes to the working checkout.}} |
| 78 | |
| 79 | ############################################################################### |
| 80 | |
| 81 | fossil extra |
| 82 | test clean-5 {[normalize_result] eq {}} |
| 83 | |
| 84 | ############################################################################### |
| 85 | |
| 86 | fossil undo |
| 87 | testq {NEW f2}} |
| 88 | test clean-7 {[read_file f2] eq "f2 line"} |
| 89 | |
| 90 | ############################################################################### |
| 91 | |
| 92 | fossil extra |
| 93 | test clean-8 {[normalize_result] eq {f2}} |
| 94 | |
| 95 | ############################################################################### |
| 96 | |
| 97 | write_file f3 [string repeat ABCDEFGHIJK 1048576] |
| 98 | fossil extra |
| 99 | test clean-9 {[normalize_result] eq {f2 |
| 100 | f3}} |
| 101 | |
| 102 | ############################################################################### |
| 103 | |
| 104 | # clean w/undo enabled, no prompt, 1 file < 10MiB, 1 file > 10MiB |
| 105 | fossil clean --no-prompt |
| 106 | test clean-10 {[normalize_result] eq \ |
| 107 | {"fossil undo" is available to undo changes to the working checkout.}} |
| 108 | |
| 109 | #################################### |
| 110 | |
| 111 | fossil undo -expectError |
| 112 | test clean-23 {[normalize_result] eq {nothing to undo}} |
| 113 | |
| 114 | ############################################################################### |
| 115 | |
| 116 | # clean w/undo disabled, force, 1 file < 10MiB, 1 file > 10MiB |
| 117 | fossil clean --disable-undo --force |
| 118 | test clean-24 {[normalize_result] eq {}} |
| 119 | |
| 120 | ############################################################################### |
| 121 | |
| 122 | fossil extra |
| 123 | test clean-25 {[normalize_result] eq {}} |
| 124 | |
| 125 | ###################################### |
| 126 | test clean-26 {[normalize_result] eq {nothing to undo}} |
| 127 | |
| 128 | ############################################################################### |
| 129 | |
| 130 | write_file f5 "f5 line" |
| 131 | fossil extra |
| 132 | test clean-27 {[normalize_result] eq {f5}} |
| 133 | |
| 134 | ############################################################################### |
| 135 | |
| 136 | # clean w/undo disabled, should prompt, 1 file < 10MiB |
| 137 | fossil_maybe_answer Y clean --disable-undo |
| 138 | test clean-28 {[normalize_result] eq \ |
| 139 | {WARNING: Deletion of this file will not be undoable via the 'undo' |
| 140 | command because undo is disabled for this operation. |
| 141 | |
| 142 | Remove unmanaged file "f5" (a=all/y/N)?}} |
| 143 | |
| 144 | ############################################################################### |
| 145 | |
| 146 | fossil extra |
| 147 | test clean-29 {[normalize_result] eq {}} |
| 148 | |
| 149 | ############################################################################### |
| 150 | |
| 151 | fossil undo |
| 152 | test clean-30 {[normalize_result] eq {nothing to undo}} |
| 153 | |
| 154 | ############################################################################### |
| 155 | |
| 156 | fossil extra |
| 157 | test clean-31 {[normalize_result] eq {}} |
+26
-2
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -87,22 +87,46 @@ | ||
| 87 | 87 | puts $out $msg |
| 88 | 88 | close $out |
| 89 | 89 | } |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | -# Run the fossil program | |
| 92 | +# Run the Fossil program with the specified arguments. | |
| 93 | +# | |
| 94 | +# Consults the VERBOSE global variable to determine if | |
| 95 | +# diagnostics should be emitted when no error is seen. | |
| 96 | +# Sets the CODE and RESULT global variables for use in | |
| 97 | +# test expressions. | |
| 93 | 98 | # |
| 94 | 99 | proc fossil {args} { |
| 100 | + return [uplevel 1 fossil_maybe_answer [list ""] $args] | |
| 101 | +} | |
| 102 | + | |
| 103 | +# Run the Fossil program with the specified arguments | |
| 104 | +# and possibly answer the first prompt, if any. | |
| 105 | +# | |
| 106 | +# Consults the VERBOSE global variable to determine if | |
| 107 | +# diagnostics should be emitted when no error is seen. | |
| 108 | +# Sets the CODE and RESULT global variables for use in | |
| 109 | +# test expressions. | |
| 110 | +# | |
| 111 | +proc fossil_maybe_answer {answer args} { | |
| 95 | 112 | global fossilexe |
| 96 | 113 | set cmd $fossilexe |
| 97 | 114 | foreach a $args { |
| 98 | 115 | lappend cmd $a |
| 99 | 116 | } |
| 100 | 117 | protOut $cmd |
| 101 | 118 | |
| 102 | 119 | flush stdout |
| 103 | - set rc [catch {eval exec $cmd} result] | |
| 120 | + if {[string length $answer] > 0} { | |
| 121 | + set prompt_file [file join $::tempPath fossil_prompt_answer] | |
| 122 | + write_file $prompt_file $answer\n | |
| 123 | + set rc [catch {eval exec $cmd <$prompt_file} result] | |
| 124 | + file delete $prompt_file | |
| 125 | + } else { | |
| 126 | + set rc [catch {eval exec $cmd} result] | |
| 127 | + } | |
| 104 | 128 | global RESULT CODE |
| 105 | 129 | set CODE $rc |
| 106 | 130 | if {$rc} { |
| 107 | 131 | protOut "ERROR: $result" |
| 108 | 132 | } elseif {$::VERBOSE} { |
| 109 | 133 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -87,22 +87,46 @@ | |
| 87 | puts $out $msg |
| 88 | close $out |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | # Run the fossil program |
| 93 | # |
| 94 | proc fossil {args} { |
| 95 | global fossilexe |
| 96 | set cmd $fossilexe |
| 97 | foreach a $args { |
| 98 | lappend cmd $a |
| 99 | } |
| 100 | protOut $cmd |
| 101 | |
| 102 | flush stdout |
| 103 | set rc [catch {eval exec $cmd} result] |
| 104 | global RESULT CODE |
| 105 | set CODE $rc |
| 106 | if {$rc} { |
| 107 | protOut "ERROR: $result" |
| 108 | } elseif {$::VERBOSE} { |
| 109 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -87,22 +87,46 @@ | |
| 87 | puts $out $msg |
| 88 | close $out |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | # Run the Fossil program with the specified arguments. |
| 93 | # |
| 94 | # Consults the VERBOSE global variable to determine if |
| 95 | # diagnostics should be emitted when no error is seen. |
| 96 | # Sets the CODE and RESULT global variables for use in |
| 97 | # test expressions. |
| 98 | # |
| 99 | proc fossil {args} { |
| 100 | return [uplevel 1 fossil_maybe_answer [list ""] $args] |
| 101 | } |
| 102 | |
| 103 | # Run the Fossil program with the specified arguments |
| 104 | # and possibly answer the first prompt, if any. |
| 105 | # |
| 106 | # Consults the VERBOSE global variable to determine if |
| 107 | # diagnostics should be emitted when no error is seen. |
| 108 | # Sets the CODE and RESULT global variables for use in |
| 109 | # test expressions. |
| 110 | # |
| 111 | proc fossil_maybe_answer {answer args} { |
| 112 | global fossilexe |
| 113 | set cmd $fossilexe |
| 114 | foreach a $args { |
| 115 | lappend cmd $a |
| 116 | } |
| 117 | protOut $cmd |
| 118 | |
| 119 | flush stdout |
| 120 | if {[string length $answer] > 0} { |
| 121 | set prompt_file [file join $::tempPath fossil_prompt_answer] |
| 122 | write_file $prompt_file $answer\n |
| 123 | set rc [catch {eval exec $cmd <$prompt_file} result] |
| 124 | file delete $prompt_file |
| 125 | } else { |
| 126 | set rc [catch {eval exec $cmd} result] |
| 127 | } |
| 128 | global RESULT CODE |
| 129 | set CODE $rc |
| 130 | if {$rc} { |
| 131 | protOut "ERROR: $result" |
| 132 | } elseif {$::VERBOSE} { |
| 133 |
+42
| --- test/th1-tcl.test | ||
| +++ test/th1-tcl.test | ||
| @@ -123,5 +123,47 @@ | ||
| 123 | 123 | [file nativename [file join $dir th1-tcl9.txt]] |
| 124 | 124 | |
| 125 | 125 | test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 3 \ |
| 126 | 126 | [list test-th-render --open-config [file nativename [file join $dir \ |
| 127 | 127 | th1-tcl9.txt]]]]} |
| 128 | + | |
| 129 | +############################################################################### | |
| 130 | + | |
| 131 | +fossil test-th-eval "tclMakeSafe a" | |
| 132 | +test th1-tcl-10 {[normalize_result] eq \ | |
| 133 | +{TH_ERROR: wrong # args: should be "tclMakeSafe"}} | |
| 134 | + | |
| 135 | +############################################################################### | |
| 136 | + | |
| 137 | +fossil test-th-eval "list \[tclIsSafe\] \[tclMakeSafe\] \[tclIsSafe\]" | |
| 138 | +test th1-tcl-11 {[normalize_result] eq {0 {} 1}} | |
| 139 | + | |
| 140 | +############################################################################### | |
| 141 | + | |
| 142 | +fossil test-th-eval "tclMakeSafe; tclMakeSafe" | |
| 143 | +test th1-tcl-12 {[normalize_result] eq \ | |
| 144 | +{TH_ERROR: Tcl interpreter is already 'safe'}} | |
| 145 | + | |
| 146 | +############################################################################### | |
| 147 | + | |
| 148 | +fossil test-th-eval "tclEval pwd; tclMakeSafe; tclEval pwd" | |
| 149 | +test th1-tcl-13 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} | |
| 150 | + | |
| 151 | +############################################################################### | |
| 152 | + | |
| 153 | +fossil test-th-eval "tclMakeSafe; tclExpr {0 + \[string length \[pwd\]\]}" | |
| 154 | +test th1-tcl-14 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} | |
| 155 | + | |
| 156 | +############################################################################### | |
| 157 | + | |
| 158 | +fossil test-th-eval "tclInvoke pwd; tclMakeSafe; tclInvoke pwd" | |
| 159 | +test th1-tcl-15 {[normalize_result] eq {TH_ERROR: Tcl command not found: pwd}} | |
| 160 | + | |
| 161 | +############################################################################### | |
| 162 | + | |
| 163 | +fossil test-th-eval "tclMakeSafe; tclEval set x 2" | |
| 164 | +test th1-tcl-16 {[normalize_result] eq {2}} | |
| 165 | + | |
| 166 | +############################################################################### | |
| 167 | + | |
| 168 | +fossil test-th-eval "tclMakeSafe; tclEval set x 2; tclEval info vars x" | |
| 169 | +test th1-tcl-17 {[normalize_result] eq {x}} | |
| 128 | 170 |
| --- test/th1-tcl.test | |
| +++ test/th1-tcl.test | |
| @@ -123,5 +123,47 @@ | |
| 123 | [file nativename [file join $dir th1-tcl9.txt]] |
| 124 | |
| 125 | test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 3 \ |
| 126 | [list test-th-render --open-config [file nativename [file join $dir \ |
| 127 | th1-tcl9.txt]]]]} |
| 128 |
| --- test/th1-tcl.test | |
| +++ test/th1-tcl.test | |
| @@ -123,5 +123,47 @@ | |
| 123 | [file nativename [file join $dir th1-tcl9.txt]] |
| 124 | |
| 125 | test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 3 \ |
| 126 | [list test-th-render --open-config [file nativename [file join $dir \ |
| 127 | th1-tcl9.txt]]]]} |
| 128 | |
| 129 | ############################################################################### |
| 130 | |
| 131 | fossil test-th-eval "tclMakeSafe a" |
| 132 | test th1-tcl-10 {[normalize_result] eq \ |
| 133 | {TH_ERROR: wrong # args: should be "tclMakeSafe"}} |
| 134 | |
| 135 | ############################################################################### |
| 136 | |
| 137 | fossil test-th-eval "list \[tclIsSafe\] \[tclMakeSafe\] \[tclIsSafe\]" |
| 138 | test th1-tcl-11 {[normalize_result] eq {0 {} 1}} |
| 139 | |
| 140 | ############################################################################### |
| 141 | |
| 142 | fossil test-th-eval "tclMakeSafe; tclMakeSafe" |
| 143 | test th1-tcl-12 {[normalize_result] eq \ |
| 144 | {TH_ERROR: Tcl interpreter is already 'safe'}} |
| 145 | |
| 146 | ############################################################################### |
| 147 | |
| 148 | fossil test-th-eval "tclEval pwd; tclMakeSafe; tclEval pwd" |
| 149 | test th1-tcl-13 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} |
| 150 | |
| 151 | ############################################################################### |
| 152 | |
| 153 | fossil test-th-eval "tclMakeSafe; tclExpr {0 + \[string length \[pwd\]\]}" |
| 154 | test th1-tcl-14 {[normalize_result] eq {TH_ERROR: invalid command name "pwd"}} |
| 155 | |
| 156 | ############################################################################### |
| 157 | |
| 158 | fossil test-th-eval "tclInvoke pwd; tclMakeSafe; tclInvoke pwd" |
| 159 | test th1-tcl-15 {[normalize_result] eq {TH_ERROR: Tcl command not found: pwd}} |
| 160 | |
| 161 | ############################################################################### |
| 162 | |
| 163 | fossil test-th-eval "tclMakeSafe; tclEval set x 2" |
| 164 | test th1-tcl-16 {[normalize_result] eq {2}} |
| 165 | |
| 166 | ############################################################################### |
| 167 | |
| 168 | fossil test-th-eval "tclMakeSafe; tclEval set x 2; tclEval info vars x" |
| 169 | test th1-tcl-17 {[normalize_result] eq {x}} |
| 170 |
+159
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -1012,5 +1012,164 @@ | ||
| 1012 | 1012 | |
| 1013 | 1013 | ############################################################################### |
| 1014 | 1014 | |
| 1015 | 1015 | fossil test-th-eval {list [glob_match {a[bd]c} abc] [glob_match abc {a[bd]c}]} |
| 1016 | 1016 | test th1-glob-match-13 {$RESULT eq "1 0"} |
| 1017 | + | |
| 1018 | +############################################################################### | |
| 1019 | + | |
| 1020 | +fossil test-th-eval {string is} | |
| 1021 | +test th1-string-is-1 {$RESULT eq \ | |
| 1022 | +{TH_ERROR: wrong # args: should be "string is class string"}} | |
| 1023 | + | |
| 1024 | +############################################################################### | |
| 1025 | + | |
| 1026 | +fossil test-th-eval {string is something} | |
| 1027 | +test th1-string-is-2 {$RESULT eq \ | |
| 1028 | +{TH_ERROR: wrong # args: should be "string is class string"}} | |
| 1029 | + | |
| 1030 | +############################################################################### | |
| 1031 | + | |
| 1032 | +fossil test-th-eval {string is not something else} | |
| 1033 | +test th1-string-is-3 {$RESULT eq \ | |
| 1034 | +{TH_ERROR: wrong # args: should be "string is class string"}} | |
| 1035 | + | |
| 1036 | +############################################################################### | |
| 1037 | + | |
| 1038 | +fossil test-th-eval {string is other 123} | |
| 1039 | +test th1-string-is-4 {$RESULT eq \ | |
| 1040 | +"TH_ERROR: Expected alnum, double, integer, or list, got: other"} | |
| 1041 | + | |
| 1042 | +############################################################################### | |
| 1043 | + | |
| 1044 | +fossil test-th-eval {string is alnum 123} | |
| 1045 | +test th1-string-is-5 {$RESULT eq "1"} | |
| 1046 | + | |
| 1047 | +############################################################################### | |
| 1048 | + | |
| 1049 | +fossil test-th-eval {string is alnum abc} | |
| 1050 | +test th1-string-is-6 {$RESULT eq "1"} | |
| 1051 | + | |
| 1052 | +############################################################################### | |
| 1053 | + | |
| 1054 | +fossil test-th-eval {string is alnum 123abc} | |
| 1055 | +test th1-string-is-7 {$RESULT eq "1"} | |
| 1056 | + | |
| 1057 | +############################################################################### | |
| 1058 | + | |
| 1059 | +fossil test-th-eval {string is alnum abc123} | |
| 1060 | +test th1-string-is-8 {$RESULT eq "1"} | |
| 1061 | + | |
| 1062 | +############################################################################### | |
| 1063 | + | |
| 1064 | +fossil test-th-eval {string is alnum _abc123} | |
| 1065 | +test th1-string-is-9 {$RESULT eq "0"} | |
| 1066 | + | |
| 1067 | +############################################################################### | |
| 1068 | + | |
| 1069 | +fossil test-th-eval {string is alnum abc.123} | |
| 1070 | +test th1-string-is-10 {$RESULT eq "0"} | |
| 1071 | + | |
| 1072 | +############################################################################### | |
| 1073 | + | |
| 1074 | +fossil test-th-eval {string is alnum abc123_} | |
| 1075 | +test th1-string-is-11 {$RESULT eq "0"} | |
| 1076 | + | |
| 1077 | +############################################################################### | |
| 1078 | + | |
| 1079 | +fossil test-th-eval {string is list ""} | |
| 1080 | +test th1-string-is-12 {$RESULT eq "1"} | |
| 1081 | + | |
| 1082 | +############################################################################### | |
| 1083 | + | |
| 1084 | +fossil test-th-eval {string is list 1} | |
| 1085 | +test th1-string-is-13 {$RESULT eq "1"} | |
| 1086 | + | |
| 1087 | +############################################################################### | |
| 1088 | + | |
| 1089 | +fossil test-th-eval {string is list "1 2 3"} | |
| 1090 | +test th1-string-is-14 {$RESULT eq "1"} | |
| 1091 | + | |
| 1092 | +############################################################################### | |
| 1093 | + | |
| 1094 | +fossil test-th-eval {string is list "\{"} | |
| 1095 | +test th1-string-is-15 {$RESULT eq "0"} | |
| 1096 | + | |
| 1097 | +############################################################################### | |
| 1098 | + | |
| 1099 | +fossil test-th-eval {string is list "1 2 3 \{"} | |
| 1100 | +test th1-string-is-16 {$RESULT eq "0"} | |
| 1101 | + | |
| 1102 | +############################################################################### | |
| 1103 | + | |
| 1104 | +fossil test-th-eval {string is list "1 2 3 \{\}"} | |
| 1105 | +test th1-string-is-17 {$RESULT eq "1"} | |
| 1106 | + | |
| 1107 | +############################################################################### | |
| 1108 | + | |
| 1109 | +fossil test-th-eval {string is list "1 2 3 \{\{\}"} | |
| 1110 | +test th1-string-is-18 {$RESULT eq "0"} | |
| 1111 | + | |
| 1112 | +############################################################################### | |
| 1113 | + | |
| 1114 | +fossil test-th-eval {string is double 123} | |
| 1115 | +test th1-string-is-19 {$RESULT eq "1"} | |
| 1116 | + | |
| 1117 | +############################################################################### | |
| 1118 | + | |
| 1119 | +fossil test-th-eval {string is double 123.456} | |
| 1120 | +test th1-string-is-20 {$RESULT eq "1"} | |
| 1121 | + | |
| 1122 | +############################################################################### | |
| 1123 | + | |
| 1124 | +fossil test-th-eval {string is double 123abc} | |
| 1125 | +test th1-string-is-21 {$RESULT eq "0"} | |
| 1126 | + | |
| 1127 | +############################################################################### | |
| 1128 | + | |
| 1129 | +fossil test-th-eval {string is double 123_456} | |
| 1130 | +test th1-string-is-22 {$RESULT eq "0"} | |
| 1131 | + | |
| 1132 | +############################################################################### | |
| 1133 | + | |
| 1134 | +fossil test-th-eval {string is integer 123} | |
| 1135 | +test th1-string-is-23 {$RESULT eq "1"} | |
| 1136 | + | |
| 1137 | +############################################################################### | |
| 1138 | + | |
| 1139 | +fossil test-th-eval {string is integer 123.456} | |
| 1140 | +test th1-string-is-24 {$RESULT eq "0"} | |
| 1141 | + | |
| 1142 | +############################################################################### | |
| 1143 | + | |
| 1144 | +fossil test-th-eval {string is integer 123abc} | |
| 1145 | +test th1-string-is-25 {$RESULT eq "0"} | |
| 1146 | + | |
| 1147 | +############################################################################### | |
| 1148 | + | |
| 1149 | +fossil test-th-eval {string is integer 0b11001001} | |
| 1150 | +test th1-string-is-26 {$RESULT eq "1"} | |
| 1151 | + | |
| 1152 | +############################################################################### | |
| 1153 | + | |
| 1154 | +fossil test-th-eval {string is integer 0b11001002} | |
| 1155 | +test th1-string-is-27 {$RESULT eq "0"} | |
| 1156 | + | |
| 1157 | +############################################################################### | |
| 1158 | + | |
| 1159 | +fossil test-th-eval {string is integer 0o777} | |
| 1160 | +test th1-string-is-28 {$RESULT eq "1"} | |
| 1161 | + | |
| 1162 | +############################################################################### | |
| 1163 | + | |
| 1164 | +fossil test-th-eval {string is integer 0o778} | |
| 1165 | +test th1-string-is-29 {$RESULT eq "0"} | |
| 1166 | + | |
| 1167 | +############################################################################### | |
| 1168 | + | |
| 1169 | +fossil test-th-eval {string is integer 0xC0DEF00D} | |
| 1170 | +test th1-string-is-30 {$RESULT eq "1"} | |
| 1171 | + | |
| 1172 | +############################################################################### | |
| 1173 | + | |
| 1174 | +fossil test-th-eval {string is integer 0xC0DEF00Z} | |
| 1175 | +test th1-string-is-31 {$RESULT eq "0"} | |
| 1017 | 1176 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -1012,5 +1012,164 @@ | |
| 1012 | |
| 1013 | ############################################################################### |
| 1014 | |
| 1015 | fossil test-th-eval {list [glob_match {a[bd]c} abc] [glob_match abc {a[bd]c}]} |
| 1016 | test th1-glob-match-13 {$RESULT eq "1 0"} |
| 1017 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -1012,5 +1012,164 @@ | |
| 1012 | |
| 1013 | ############################################################################### |
| 1014 | |
| 1015 | fossil test-th-eval {list [glob_match {a[bd]c} abc] [glob_match abc {a[bd]c}]} |
| 1016 | test th1-glob-match-13 {$RESULT eq "1 0"} |
| 1017 | |
| 1018 | ############################################################################### |
| 1019 | |
| 1020 | fossil test-th-eval {string is} |
| 1021 | test th1-string-is-1 {$RESULT eq \ |
| 1022 | {TH_ERROR: wrong # args: should be "string is class string"}} |
| 1023 | |
| 1024 | ############################################################################### |
| 1025 | |
| 1026 | fossil test-th-eval {string is something} |
| 1027 | test th1-string-is-2 {$RESULT eq \ |
| 1028 | {TH_ERROR: wrong # args: should be "string is class string"}} |
| 1029 | |
| 1030 | ############################################################################### |
| 1031 | |
| 1032 | fossil test-th-eval {string is not something else} |
| 1033 | test th1-string-is-3 {$RESULT eq \ |
| 1034 | {TH_ERROR: wrong # args: should be "string is class string"}} |
| 1035 | |
| 1036 | ############################################################################### |
| 1037 | |
| 1038 | fossil test-th-eval {string is other 123} |
| 1039 | test th1-string-is-4 {$RESULT eq \ |
| 1040 | "TH_ERROR: Expected alnum, double, integer, or list, got: other"} |
| 1041 | |
| 1042 | ############################################################################### |
| 1043 | |
| 1044 | fossil test-th-eval {string is alnum 123} |
| 1045 | test th1-string-is-5 {$RESULT eq "1"} |
| 1046 | |
| 1047 | ############################################################################### |
| 1048 | |
| 1049 | fossil test-th-eval {string is alnum abc} |
| 1050 | test th1-string-is-6 {$RESULT eq "1"} |
| 1051 | |
| 1052 | ############################################################################### |
| 1053 | |
| 1054 | fossil test-th-eval {string is alnum 123abc} |
| 1055 | test th1-string-is-7 {$RESULT eq "1"} |
| 1056 | |
| 1057 | ############################################################################### |
| 1058 | |
| 1059 | fossil test-th-eval {string is alnum abc123} |
| 1060 | test th1-string-is-8 {$RESULT eq "1"} |
| 1061 | |
| 1062 | ############################################################################### |
| 1063 | |
| 1064 | fossil test-th-eval {string is alnum _abc123} |
| 1065 | test th1-string-is-9 {$RESULT eq "0"} |
| 1066 | |
| 1067 | ############################################################################### |
| 1068 | |
| 1069 | fossil test-th-eval {string is alnum abc.123} |
| 1070 | test th1-string-is-10 {$RESULT eq "0"} |
| 1071 | |
| 1072 | ############################################################################### |
| 1073 | |
| 1074 | fossil test-th-eval {string is alnum abc123_} |
| 1075 | test th1-string-is-11 {$RESULT eq "0"} |
| 1076 | |
| 1077 | ############################################################################### |
| 1078 | |
| 1079 | fossil test-th-eval {string is list ""} |
| 1080 | test th1-string-is-12 {$RESULT eq "1"} |
| 1081 | |
| 1082 | ############################################################################### |
| 1083 | |
| 1084 | fossil test-th-eval {string is list 1} |
| 1085 | test th1-string-is-13 {$RESULT eq "1"} |
| 1086 | |
| 1087 | ############################################################################### |
| 1088 | |
| 1089 | fossil test-th-eval {string is list "1 2 3"} |
| 1090 | test th1-string-is-14 {$RESULT eq "1"} |
| 1091 | |
| 1092 | ############################################################################### |
| 1093 | |
| 1094 | fossil test-th-eval {string is list "\{"} |
| 1095 | test th1-string-is-15 {$RESULT eq "0"} |
| 1096 | |
| 1097 | ############################################################################### |
| 1098 | |
| 1099 | fossil test-th-eval {string is list "1 2 3 \{"} |
| 1100 | test th1-string-is-16 {$RESULT eq "0"} |
| 1101 | |
| 1102 | ############################################################################### |
| 1103 | |
| 1104 | fossil test-th-eval {string is list "1 2 3 \{\}"} |
| 1105 | test th1-string-is-17 {$RESULT eq "1"} |
| 1106 | |
| 1107 | ############################################################################### |
| 1108 | |
| 1109 | fossil test-th-eval {string is list "1 2 3 \{\{\}"} |
| 1110 | test th1-string-is-18 {$RESULT eq "0"} |
| 1111 | |
| 1112 | ############################################################################### |
| 1113 | |
| 1114 | fossil test-th-eval {string is double 123} |
| 1115 | test th1-string-is-19 {$RESULT eq "1"} |
| 1116 | |
| 1117 | ############################################################################### |
| 1118 | |
| 1119 | fossil test-th-eval {string is double 123.456} |
| 1120 | test th1-string-is-20 {$RESULT eq "1"} |
| 1121 | |
| 1122 | ############################################################################### |
| 1123 | |
| 1124 | fossil test-th-eval {string is double 123abc} |
| 1125 | test th1-string-is-21 {$RESULT eq "0"} |
| 1126 | |
| 1127 | ############################################################################### |
| 1128 | |
| 1129 | fossil test-th-eval {string is double 123_456} |
| 1130 | test th1-string-is-22 {$RESULT eq "0"} |
| 1131 | |
| 1132 | ############################################################################### |
| 1133 | |
| 1134 | fossil test-th-eval {string is integer 123} |
| 1135 | test th1-string-is-23 {$RESULT eq "1"} |
| 1136 | |
| 1137 | ############################################################################### |
| 1138 | |
| 1139 | fossil test-th-eval {string is integer 123.456} |
| 1140 | test th1-string-is-24 {$RESULT eq "0"} |
| 1141 | |
| 1142 | ############################################################################### |
| 1143 | |
| 1144 | fossil test-th-eval {string is integer 123abc} |
| 1145 | test th1-string-is-25 {$RESULT eq "0"} |
| 1146 | |
| 1147 | ############################################################################### |
| 1148 | |
| 1149 | fossil test-th-eval {string is integer 0b11001001} |
| 1150 | test th1-string-is-26 {$RESULT eq "1"} |
| 1151 | |
| 1152 | ############################################################################### |
| 1153 | |
| 1154 | fossil test-th-eval {string is integer 0b11001002} |
| 1155 | test th1-string-is-27 {$RESULT eq "0"} |
| 1156 | |
| 1157 | ############################################################################### |
| 1158 | |
| 1159 | fossil test-th-eval {string is integer 0o777} |
| 1160 | test th1-string-is-28 {$RESULT eq "1"} |
| 1161 | |
| 1162 | ############################################################################### |
| 1163 | |
| 1164 | fossil test-th-eval {string is integer 0o778} |
| 1165 | test th1-string-is-29 {$RESULT eq "0"} |
| 1166 | |
| 1167 | ############################################################################### |
| 1168 | |
| 1169 | fossil test-th-eval {string is integer 0xC0DEF00D} |
| 1170 | test th1-string-is-30 {$RESULT eq "1"} |
| 1171 | |
| 1172 | ############################################################################### |
| 1173 | |
| 1174 | fossil test-th-eval {string is integer 0xC0DEF00Z} |
| 1175 | test th1-string-is-31 {$RESULT eq "0"} |
| 1176 |
+1
-1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -150,11 +150,11 @@ | ||
| 150 | 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | 154 | # |
| 155 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c | |
| 155 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d | |
| 156 | 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | 158 | |
| 159 | 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | 160 | # source code directory resides (depending on the value of the macro |
| 161 | 161 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -150,11 +150,11 @@ | |
| 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | # |
| 155 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c |
| 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | |
| 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | # source code directory resides (depending on the value of the macro |
| 161 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -150,11 +150,11 @@ | |
| 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | # |
| 155 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d |
| 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | |
| 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | # source code directory resides (depending on the value of the macro |
| 161 |
+1
-1
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -150,11 +150,11 @@ | ||
| 150 | 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | 154 | # |
| 155 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c | |
| 155 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d | |
| 156 | 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | 158 | |
| 159 | 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | 160 | # source code directory resides (depending on the value of the macro |
| 161 | 161 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -150,11 +150,11 @@ | |
| 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | # |
| 155 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2c |
| 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | |
| 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | # source code directory resides (depending on the value of the macro |
| 161 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -150,11 +150,11 @@ | |
| 150 | #### The directories where the OpenSSL include and library files are located. |
| 151 | # The recommended usage here is to use the Sysinternals junction tool |
| 152 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 153 | # Fossil source code directory and the target OpenSSL source directory. |
| 154 | # |
| 155 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2d |
| 156 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 157 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 158 | |
| 159 | #### Either the directory where the Tcl library is installed or the Tcl |
| 160 | # source code directory resides (depending on the value of the macro |
| 161 |
+2
-2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | FOSSIL_BUILD_SSL = 0 |
| 40 | 40 | !endif |
| 41 | 41 | |
| 42 | 42 | # Build the included zlib library? |
| 43 | 43 | !ifndef FOSSIL_BUILD_ZLIB |
| 44 | -FOSSIL_BUILD_ZLIB = 0 | |
| 44 | +FOSSIL_BUILD_ZLIB = 1 | |
| 45 | 45 | !endif |
| 46 | 46 | |
| 47 | 47 | # Link everything except SQLite dynamically? |
| 48 | 48 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 49 | 49 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -88,11 +88,11 @@ | ||
| 88 | 88 | !ifndef FOSSIL_ENABLE_WINXP |
| 89 | 89 | FOSSIL_ENABLE_WINXP = 0 |
| 90 | 90 | !endif |
| 91 | 91 | |
| 92 | 92 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 93 | -SSLDIR = $(B)\compat\openssl-1.0.2c | |
| 93 | +SSLDIR = $(B)\compat\openssl-1.0.2d | |
| 94 | 94 | SSLINCDIR = $(SSLDIR)\inc32 |
| 95 | 95 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 96 | 96 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 97 | 97 | !else |
| 98 | 98 | SSLLIBDIR = $(SSLDIR)\out32 |
| 99 | 99 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -39,11 +39,11 @@ | |
| 39 | FOSSIL_BUILD_SSL = 0 |
| 40 | !endif |
| 41 | |
| 42 | # Build the included zlib library? |
| 43 | !ifndef FOSSIL_BUILD_ZLIB |
| 44 | FOSSIL_BUILD_ZLIB = 0 |
| 45 | !endif |
| 46 | |
| 47 | # Link everything except SQLite dynamically? |
| 48 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 49 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -88,11 +88,11 @@ | |
| 88 | !ifndef FOSSIL_ENABLE_WINXP |
| 89 | FOSSIL_ENABLE_WINXP = 0 |
| 90 | !endif |
| 91 | |
| 92 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 93 | SSLDIR = $(B)\compat\openssl-1.0.2c |
| 94 | SSLINCDIR = $(SSLDIR)\inc32 |
| 95 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 96 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 97 | !else |
| 98 | SSLLIBDIR = $(SSLDIR)\out32 |
| 99 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -39,11 +39,11 @@ | |
| 39 | FOSSIL_BUILD_SSL = 0 |
| 40 | !endif |
| 41 | |
| 42 | # Build the included zlib library? |
| 43 | !ifndef FOSSIL_BUILD_ZLIB |
| 44 | FOSSIL_BUILD_ZLIB = 1 |
| 45 | !endif |
| 46 | |
| 47 | # Link everything except SQLite dynamically? |
| 48 | !ifndef FOSSIL_DYNAMIC_BUILD |
| 49 | FOSSIL_DYNAMIC_BUILD = 0 |
| @@ -88,11 +88,11 @@ | |
| 88 | !ifndef FOSSIL_ENABLE_WINXP |
| 89 | FOSSIL_ENABLE_WINXP = 0 |
| 90 | !endif |
| 91 | |
| 92 | !if $(FOSSIL_ENABLE_SSL)!=0 |
| 93 | SSLDIR = $(B)\compat\openssl-1.0.2d |
| 94 | SSLINCDIR = $(SSLDIR)\inc32 |
| 95 | !if $(FOSSIL_DYNAMIC_BUILD)!=0 |
| 96 | SSLLIBDIR = $(SSLDIR)\out32dll |
| 97 | !else |
| 98 | SSLLIBDIR = $(SSLDIR)\out32 |
| 99 |
+1
-1
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -135,11 +135,11 @@ | ||
| 135 | 135 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 136 | 136 | first <a href="https://www.openssl.org/source/">download the official |
| 137 | 137 | source code for OpenSSL</a> and extract it to an appropriately named |
| 138 | 138 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 139 | 139 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 140 | -"<b>compat/openssl-1.0.2c</b>"), then make sure that some recent | |
| 140 | +"<b>compat/openssl-1.0.2d</b>"), then make sure that some recent | |
| 141 | 141 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 142 | 142 | and finally run one of the following commands: |
| 143 | 143 | <blockquote><pre> |
| 144 | 144 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 145 | 145 | </pre></blockquote> |
| 146 | 146 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -135,11 +135,11 @@ | |
| 135 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 136 | first <a href="https://www.openssl.org/source/">download the official |
| 137 | source code for OpenSSL</a> and extract it to an appropriately named |
| 138 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 139 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 140 | "<b>compat/openssl-1.0.2c</b>"), then make sure that some recent |
| 141 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 142 | and finally run one of the following commands: |
| 143 | <blockquote><pre> |
| 144 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 145 | </pre></blockquote> |
| 146 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -135,11 +135,11 @@ | |
| 135 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 136 | first <a href="https://www.openssl.org/source/">download the official |
| 137 | source code for OpenSSL</a> and extract it to an appropriately named |
| 138 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 139 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 140 | "<b>compat/openssl-1.0.2d</b>"), then make sure that some recent |
| 141 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 142 | and finally run one of the following commands: |
| 143 | <blockquote><pre> |
| 144 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 145 | </pre></blockquote> |
| 146 |
+8
-3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,13 +1,18 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <h2>Changes for Version 1.34 (2015-??-??)</h2> |
| 4 | 4 | * Fix --hard option to mv/rm to enable them to work properly with certain |
| 5 | 5 | relative paths. |
| 6 | - * Add minimal 'lsearch' command to TH1. Only exact case-sensitive matching | |
| 7 | - is supported. | |
| 8 | - * Add 'glob_match' command to TH1. | |
| 6 | + * Make the clean command undoable for files less than 10MiB. | |
| 7 | + * Add minimal <nowiki>[lsearch]</nowiki> command to TH1. Only exact | |
| 8 | + case-sensitive matching is supported. | |
| 9 | + * Add the <nowiki>[glob_match]</nowiki> command to TH1. | |
| 10 | + * Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> TH1 commands to | |
| 11 | + the Tcl integration subsystem. | |
| 12 | + * Add 'double', 'integer', and 'list' classes to the | |
| 13 | + <nowiki>[string is]</nowiki> command in TH1. | |
| 9 | 14 | * Update internal Unicode character tables, used in regular expression |
| 10 | 15 | handling, from version 7.0 to 8.0. |
| 11 | 16 | |
| 12 | 17 | <h2>Changes for Version 1.33 (2015-05-23)</h2> |
| 13 | 18 | * Improved fork detection on [/help?cmd=update|fossil update], |
| 14 | 19 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,13 +1,18 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2>Changes for Version 1.34 (2015-??-??)</h2> |
| 4 | * Fix --hard option to mv/rm to enable them to work properly with certain |
| 5 | relative paths. |
| 6 | * Add minimal 'lsearch' command to TH1. Only exact case-sensitive matching |
| 7 | is supported. |
| 8 | * Add 'glob_match' command to TH1. |
| 9 | * Update internal Unicode character tables, used in regular expression |
| 10 | handling, from version 7.0 to 8.0. |
| 11 | |
| 12 | <h2>Changes for Version 1.33 (2015-05-23)</h2> |
| 13 | * Improved fork detection on [/help?cmd=update|fossil update], |
| 14 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,13 +1,18 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2>Changes for Version 1.34 (2015-??-??)</h2> |
| 4 | * Fix --hard option to mv/rm to enable them to work properly with certain |
| 5 | relative paths. |
| 6 | * Make the clean command undoable for files less than 10MiB. |
| 7 | * Add minimal <nowiki>[lsearch]</nowiki> command to TH1. Only exact |
| 8 | case-sensitive matching is supported. |
| 9 | * Add the <nowiki>[glob_match]</nowiki> command to TH1. |
| 10 | * Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> TH1 commands to |
| 11 | the Tcl integration subsystem. |
| 12 | * Add 'double', 'integer', and 'list' classes to the |
| 13 | <nowiki>[string is]</nowiki> command in TH1. |
| 14 | * Update internal Unicode character tables, used in regular expression |
| 15 | handling, from version 7.0 to 8.0. |
| 16 | |
| 17 | <h2>Changes for Version 1.33 (2015-05-23)</h2> |
| 18 | * Improved fork detection on [/help?cmd=update|fossil update], |
| 19 |
+31
-6
| --- www/th1.md | ||
| +++ www/th1.md | ||
| @@ -154,10 +154,12 @@ | ||
| 154 | 154 | * styleHeader |
| 155 | 155 | * styleFooter |
| 156 | 156 | * tclEval |
| 157 | 157 | * tclExpr |
| 158 | 158 | * tclInvoke |
| 159 | + * tclIsSafe | |
| 160 | + * tclMakeSafe | |
| 159 | 161 | * tclReady |
| 160 | 162 | * trace |
| 161 | 163 | * stime |
| 162 | 164 | * utime |
| 163 | 165 | * wiki |
| @@ -473,34 +475,57 @@ | ||
| 473 | 475 | **This command requires the Tcl integration feature.** |
| 474 | 476 | |
| 475 | 477 | * tclEval arg ?arg ...? |
| 476 | 478 | |
| 477 | 479 | Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 478 | -error is generated, it will be transformed into a TH1 script error. A Tcl | |
| 479 | -interpreter will be created automatically if it has not been already. | |
| 480 | +error is generated, it will be transformed into a TH1 script error. The | |
| 481 | +Tcl interpreter will be created automatically if it has not been already. | |
| 480 | 482 | |
| 481 | 483 | <a name="tclExpr"></a>TH1 tclExpr Command |
| 482 | 484 | ----------------------------------------- |
| 483 | 485 | |
| 484 | 486 | **This command requires the Tcl integration feature.** |
| 485 | 487 | |
| 486 | 488 | * tclExpr arg ?arg ...? |
| 487 | 489 | |
| 488 | 490 | Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 489 | -script error is generated, it will be transformed into a TH1 script error. | |
| 490 | -A Tcl interpreter will be created automatically if it has not been already. | |
| 491 | +script error is generated, it will be transformed into a TH1 script | |
| 492 | +error. The Tcl interpreter will be created automatically if it has not | |
| 493 | +been already. | |
| 491 | 494 | |
| 492 | 495 | <a name="tclInvoke"></a>TH1 tclInvoke Command |
| 493 | 496 | --------------------------------------------- |
| 494 | 497 | |
| 495 | 498 | **This command requires the Tcl integration feature.** |
| 496 | 499 | |
| 497 | 500 | * tclInvoke command ?arg ...? |
| 498 | 501 | |
| 499 | 502 | Invokes the Tcl command using the supplied arguments. No additional |
| 500 | -substitutions are performed on the arguments. A Tcl interpreter will | |
| 501 | -be created automatically if it has not been already. | |
| 503 | +substitutions are performed on the arguments. The Tcl interpreter | |
| 504 | +will be created automatically if it has not been already. | |
| 505 | + | |
| 506 | +<a name="tclIsSafe"></a>TH1 tclIsSafe Command | |
| 507 | +--------------------------------------------- | |
| 508 | + | |
| 509 | +**This command requires the Tcl integration feature.** | |
| 510 | + | |
| 511 | + * tclIsSafe | |
| 512 | + | |
| 513 | +Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter | |
| 514 | +will be created automatically if it has not been already. | |
| 515 | + | |
| 516 | +<a name="tclMakeSafe"></a>TH1 tclMakeSafe Command | |
| 517 | +--------------------------------------------- | |
| 518 | + | |
| 519 | +**This command requires the Tcl integration feature.** | |
| 520 | + | |
| 521 | + * tclMakeSafe | |
| 522 | + | |
| 523 | +Forces the Tcl interpreter into "safe" mode by removing all "unsafe" | |
| 524 | +commands and variables. This operation cannot be undone. The Tcl | |
| 525 | +interpreter will remain "safe" until the process terminates. The Tcl | |
| 526 | +interpreter will be created automatically if it has not been already. | |
| 502 | 527 | |
| 503 | 528 | <a name="tclReady"></a>TH1 tclReady Command |
| 504 | 529 | ------------------------------------------- |
| 505 | 530 | |
| 506 | 531 | * tclReady |
| 507 | 532 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -154,10 +154,12 @@ | |
| 154 | * styleHeader |
| 155 | * styleFooter |
| 156 | * tclEval |
| 157 | * tclExpr |
| 158 | * tclInvoke |
| 159 | * tclReady |
| 160 | * trace |
| 161 | * stime |
| 162 | * utime |
| 163 | * wiki |
| @@ -473,34 +475,57 @@ | |
| 473 | **This command requires the Tcl integration feature.** |
| 474 | |
| 475 | * tclEval arg ?arg ...? |
| 476 | |
| 477 | Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 478 | error is generated, it will be transformed into a TH1 script error. A Tcl |
| 479 | interpreter will be created automatically if it has not been already. |
| 480 | |
| 481 | <a name="tclExpr"></a>TH1 tclExpr Command |
| 482 | ----------------------------------------- |
| 483 | |
| 484 | **This command requires the Tcl integration feature.** |
| 485 | |
| 486 | * tclExpr arg ?arg ...? |
| 487 | |
| 488 | Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 489 | script error is generated, it will be transformed into a TH1 script error. |
| 490 | A Tcl interpreter will be created automatically if it has not been already. |
| 491 | |
| 492 | <a name="tclInvoke"></a>TH1 tclInvoke Command |
| 493 | --------------------------------------------- |
| 494 | |
| 495 | **This command requires the Tcl integration feature.** |
| 496 | |
| 497 | * tclInvoke command ?arg ...? |
| 498 | |
| 499 | Invokes the Tcl command using the supplied arguments. No additional |
| 500 | substitutions are performed on the arguments. A Tcl interpreter will |
| 501 | be created automatically if it has not been already. |
| 502 | |
| 503 | <a name="tclReady"></a>TH1 tclReady Command |
| 504 | ------------------------------------------- |
| 505 | |
| 506 | * tclReady |
| 507 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -154,10 +154,12 @@ | |
| 154 | * styleHeader |
| 155 | * styleFooter |
| 156 | * tclEval |
| 157 | * tclExpr |
| 158 | * tclInvoke |
| 159 | * tclIsSafe |
| 160 | * tclMakeSafe |
| 161 | * tclReady |
| 162 | * trace |
| 163 | * stime |
| 164 | * utime |
| 165 | * wiki |
| @@ -473,34 +475,57 @@ | |
| 475 | **This command requires the Tcl integration feature.** |
| 476 | |
| 477 | * tclEval arg ?arg ...? |
| 478 | |
| 479 | Evaluates the Tcl script and returns its result verbatim. If a Tcl script |
| 480 | error is generated, it will be transformed into a TH1 script error. The |
| 481 | Tcl interpreter will be created automatically if it has not been already. |
| 482 | |
| 483 | <a name="tclExpr"></a>TH1 tclExpr Command |
| 484 | ----------------------------------------- |
| 485 | |
| 486 | **This command requires the Tcl integration feature.** |
| 487 | |
| 488 | * tclExpr arg ?arg ...? |
| 489 | |
| 490 | Evaluates the Tcl expression and returns its result verbatim. If a Tcl |
| 491 | script error is generated, it will be transformed into a TH1 script |
| 492 | error. The Tcl interpreter will be created automatically if it has not |
| 493 | been already. |
| 494 | |
| 495 | <a name="tclInvoke"></a>TH1 tclInvoke Command |
| 496 | --------------------------------------------- |
| 497 | |
| 498 | **This command requires the Tcl integration feature.** |
| 499 | |
| 500 | * tclInvoke command ?arg ...? |
| 501 | |
| 502 | Invokes the Tcl command using the supplied arguments. No additional |
| 503 | substitutions are performed on the arguments. The Tcl interpreter |
| 504 | will be created automatically if it has not been already. |
| 505 | |
| 506 | <a name="tclIsSafe"></a>TH1 tclIsSafe Command |
| 507 | --------------------------------------------- |
| 508 | |
| 509 | **This command requires the Tcl integration feature.** |
| 510 | |
| 511 | * tclIsSafe |
| 512 | |
| 513 | Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter |
| 514 | will be created automatically if it has not been already. |
| 515 | |
| 516 | <a name="tclMakeSafe"></a>TH1 tclMakeSafe Command |
| 517 | --------------------------------------------- |
| 518 | |
| 519 | **This command requires the Tcl integration feature.** |
| 520 | |
| 521 | * tclMakeSafe |
| 522 | |
| 523 | Forces the Tcl interpreter into "safe" mode by removing all "unsafe" |
| 524 | commands and variables. This operation cannot be undone. The Tcl |
| 525 | interpreter will remain "safe" until the process terminates. The Tcl |
| 526 | interpreter will be created automatically if it has not been already. |
| 527 | |
| 528 | <a name="tclReady"></a>TH1 tclReady Command |
| 529 | ------------------------------------------- |
| 530 | |
| 531 | * tclReady |
| 532 |