Fossil SCM
Merge from trunk.
Commit
1a6876db8decace4d1869c93a3c0a3efb1595964
Parent
3f49a849954f0fb…
78 files changed
+38
-1
+38
-1
+17
-4
+3
-3
+1
-1
+29
-14
+20
-20
+61
-40
+3
-3
+23
-24
+304
-160
+60
-47
+1
-1
+3
-3
+59
-45
+2
-1
+64
-13
+32
-10
+22
-12
+8
-13
+121
-72
+10
-11
+92
-11
+27
-30
+1
-1
+2
-4
+9
-6
+1
+5
-7
+100
-94
+5
-6
+135
-9
+7
-2
+1
-1
+13
-2
+731
-369
+27
-1
+2
-2
+10
-10
+1
-1
+3
-4
+60
-9
+180
-46
+10
+3
-4
+2
-2
+198
-425
+4
+36
-37
+1
-1
+1
-1
+1
+4
-4
+1
-1
+2
-1
+4
-4
+8
-5
+257
-20
+14
-11
+2
-2
+2
-2
+31
-46
+34
-60
+29
-32
+4
-7
+130
-42
+26
-25
+1
-1
+12
-12
+4
-4
+8
-8
+38
-5
+69
-20
+27
-5
+29
+1
+28
-6
~
Makefile
~
Makefile
~
src/allrepo.c
~
src/attach.c
~
src/bag.c
~
src/blob.c
~
src/branch.c
~
src/browse.c
~
src/captcha.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/clearsign.c
~
src/comformat.c
~
src/config.h
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/delta.c
~
src/diff.c
~
src/diffcmd.c
~
src/doc.c
~
src/encode.c
~
src/event.c
~
src/finfo.c
~
src/graph.c
~
src/http.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/info.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/makeheaders.c
~
src/makemake.tcl
~
src/manifest.c
~
src/md5.c
~
src/merge3.c
~
src/name.c
~
src/pqueue.c
~
src/printf.c
~
src/rebuild.c
~
src/report.c
~
src/schema.c
~
src/search.c
~
src/setup.c
~
src/sha1.c
~
src/shell.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stat.c
~
src/style.c
~
src/sync.c
~
src/tag.c
~
src/th_lang.c
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/update.c
~
src/url.c
~
src/user.c
~
src/vfile.c
~
src/wiki.c
~
src/wikiformat.c
~
src/winhttp.c
~
src/xfer.c
~
src/zip.c
~
test/delta1.test
~
test/merge1.test
~
test/merge3.test
~
test/merge4.test
~
test/tester.tcl
~
win/Makefile.PellesCGMake
~
win/Makefile.dmc
~
win/fossil.ico
~
win/fossil.rc
~
win/version.c
~
www/fileformat.wiki
M
Makefile
+38
-1
| --- Makefile | ||
| +++ Makefile | ||
| @@ -16,13 +16,50 @@ | ||
| 16 | 16 | -include config.mk # Configure if present. |
| 17 | 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | 19 | endif |
| 20 | 20 | |
| 21 | -#### The Tcl shell to run for test suites. | |
| 21 | +#### C Compile and options for use in building executables that | |
| 22 | +# will run on the target platform. This is usually the same | |
| 23 | +# as BCC, unless you are cross-compiling. This C compiler builds | |
| 24 | +# the finished binary for fossil. The BCC compiler above is used | |
| 25 | +# for building intermediate code-generator tools. | |
| 26 | +# | |
| 27 | +#TCC = gcc -O6 | |
| 28 | +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage | |
| 29 | +TCC = gcc -g -Os -Wall | |
| 30 | + | |
| 31 | +# To add support for HTTPS | |
| 32 | +TCC += -DFOSSIL_ENABLE_SSL | |
| 33 | + | |
| 34 | +#### Extra arguments for linking the finished binary. Fossil needs | |
| 35 | +# to link against the Z-Lib compression library. There are no | |
| 36 | +# other dependencies. We sometimes add the -static option here | |
| 37 | +# so that we can build a static executable that will run in a | |
| 38 | +# chroot jail. | |
| 39 | +# | |
| 40 | +LIB = -lz $(LDFLAGS) | |
| 41 | + | |
| 42 | +# If using HTTPS: | |
| 43 | +LIB += -lcrypto -lssl | |
| 44 | + | |
| 45 | +#### Tcl shell for use in running the fossil testsuite. | |
| 22 | 46 | # |
| 23 | 47 | TCLSH = tclsh |
| 24 | 48 | |
| 25 | 49 | # You should not need to change anything below this line |
| 26 | 50 | ############################################################################### |
| 51 | +# | |
| 52 | +# Automatic platform-specific options. | |
| 53 | +HOST_OS!= uname -s | |
| 54 | + | |
| 55 | +LIB.SunOS= -lsocket -lnsl | |
| 56 | +LIB += $(LIB.$(HOST_OS)) | |
| 57 | + | |
| 58 | +TCC.DragonFly += -DUSE_PREAD | |
| 59 | +TCC.FreeBSD += -DUSE_PREAD | |
| 60 | +TCC.NetBSD += -DUSE_PREAD | |
| 61 | +TCC.OpenBSD += -DUSE_PREAD | |
| 62 | +TCC += $(TCC.$(HOST_OS)) | |
| 63 | + | |
| 27 | 64 | include $(SRCDIR)/main.mk |
| 28 | 65 | |
| 29 | 66 |
| --- Makefile | |
| +++ Makefile | |
| @@ -16,13 +16,50 @@ | |
| 16 | -include config.mk # Configure if present. |
| 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | endif |
| 20 | |
| 21 | #### The Tcl shell to run for test suites. |
| 22 | # |
| 23 | TCLSH = tclsh |
| 24 | |
| 25 | # You should not need to change anything below this line |
| 26 | ############################################################################### |
| 27 | include $(SRCDIR)/main.mk |
| 28 | |
| 29 |
| --- Makefile | |
| +++ Makefile | |
| @@ -16,13 +16,50 @@ | |
| 16 | -include config.mk # Configure if present. |
| 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | endif |
| 20 | |
| 21 | #### C Compile and options for use in building executables that |
| 22 | # will run on the target platform. This is usually the same |
| 23 | # as BCC, unless you are cross-compiling. This C compiler builds |
| 24 | # the finished binary for fossil. The BCC compiler above is used |
| 25 | # for building intermediate code-generator tools. |
| 26 | # |
| 27 | #TCC = gcc -O6 |
| 28 | #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage |
| 29 | TCC = gcc -g -Os -Wall |
| 30 | |
| 31 | # To add support for HTTPS |
| 32 | TCC += -DFOSSIL_ENABLE_SSL |
| 33 | |
| 34 | #### Extra arguments for linking the finished binary. Fossil needs |
| 35 | # to link against the Z-Lib compression library. There are no |
| 36 | # other dependencies. We sometimes add the -static option here |
| 37 | # so that we can build a static executable that will run in a |
| 38 | # chroot jail. |
| 39 | # |
| 40 | LIB = -lz $(LDFLAGS) |
| 41 | |
| 42 | # If using HTTPS: |
| 43 | LIB += -lcrypto -lssl |
| 44 | |
| 45 | #### Tcl shell for use in running the fossil testsuite. |
| 46 | # |
| 47 | TCLSH = tclsh |
| 48 | |
| 49 | # You should not need to change anything below this line |
| 50 | ############################################################################### |
| 51 | # |
| 52 | # Automatic platform-specific options. |
| 53 | HOST_OS!= uname -s |
| 54 | |
| 55 | LIB.SunOS= -lsocket -lnsl |
| 56 | LIB += $(LIB.$(HOST_OS)) |
| 57 | |
| 58 | TCC.DragonFly += -DUSE_PREAD |
| 59 | TCC.FreeBSD += -DUSE_PREAD |
| 60 | TCC.NetBSD += -DUSE_PREAD |
| 61 | TCC.OpenBSD += -DUSE_PREAD |
| 62 | TCC += $(TCC.$(HOST_OS)) |
| 63 | |
| 64 | include $(SRCDIR)/main.mk |
| 65 | |
| 66 |
M
Makefile
+38
-1
| --- Makefile | ||
| +++ Makefile | ||
| @@ -16,13 +16,50 @@ | ||
| 16 | 16 | -include config.mk # Configure if present. |
| 17 | 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | 19 | endif |
| 20 | 20 | |
| 21 | -#### The Tcl shell to run for test suites. | |
| 21 | +#### C Compile and options for use in building executables that | |
| 22 | +# will run on the target platform. This is usually the same | |
| 23 | +# as BCC, unless you are cross-compiling. This C compiler builds | |
| 24 | +# the finished binary for fossil. The BCC compiler above is used | |
| 25 | +# for building intermediate code-generator tools. | |
| 26 | +# | |
| 27 | +#TCC = gcc -O6 | |
| 28 | +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage | |
| 29 | +TCC = gcc -g -Os -Wall | |
| 30 | + | |
| 31 | +# To add support for HTTPS | |
| 32 | +TCC += -DFOSSIL_ENABLE_SSL | |
| 33 | + | |
| 34 | +#### Extra arguments for linking the finished binary. Fossil needs | |
| 35 | +# to link against the Z-Lib compression library. There are no | |
| 36 | +# other dependencies. We sometimes add the -static option here | |
| 37 | +# so that we can build a static executable that will run in a | |
| 38 | +# chroot jail. | |
| 39 | +# | |
| 40 | +LIB = -lz $(LDFLAGS) | |
| 41 | + | |
| 42 | +# If using HTTPS: | |
| 43 | +LIB += -lcrypto -lssl | |
| 44 | + | |
| 45 | +#### Tcl shell for use in running the fossil testsuite. | |
| 22 | 46 | # |
| 23 | 47 | TCLSH = tclsh |
| 24 | 48 | |
| 25 | 49 | # You should not need to change anything below this line |
| 26 | 50 | ############################################################################### |
| 51 | +# | |
| 52 | +# Automatic platform-specific options. | |
| 53 | +HOST_OS!= uname -s | |
| 54 | + | |
| 55 | +LIB.SunOS= -lsocket -lnsl | |
| 56 | +LIB += $(LIB.$(HOST_OS)) | |
| 57 | + | |
| 58 | +TCC.DragonFly += -DUSE_PREAD | |
| 59 | +TCC.FreeBSD += -DUSE_PREAD | |
| 60 | +TCC.NetBSD += -DUSE_PREAD | |
| 61 | +TCC.OpenBSD += -DUSE_PREAD | |
| 62 | +TCC += $(TCC.$(HOST_OS)) | |
| 63 | + | |
| 27 | 64 | include $(SRCDIR)/main.mk |
| 28 | 65 | |
| 29 | 66 |
| --- Makefile | |
| +++ Makefile | |
| @@ -16,13 +16,50 @@ | |
| 16 | -include config.mk # Configure if present. |
| 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | endif |
| 20 | |
| 21 | #### The Tcl shell to run for test suites. |
| 22 | # |
| 23 | TCLSH = tclsh |
| 24 | |
| 25 | # You should not need to change anything below this line |
| 26 | ############################################################################### |
| 27 | include $(SRCDIR)/main.mk |
| 28 | |
| 29 |
| --- Makefile | |
| +++ Makefile | |
| @@ -16,13 +16,50 @@ | |
| 16 | -include config.mk # Configure if present. |
| 17 | ifndef CONFIG_MK_COMPLETE |
| 18 | include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc. |
| 19 | endif |
| 20 | |
| 21 | #### C Compile and options for use in building executables that |
| 22 | # will run on the target platform. This is usually the same |
| 23 | # as BCC, unless you are cross-compiling. This C compiler builds |
| 24 | # the finished binary for fossil. The BCC compiler above is used |
| 25 | # for building intermediate code-generator tools. |
| 26 | # |
| 27 | #TCC = gcc -O6 |
| 28 | #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage |
| 29 | TCC = gcc -g -Os -Wall |
| 30 | |
| 31 | # To add support for HTTPS |
| 32 | TCC += -DFOSSIL_ENABLE_SSL |
| 33 | |
| 34 | #### Extra arguments for linking the finished binary. Fossil needs |
| 35 | # to link against the Z-Lib compression library. There are no |
| 36 | # other dependencies. We sometimes add the -static option here |
| 37 | # so that we can build a static executable that will run in a |
| 38 | # chroot jail. |
| 39 | # |
| 40 | LIB = -lz $(LDFLAGS) |
| 41 | |
| 42 | # If using HTTPS: |
| 43 | LIB += -lcrypto -lssl |
| 44 | |
| 45 | #### Tcl shell for use in running the fossil testsuite. |
| 46 | # |
| 47 | TCLSH = tclsh |
| 48 | |
| 49 | # You should not need to change anything below this line |
| 50 | ############################################################################### |
| 51 | # |
| 52 | # Automatic platform-specific options. |
| 53 | HOST_OS!= uname -s |
| 54 | |
| 55 | LIB.SunOS= -lsocket -lnsl |
| 56 | LIB += $(LIB.$(HOST_OS)) |
| 57 | |
| 58 | TCC.DragonFly += -DUSE_PREAD |
| 59 | TCC.FreeBSD += -DUSE_PREAD |
| 60 | TCC.NetBSD += -DUSE_PREAD |
| 61 | TCC.OpenBSD += -DUSE_PREAD |
| 62 | TCC += $(TCC.$(HOST_OS)) |
| 63 | |
| 64 | include $(SRCDIR)/main.mk |
| 65 | |
| 66 |
+17
-4
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -34,11 +34,11 @@ | ||
| 34 | 34 | static char *quoteFilename(const char *zFilename){ |
| 35 | 35 | int i, c; |
| 36 | 36 | int needQuote = 0; |
| 37 | 37 | for(i=0; (c = zFilename[i])!=0; i++){ |
| 38 | 38 | if( c=='"' ) return 0; |
| 39 | - if( isspace(c) ) needQuote = 1; | |
| 39 | + if( fossil_isspace(c) ) needQuote = 1; | |
| 40 | 40 | if( c=='\\' && zFilename[i+1]==0 ) return 0; |
| 41 | 41 | if( c=='$' ) return 0; |
| 42 | 42 | } |
| 43 | 43 | if( needQuote ){ |
| 44 | 44 | return mprintf("\"%s\"", zFilename); |
| @@ -60,10 +60,13 @@ | ||
| 60 | 60 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 61 | 61 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 62 | 62 | ** |
| 63 | 63 | ** Available operations are: |
| 64 | 64 | ** |
| 65 | +** ignore Arguments are repositories that should be ignored | |
| 66 | +** by subsequent list, pull, push, rebuild, and sync. | |
| 67 | +** | |
| 65 | 68 | ** list | ls Display the location of all repositories |
| 66 | 69 | ** |
| 67 | 70 | ** pull Run a "pull" operation on all repositories |
| 68 | 71 | ** |
| 69 | 72 | ** push Run a "push" on all repositories |
| @@ -72,11 +75,12 @@ | ||
| 72 | 75 | ** |
| 73 | 76 | ** sync Run a "sync" on all repositories |
| 74 | 77 | ** |
| 75 | 78 | ** Respositories are automatically added to the set of known repositories |
| 76 | 79 | ** when one of the following commands against the repository: clone, info, |
| 77 | -** pull, push, or sync | |
| 80 | +** pull, push, or sync. Even previously ignored repositories are added back | |
| 81 | +** to the list of repositories by these commands. | |
| 78 | 82 | */ |
| 79 | 83 | void all_cmd(void){ |
| 80 | 84 | int n; |
| 81 | 85 | Stmt q; |
| 82 | 86 | const char *zCmd; |
| @@ -99,13 +103,22 @@ | ||
| 99 | 103 | zCmd = "pull -autourl -R"; |
| 100 | 104 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 101 | 105 | zCmd = "rebuild"; |
| 102 | 106 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 103 | 107 | zCmd = "sync -autourl -R"; |
| 108 | + }else if( strncmp(zCmd, "ignore", n)==0 ){ | |
| 109 | + int j; | |
| 110 | + db_begin_transaction(); | |
| 111 | + for(j=3; j<g.argc; j++){ | |
| 112 | + db_multi_exec("DELETE FROM global_config WHERE name GLOB 'repo:%q'", | |
| 113 | + g.argv[j]); | |
| 114 | + } | |
| 115 | + db_end_transaction(0); | |
| 116 | + return; | |
| 104 | 117 | }else{ |
| 105 | 118 | fossil_fatal("\"all\" subcommand should be one of: " |
| 106 | - "list ls push pull rebuild sync"); | |
| 119 | + "ignore list ls push pull rebuild sync"); | |
| 107 | 120 | } |
| 108 | 121 | zFossil = quoteFilename(g.argv[0]); |
| 109 | 122 | nMissing = 0; |
| 110 | 123 | db_prepare(&q, |
| 111 | 124 | "SELECT DISTINCT substr(name, 6) COLLATE nocase" |
| @@ -125,11 +138,11 @@ | ||
| 125 | 138 | } |
| 126 | 139 | zQFilename = quoteFilename(zFilename); |
| 127 | 140 | zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); |
| 128 | 141 | printf("%s\n", zSyscmd); |
| 129 | 142 | fflush(stdout); |
| 130 | - portable_system(zSyscmd); | |
| 143 | + fossil_system(zSyscmd); | |
| 131 | 144 | free(zSyscmd); |
| 132 | 145 | free(zQFilename); |
| 133 | 146 | } |
| 134 | 147 | |
| 135 | 148 | /* If any repositories whose names appear in the ~/.fossil file could not |
| 136 | 149 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | static char *quoteFilename(const char *zFilename){ |
| 35 | int i, c; |
| 36 | int needQuote = 0; |
| 37 | for(i=0; (c = zFilename[i])!=0; i++){ |
| 38 | if( c=='"' ) return 0; |
| 39 | if( isspace(c) ) needQuote = 1; |
| 40 | if( c=='\\' && zFilename[i+1]==0 ) return 0; |
| 41 | if( c=='$' ) return 0; |
| 42 | } |
| 43 | if( needQuote ){ |
| 44 | return mprintf("\"%s\"", zFilename); |
| @@ -60,10 +60,13 @@ | |
| 60 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 61 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 62 | ** |
| 63 | ** Available operations are: |
| 64 | ** |
| 65 | ** list | ls Display the location of all repositories |
| 66 | ** |
| 67 | ** pull Run a "pull" operation on all repositories |
| 68 | ** |
| 69 | ** push Run a "push" on all repositories |
| @@ -72,11 +75,12 @@ | |
| 72 | ** |
| 73 | ** sync Run a "sync" on all repositories |
| 74 | ** |
| 75 | ** Respositories are automatically added to the set of known repositories |
| 76 | ** when one of the following commands against the repository: clone, info, |
| 77 | ** pull, push, or sync |
| 78 | */ |
| 79 | void all_cmd(void){ |
| 80 | int n; |
| 81 | Stmt q; |
| 82 | const char *zCmd; |
| @@ -99,13 +103,22 @@ | |
| 99 | zCmd = "pull -autourl -R"; |
| 100 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 101 | zCmd = "rebuild"; |
| 102 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 103 | zCmd = "sync -autourl -R"; |
| 104 | }else{ |
| 105 | fossil_fatal("\"all\" subcommand should be one of: " |
| 106 | "list ls push pull rebuild sync"); |
| 107 | } |
| 108 | zFossil = quoteFilename(g.argv[0]); |
| 109 | nMissing = 0; |
| 110 | db_prepare(&q, |
| 111 | "SELECT DISTINCT substr(name, 6) COLLATE nocase" |
| @@ -125,11 +138,11 @@ | |
| 125 | } |
| 126 | zQFilename = quoteFilename(zFilename); |
| 127 | zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); |
| 128 | printf("%s\n", zSyscmd); |
| 129 | fflush(stdout); |
| 130 | portable_system(zSyscmd); |
| 131 | free(zSyscmd); |
| 132 | free(zQFilename); |
| 133 | } |
| 134 | |
| 135 | /* If any repositories whose names appear in the ~/.fossil file could not |
| 136 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | static char *quoteFilename(const char *zFilename){ |
| 35 | int i, c; |
| 36 | int needQuote = 0; |
| 37 | for(i=0; (c = zFilename[i])!=0; i++){ |
| 38 | if( c=='"' ) return 0; |
| 39 | if( fossil_isspace(c) ) needQuote = 1; |
| 40 | if( c=='\\' && zFilename[i+1]==0 ) return 0; |
| 41 | if( c=='$' ) return 0; |
| 42 | } |
| 43 | if( needQuote ){ |
| 44 | return mprintf("\"%s\"", zFilename); |
| @@ -60,10 +60,13 @@ | |
| 60 | ** On Win32 systems, the file is named "_fossil" and is located in |
| 61 | ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. |
| 62 | ** |
| 63 | ** Available operations are: |
| 64 | ** |
| 65 | ** ignore Arguments are repositories that should be ignored |
| 66 | ** by subsequent list, pull, push, rebuild, and sync. |
| 67 | ** |
| 68 | ** list | ls Display the location of all repositories |
| 69 | ** |
| 70 | ** pull Run a "pull" operation on all repositories |
| 71 | ** |
| 72 | ** push Run a "push" on all repositories |
| @@ -72,11 +75,12 @@ | |
| 75 | ** |
| 76 | ** sync Run a "sync" on all repositories |
| 77 | ** |
| 78 | ** Respositories are automatically added to the set of known repositories |
| 79 | ** when one of the following commands against the repository: clone, info, |
| 80 | ** pull, push, or sync. Even previously ignored repositories are added back |
| 81 | ** to the list of repositories by these commands. |
| 82 | */ |
| 83 | void all_cmd(void){ |
| 84 | int n; |
| 85 | Stmt q; |
| 86 | const char *zCmd; |
| @@ -99,13 +103,22 @@ | |
| 103 | zCmd = "pull -autourl -R"; |
| 104 | }else if( strncmp(zCmd, "rebuild", n)==0 ){ |
| 105 | zCmd = "rebuild"; |
| 106 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 107 | zCmd = "sync -autourl -R"; |
| 108 | }else if( strncmp(zCmd, "ignore", n)==0 ){ |
| 109 | int j; |
| 110 | db_begin_transaction(); |
| 111 | for(j=3; j<g.argc; j++){ |
| 112 | db_multi_exec("DELETE FROM global_config WHERE name GLOB 'repo:%q'", |
| 113 | g.argv[j]); |
| 114 | } |
| 115 | db_end_transaction(0); |
| 116 | return; |
| 117 | }else{ |
| 118 | fossil_fatal("\"all\" subcommand should be one of: " |
| 119 | "ignore list ls push pull rebuild sync"); |
| 120 | } |
| 121 | zFossil = quoteFilename(g.argv[0]); |
| 122 | nMissing = 0; |
| 123 | db_prepare(&q, |
| 124 | "SELECT DISTINCT substr(name, 6) COLLATE nocase" |
| @@ -125,11 +138,11 @@ | |
| 138 | } |
| 139 | zQFilename = quoteFilename(zFilename); |
| 140 | zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); |
| 141 | printf("%s\n", zSyscmd); |
| 142 | fflush(stdout); |
| 143 | fossil_system(zSyscmd); |
| 144 | free(zSyscmd); |
| 145 | free(zQFilename); |
| 146 | } |
| 147 | |
| 148 | /* If any repositories whose names appear in the ~/.fossil file could not |
| 149 |
+3
-3
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -82,11 +82,11 @@ | ||
| 82 | 82 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 83 | 83 | } |
| 84 | 84 | @ |
| 85 | 85 | @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 86 | 86 | @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> |
| 87 | - if( zComment ) while( isspace(zComment[0]) ) zComment++; | |
| 87 | + if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; | |
| 88 | 88 | if( zComment && zComment[0] ){ |
| 89 | 89 | @ %w(zComment)<br /> |
| 90 | 90 | } |
| 91 | 91 | if( zPage==0 && zTkt==0 ){ |
| 92 | 92 | if( zSrc==0 || zSrc[0]==0 ){ |
| @@ -251,13 +251,13 @@ | ||
| 251 | 251 | } |
| 252 | 252 | zName += n; |
| 253 | 253 | if( zName[0]==0 ) zName = "unknown"; |
| 254 | 254 | blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); |
| 255 | 255 | zComment = PD("comment", ""); |
| 256 | - while( isspace(zComment[0]) ) zComment++; | |
| 256 | + while( fossil_isspace(zComment[0]) ) zComment++; | |
| 257 | 257 | n = strlen(zComment); |
| 258 | - while( n>0 && isspace(zComment[n-1]) ){ n--; } | |
| 258 | + while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } | |
| 259 | 259 | if( n>0 ){ |
| 260 | 260 | blob_appendf(&manifest, "C %F\n", zComment); |
| 261 | 261 | } |
| 262 | 262 | zDate = db_text(0, "SELECT datetime('now')"); |
| 263 | 263 | zDate[10] = 'T'; |
| 264 | 264 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -82,11 +82,11 @@ | |
| 82 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 83 | } |
| 84 | @ |
| 85 | @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 86 | @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> |
| 87 | if( zComment ) while( isspace(zComment[0]) ) zComment++; |
| 88 | if( zComment && zComment[0] ){ |
| 89 | @ %w(zComment)<br /> |
| 90 | } |
| 91 | if( zPage==0 && zTkt==0 ){ |
| 92 | if( zSrc==0 || zSrc[0]==0 ){ |
| @@ -251,13 +251,13 @@ | |
| 251 | } |
| 252 | zName += n; |
| 253 | if( zName[0]==0 ) zName = "unknown"; |
| 254 | blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); |
| 255 | zComment = PD("comment", ""); |
| 256 | while( isspace(zComment[0]) ) zComment++; |
| 257 | n = strlen(zComment); |
| 258 | while( n>0 && isspace(zComment[n-1]) ){ n--; } |
| 259 | if( n>0 ){ |
| 260 | blob_appendf(&manifest, "C %F\n", zComment); |
| 261 | } |
| 262 | zDate = db_text(0, "SELECT datetime('now')"); |
| 263 | zDate[10] = 'T'; |
| 264 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -82,11 +82,11 @@ | |
| 82 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 83 | } |
| 84 | @ |
| 85 | @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 86 | @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> |
| 87 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 88 | if( zComment && zComment[0] ){ |
| 89 | @ %w(zComment)<br /> |
| 90 | } |
| 91 | if( zPage==0 && zTkt==0 ){ |
| 92 | if( zSrc==0 || zSrc[0]==0 ){ |
| @@ -251,13 +251,13 @@ | |
| 251 | } |
| 252 | zName += n; |
| 253 | if( zName[0]==0 ) zName = "unknown"; |
| 254 | blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID); |
| 255 | zComment = PD("comment", ""); |
| 256 | while( fossil_isspace(zComment[0]) ) zComment++; |
| 257 | n = strlen(zComment); |
| 258 | while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } |
| 259 | if( n>0 ){ |
| 260 | blob_appendf(&manifest, "C %F\n", zComment); |
| 261 | } |
| 262 | zDate = db_text(0, "SELECT datetime('now')"); |
| 263 | zDate[10] = 'T'; |
| 264 |
+1
-1
| --- src/bag.c | ||
| +++ src/bag.c | ||
| @@ -86,11 +86,11 @@ | ||
| 86 | 86 | int nDel = 0; /* Number of deleted entries */ |
| 87 | 87 | int nLive = 0; /* Number of live entries */ |
| 88 | 88 | |
| 89 | 89 | old = *p; |
| 90 | 90 | assert( newSize>old.cnt ); |
| 91 | - p->a = malloc( sizeof(p->a[0])*newSize ); | |
| 91 | + p->a = fossil_malloc( sizeof(p->a[0])*newSize ); | |
| 92 | 92 | p->sz = newSize; |
| 93 | 93 | memset(p->a, 0, sizeof(p->a[0])*newSize ); |
| 94 | 94 | for(i=0; i<old.sz; i++){ |
| 95 | 95 | int e = old.a[i]; |
| 96 | 96 | if( e>0 ){ |
| 97 | 97 |
| --- src/bag.c | |
| +++ src/bag.c | |
| @@ -86,11 +86,11 @@ | |
| 86 | int nDel = 0; /* Number of deleted entries */ |
| 87 | int nLive = 0; /* Number of live entries */ |
| 88 | |
| 89 | old = *p; |
| 90 | assert( newSize>old.cnt ); |
| 91 | p->a = malloc( sizeof(p->a[0])*newSize ); |
| 92 | p->sz = newSize; |
| 93 | memset(p->a, 0, sizeof(p->a[0])*newSize ); |
| 94 | for(i=0; i<old.sz; i++){ |
| 95 | int e = old.a[i]; |
| 96 | if( e>0 ){ |
| 97 |
| --- src/bag.c | |
| +++ src/bag.c | |
| @@ -86,11 +86,11 @@ | |
| 86 | int nDel = 0; /* Number of deleted entries */ |
| 87 | int nLive = 0; /* Number of live entries */ |
| 88 | |
| 89 | old = *p; |
| 90 | assert( newSize>old.cnt ); |
| 91 | p->a = fossil_malloc( sizeof(p->a[0])*newSize ); |
| 92 | p->sz = newSize; |
| 93 | memset(p->a, 0, sizeof(p->a[0])*newSize ); |
| 94 | for(i=0; i<old.sz; i++){ |
| 95 | int e = old.a[i]; |
| 96 | if( e>0 ){ |
| 97 |
+29
-14
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -72,25 +72,42 @@ | ||
| 72 | 72 | |
| 73 | 73 | /* |
| 74 | 74 | ** We find that the built-in isspace() function does not work for |
| 75 | 75 | ** some international character sets. So here is a substitute. |
| 76 | 76 | */ |
| 77 | -int blob_isspace(char c){ | |
| 77 | +int fossil_isspace(char c){ | |
| 78 | 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | 79 | } |
| 80 | + | |
| 81 | +/* | |
| 82 | +** Other replacements for ctype.h functions. | |
| 83 | +*/ | |
| 84 | +int fossil_islower(char c){ return c>='a' && c<='z'; } | |
| 85 | +int fossil_isupper(char c){ return c>='A' && c<='Z'; } | |
| 86 | +int fossil_isdigit(char c){ return c>='0' && c<='9'; } | |
| 87 | +int fossil_tolower(char c){ | |
| 88 | + return fossil_isupper(c) ? c - 'A' + 'a' : c; | |
| 89 | +} | |
| 90 | +int fossil_isalpha(char c){ | |
| 91 | + return (c>='a' && c<='z') || (c>='A' && c<='Z'); | |
| 92 | +} | |
| 93 | +int fossil_isalnum(char c){ | |
| 94 | + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); | |
| 95 | +} | |
| 96 | + | |
| 80 | 97 | |
| 81 | 98 | /* |
| 82 | 99 | ** COMMAND: test-isspace |
| 83 | 100 | */ |
| 84 | 101 | void isspace_cmd(void){ |
| 85 | 102 | int i; |
| 86 | 103 | for(i=0; i<=255; i++){ |
| 87 | 104 | if( i==' ' || i=='\n' || i=='\t' || i=='\v' |
| 88 | 105 | || i=='\f' || i=='\r' ){ |
| 89 | - assert( blob_isspace((char)i) ); | |
| 106 | + assert( fossil_isspace((char)i) ); | |
| 90 | 107 | }else{ |
| 91 | - assert( !blob_isspace((char)i) ); | |
| 108 | + assert( !fossil_isspace((char)i) ); | |
| 92 | 109 | } |
| 93 | 110 | } |
| 94 | 111 | printf("All 256 characters OK\n"); |
| 95 | 112 | } |
| 96 | 113 | |
| @@ -119,12 +136,11 @@ | ||
| 119 | 136 | pBlob->aData = 0; |
| 120 | 137 | pBlob->nAlloc = 0; |
| 121 | 138 | pBlob->nUsed = 0; |
| 122 | 139 | pBlob->iCursor = 0; |
| 123 | 140 | }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ |
| 124 | - char *pNew = realloc(pBlob->aData, newSize); | |
| 125 | - if( pNew==0 ) blob_panic(); | |
| 141 | + char *pNew = fossil_realloc(pBlob->aData, newSize); | |
| 126 | 142 | pBlob->aData = pNew; |
| 127 | 143 | pBlob->nAlloc = newSize; |
| 128 | 144 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 129 | 145 | pBlob->nUsed = pBlob->nAlloc; |
| 130 | 146 | } |
| @@ -145,12 +161,11 @@ | ||
| 145 | 161 | */ |
| 146 | 162 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 147 | 163 | if( newSize==0 ){ |
| 148 | 164 | *pBlob = empty_blob; |
| 149 | 165 | }else{ |
| 150 | - char *pNew = malloc( newSize ); | |
| 151 | - if( pNew==0 ) blob_panic(); | |
| 166 | + char *pNew = fossil_malloc( newSize ); | |
| 152 | 167 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 153 | 168 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 154 | 169 | pBlob->aData = pNew; |
| 155 | 170 | pBlob->xRealloc = blobReallocMalloc; |
| 156 | 171 | pBlob->nAlloc = newSize; |
| @@ -429,11 +444,11 @@ | ||
| 429 | 444 | ** not insert a new zero terminator. |
| 430 | 445 | */ |
| 431 | 446 | int blob_trim(Blob *p){ |
| 432 | 447 | char *z = p->aData; |
| 433 | 448 | int n = p->nUsed; |
| 434 | - while( n>0 && blob_isspace(z[n-1]) ){ n--; } | |
| 449 | + while( n>0 && fossil_isspace(z[n-1]) ){ n--; } | |
| 435 | 450 | p->nUsed = n; |
| 436 | 451 | return n; |
| 437 | 452 | } |
| 438 | 453 | |
| 439 | 454 | /* |
| @@ -452,15 +467,15 @@ | ||
| 452 | 467 | */ |
| 453 | 468 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 454 | 469 | char *aData = pFrom->aData; |
| 455 | 470 | int n = pFrom->nUsed; |
| 456 | 471 | int i = pFrom->iCursor; |
| 457 | - while( i<n && blob_isspace(aData[i]) ){ i++; } | |
| 472 | + while( i<n && fossil_isspace(aData[i]) ){ i++; } | |
| 458 | 473 | pFrom->iCursor = i; |
| 459 | - while( i<n && !blob_isspace(aData[i]) ){ i++; } | |
| 474 | + while( i<n && !fossil_isspace(aData[i]) ){ i++; } | |
| 460 | 475 | blob_extract(pFrom, i-pFrom->iCursor, pTo); |
| 461 | - while( i<n && blob_isspace(aData[i]) ){ i++; } | |
| 476 | + while( i<n && fossil_isspace(aData[i]) ){ i++; } | |
| 462 | 477 | pFrom->iCursor = i; |
| 463 | 478 | return pTo->nUsed; |
| 464 | 479 | } |
| 465 | 480 | |
| 466 | 481 | /* |
| @@ -523,11 +538,11 @@ | ||
| 523 | 538 | int blob_is_int(Blob *pBlob, int *pValue){ |
| 524 | 539 | const char *z = blob_buffer(pBlob); |
| 525 | 540 | int i, n, c, v; |
| 526 | 541 | n = blob_size(pBlob); |
| 527 | 542 | v = 0; |
| 528 | - for(i=0; i<n && (c = z[i])!=0 && isdigit(c); i++){ | |
| 543 | + for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){ | |
| 529 | 544 | v = v*10 + c - '0'; |
| 530 | 545 | } |
| 531 | 546 | if( i==n ){ |
| 532 | 547 | *pValue = v; |
| 533 | 548 | return 1; |
| @@ -612,11 +627,11 @@ | ||
| 612 | 627 | return blob_read_from_channel(pBlob, stdin, -1); |
| 613 | 628 | } |
| 614 | 629 | size = file_size(zFilename); |
| 615 | 630 | blob_zero(pBlob); |
| 616 | 631 | if( size<0 ){ |
| 617 | - fossil_panic("no such file: %s", zFilename); | |
| 632 | + fossil_fatal("no such file: %s", zFilename); | |
| 618 | 633 | } |
| 619 | 634 | if( size==0 ){ |
| 620 | 635 | return 0; |
| 621 | 636 | } |
| 622 | 637 | blob_resize(pBlob, size); |
| @@ -906,11 +921,11 @@ | ||
| 906 | 921 | int n = blob_size(pBlob); |
| 907 | 922 | int k = strlen(zIn); |
| 908 | 923 | int i, c; |
| 909 | 924 | char *z; |
| 910 | 925 | for(i=0; (c = zIn[i])!=0; i++){ |
| 911 | - if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){ | |
| 926 | + if( fossil_isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){ | |
| 912 | 927 | blob_appendf(pBlob, "\"%s\"", zIn); |
| 913 | 928 | z = blob_buffer(pBlob); |
| 914 | 929 | for(i=n+1; i<=n+k; i++){ |
| 915 | 930 | if( z[i]=='"' ) z[i] = '_'; |
| 916 | 931 | } |
| 917 | 932 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -72,25 +72,42 @@ | |
| 72 | |
| 73 | /* |
| 74 | ** We find that the built-in isspace() function does not work for |
| 75 | ** some international character sets. So here is a substitute. |
| 76 | */ |
| 77 | int blob_isspace(char c){ |
| 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** COMMAND: test-isspace |
| 83 | */ |
| 84 | void isspace_cmd(void){ |
| 85 | int i; |
| 86 | for(i=0; i<=255; i++){ |
| 87 | if( i==' ' || i=='\n' || i=='\t' || i=='\v' |
| 88 | || i=='\f' || i=='\r' ){ |
| 89 | assert( blob_isspace((char)i) ); |
| 90 | }else{ |
| 91 | assert( !blob_isspace((char)i) ); |
| 92 | } |
| 93 | } |
| 94 | printf("All 256 characters OK\n"); |
| 95 | } |
| 96 | |
| @@ -119,12 +136,11 @@ | |
| 119 | pBlob->aData = 0; |
| 120 | pBlob->nAlloc = 0; |
| 121 | pBlob->nUsed = 0; |
| 122 | pBlob->iCursor = 0; |
| 123 | }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ |
| 124 | char *pNew = realloc(pBlob->aData, newSize); |
| 125 | if( pNew==0 ) blob_panic(); |
| 126 | pBlob->aData = pNew; |
| 127 | pBlob->nAlloc = newSize; |
| 128 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 129 | pBlob->nUsed = pBlob->nAlloc; |
| 130 | } |
| @@ -145,12 +161,11 @@ | |
| 145 | */ |
| 146 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 147 | if( newSize==0 ){ |
| 148 | *pBlob = empty_blob; |
| 149 | }else{ |
| 150 | char *pNew = malloc( newSize ); |
| 151 | if( pNew==0 ) blob_panic(); |
| 152 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 153 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 154 | pBlob->aData = pNew; |
| 155 | pBlob->xRealloc = blobReallocMalloc; |
| 156 | pBlob->nAlloc = newSize; |
| @@ -429,11 +444,11 @@ | |
| 429 | ** not insert a new zero terminator. |
| 430 | */ |
| 431 | int blob_trim(Blob *p){ |
| 432 | char *z = p->aData; |
| 433 | int n = p->nUsed; |
| 434 | while( n>0 && blob_isspace(z[n-1]) ){ n--; } |
| 435 | p->nUsed = n; |
| 436 | return n; |
| 437 | } |
| 438 | |
| 439 | /* |
| @@ -452,15 +467,15 @@ | |
| 452 | */ |
| 453 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 454 | char *aData = pFrom->aData; |
| 455 | int n = pFrom->nUsed; |
| 456 | int i = pFrom->iCursor; |
| 457 | while( i<n && blob_isspace(aData[i]) ){ i++; } |
| 458 | pFrom->iCursor = i; |
| 459 | while( i<n && !blob_isspace(aData[i]) ){ i++; } |
| 460 | blob_extract(pFrom, i-pFrom->iCursor, pTo); |
| 461 | while( i<n && blob_isspace(aData[i]) ){ i++; } |
| 462 | pFrom->iCursor = i; |
| 463 | return pTo->nUsed; |
| 464 | } |
| 465 | |
| 466 | /* |
| @@ -523,11 +538,11 @@ | |
| 523 | int blob_is_int(Blob *pBlob, int *pValue){ |
| 524 | const char *z = blob_buffer(pBlob); |
| 525 | int i, n, c, v; |
| 526 | n = blob_size(pBlob); |
| 527 | v = 0; |
| 528 | for(i=0; i<n && (c = z[i])!=0 && isdigit(c); i++){ |
| 529 | v = v*10 + c - '0'; |
| 530 | } |
| 531 | if( i==n ){ |
| 532 | *pValue = v; |
| 533 | return 1; |
| @@ -612,11 +627,11 @@ | |
| 612 | return blob_read_from_channel(pBlob, stdin, -1); |
| 613 | } |
| 614 | size = file_size(zFilename); |
| 615 | blob_zero(pBlob); |
| 616 | if( size<0 ){ |
| 617 | fossil_panic("no such file: %s", zFilename); |
| 618 | } |
| 619 | if( size==0 ){ |
| 620 | return 0; |
| 621 | } |
| 622 | blob_resize(pBlob, size); |
| @@ -906,11 +921,11 @@ | |
| 906 | int n = blob_size(pBlob); |
| 907 | int k = strlen(zIn); |
| 908 | int i, c; |
| 909 | char *z; |
| 910 | for(i=0; (c = zIn[i])!=0; i++){ |
| 911 | if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){ |
| 912 | blob_appendf(pBlob, "\"%s\"", zIn); |
| 913 | z = blob_buffer(pBlob); |
| 914 | for(i=n+1; i<=n+k; i++){ |
| 915 | if( z[i]=='"' ) z[i] = '_'; |
| 916 | } |
| 917 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -72,25 +72,42 @@ | |
| 72 | |
| 73 | /* |
| 74 | ** We find that the built-in isspace() function does not work for |
| 75 | ** some international character sets. So here is a substitute. |
| 76 | */ |
| 77 | int fossil_isspace(char c){ |
| 78 | return c==' ' || (c<='\r' && c>='\t'); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Other replacements for ctype.h functions. |
| 83 | */ |
| 84 | int fossil_islower(char c){ return c>='a' && c<='z'; } |
| 85 | int fossil_isupper(char c){ return c>='A' && c<='Z'; } |
| 86 | int fossil_isdigit(char c){ return c>='0' && c<='9'; } |
| 87 | int fossil_tolower(char c){ |
| 88 | return fossil_isupper(c) ? c - 'A' + 'a' : c; |
| 89 | } |
| 90 | int fossil_isalpha(char c){ |
| 91 | return (c>='a' && c<='z') || (c>='A' && c<='Z'); |
| 92 | } |
| 93 | int fossil_isalnum(char c){ |
| 94 | return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); |
| 95 | } |
| 96 | |
| 97 | |
| 98 | /* |
| 99 | ** COMMAND: test-isspace |
| 100 | */ |
| 101 | void isspace_cmd(void){ |
| 102 | int i; |
| 103 | for(i=0; i<=255; i++){ |
| 104 | if( i==' ' || i=='\n' || i=='\t' || i=='\v' |
| 105 | || i=='\f' || i=='\r' ){ |
| 106 | assert( fossil_isspace((char)i) ); |
| 107 | }else{ |
| 108 | assert( !fossil_isspace((char)i) ); |
| 109 | } |
| 110 | } |
| 111 | printf("All 256 characters OK\n"); |
| 112 | } |
| 113 | |
| @@ -119,12 +136,11 @@ | |
| 136 | pBlob->aData = 0; |
| 137 | pBlob->nAlloc = 0; |
| 138 | pBlob->nUsed = 0; |
| 139 | pBlob->iCursor = 0; |
| 140 | }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ |
| 141 | char *pNew = fossil_realloc(pBlob->aData, newSize); |
| 142 | pBlob->aData = pNew; |
| 143 | pBlob->nAlloc = newSize; |
| 144 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 145 | pBlob->nUsed = pBlob->nAlloc; |
| 146 | } |
| @@ -145,12 +161,11 @@ | |
| 161 | */ |
| 162 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 163 | if( newSize==0 ){ |
| 164 | *pBlob = empty_blob; |
| 165 | }else{ |
| 166 | char *pNew = fossil_malloc( newSize ); |
| 167 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 168 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 169 | pBlob->aData = pNew; |
| 170 | pBlob->xRealloc = blobReallocMalloc; |
| 171 | pBlob->nAlloc = newSize; |
| @@ -429,11 +444,11 @@ | |
| 444 | ** not insert a new zero terminator. |
| 445 | */ |
| 446 | int blob_trim(Blob *p){ |
| 447 | char *z = p->aData; |
| 448 | int n = p->nUsed; |
| 449 | while( n>0 && fossil_isspace(z[n-1]) ){ n--; } |
| 450 | p->nUsed = n; |
| 451 | return n; |
| 452 | } |
| 453 | |
| 454 | /* |
| @@ -452,15 +467,15 @@ | |
| 467 | */ |
| 468 | int blob_token(Blob *pFrom, Blob *pTo){ |
| 469 | char *aData = pFrom->aData; |
| 470 | int n = pFrom->nUsed; |
| 471 | int i = pFrom->iCursor; |
| 472 | while( i<n && fossil_isspace(aData[i]) ){ i++; } |
| 473 | pFrom->iCursor = i; |
| 474 | while( i<n && !fossil_isspace(aData[i]) ){ i++; } |
| 475 | blob_extract(pFrom, i-pFrom->iCursor, pTo); |
| 476 | while( i<n && fossil_isspace(aData[i]) ){ i++; } |
| 477 | pFrom->iCursor = i; |
| 478 | return pTo->nUsed; |
| 479 | } |
| 480 | |
| 481 | /* |
| @@ -523,11 +538,11 @@ | |
| 538 | int blob_is_int(Blob *pBlob, int *pValue){ |
| 539 | const char *z = blob_buffer(pBlob); |
| 540 | int i, n, c, v; |
| 541 | n = blob_size(pBlob); |
| 542 | v = 0; |
| 543 | for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){ |
| 544 | v = v*10 + c - '0'; |
| 545 | } |
| 546 | if( i==n ){ |
| 547 | *pValue = v; |
| 548 | return 1; |
| @@ -612,11 +627,11 @@ | |
| 627 | return blob_read_from_channel(pBlob, stdin, -1); |
| 628 | } |
| 629 | size = file_size(zFilename); |
| 630 | blob_zero(pBlob); |
| 631 | if( size<0 ){ |
| 632 | fossil_fatal("no such file: %s", zFilename); |
| 633 | } |
| 634 | if( size==0 ){ |
| 635 | return 0; |
| 636 | } |
| 637 | blob_resize(pBlob, size); |
| @@ -906,11 +921,11 @@ | |
| 921 | int n = blob_size(pBlob); |
| 922 | int k = strlen(zIn); |
| 923 | int i, c; |
| 924 | char *z; |
| 925 | for(i=0; (c = zIn[i])!=0; i++){ |
| 926 | if( fossil_isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){ |
| 927 | blob_appendf(pBlob, "\"%s\"", zIn); |
| 928 | z = blob_buffer(pBlob); |
| 929 | for(i=n+1; i<=n+k; i++){ |
| 930 | if( z[i]=='"' ) z[i] = '_'; |
| 931 | } |
| 932 |
+20
-20
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -35,12 +35,11 @@ | ||
| 35 | 35 | const char *zBranch; /* Name of the new branch */ |
| 36 | 36 | char *zDate; /* Date that branch was created */ |
| 37 | 37 | char *zComment; /* Check-in comment for the new branch */ |
| 38 | 38 | const char *zColor; /* Color of the new branch */ |
| 39 | 39 | Blob branch; /* manifest for the new branch */ |
| 40 | - Blob parent; /* root check-in manifest */ | |
| 41 | - Manifest mParent; /* Parsed parent manifest */ | |
| 40 | + Manifest *pParent; /* Parsed parent manifest */ | |
| 42 | 41 | Blob mcksum; /* Self-checksum on the manifest */ |
| 43 | 42 | const char *zDateOvrd; /* Override date string */ |
| 44 | 43 | const char *zUserOvrd; /* Override user name */ |
| 45 | 44 | |
| 46 | 45 | noSign = find_option("nosign","",0)!=0; |
| @@ -71,41 +70,42 @@ | ||
| 71 | 70 | db_begin_transaction(); |
| 72 | 71 | rootid = name_to_rid(g.argv[4]); |
| 73 | 72 | if( rootid==0 ){ |
| 74 | 73 | fossil_fatal("unable to locate check-in off of which to branch"); |
| 75 | 74 | } |
| 75 | + | |
| 76 | + pParent = manifest_get(rootid, CFTYPE_MANIFEST); | |
| 77 | + if( pParent==0 ){ | |
| 78 | + fossil_fatal("%s is not a valid check-in", g.argv[4]); | |
| 79 | + } | |
| 76 | 80 | |
| 77 | 81 | /* Create a manifest for the new branch */ |
| 78 | 82 | blob_zero(&branch); |
| 83 | + if( pParent->zBaseline ){ | |
| 84 | + blob_appendf(&branch, "B %s\n", pParent->zBaseline); | |
| 85 | + } | |
| 79 | 86 | zComment = mprintf("Create new branch named \"%h\"", zBranch); |
| 80 | 87 | blob_appendf(&branch, "C %F\n", zComment); |
| 81 | 88 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 82 | 89 | zDate[10] = 'T'; |
| 83 | 90 | blob_appendf(&branch, "D %s\n", zDate); |
| 84 | 91 | |
| 85 | 92 | /* Copy all of the content from the parent into the branch */ |
| 86 | - content_get(rootid, &parent); | |
| 87 | - manifest_parse(&mParent, &parent); | |
| 88 | - if( mParent.type!=CFTYPE_MANIFEST ){ | |
| 89 | - fossil_fatal("%s is not a valid check-in", g.argv[4]); | |
| 90 | - } | |
| 91 | - for(i=0; i<mParent.nFile; ++i){ | |
| 92 | - if( mParent.aFile[i].zPerm[0] ){ | |
| 93 | - blob_appendf(&branch, "F %F %s %s\n", | |
| 94 | - mParent.aFile[i].zName, | |
| 95 | - mParent.aFile[i].zUuid, | |
| 96 | - mParent.aFile[i].zPerm); | |
| 97 | - }else{ | |
| 98 | - blob_appendf(&branch, "F %F %s\n", | |
| 99 | - mParent.aFile[i].zName, | |
| 100 | - mParent.aFile[i].zUuid); | |
| 101 | - } | |
| 93 | + for(i=0; i<pParent->nFile; ++i){ | |
| 94 | + blob_appendf(&branch, "F %F", pParent->aFile[i].zName); | |
| 95 | + if( pParent->aFile[i].zUuid ){ | |
| 96 | + blob_appendf(&branch, " %s", pParent->aFile[i].zUuid); | |
| 97 | + if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){ | |
| 98 | + blob_appendf(&branch, " %s", pParent->aFile[i].zPerm); | |
| 99 | + } | |
| 100 | + } | |
| 101 | + blob_append(&branch, "\n", 1); | |
| 102 | 102 | } |
| 103 | 103 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); |
| 104 | 104 | blob_appendf(&branch, "P %s\n", zUuid); |
| 105 | - blob_appendf(&branch, "R %s\n", mParent.zRepoCksum); | |
| 106 | - manifest_clear(&mParent); | |
| 105 | + blob_appendf(&branch, "R %s\n", pParent->zRepoCksum); | |
| 106 | + manifest_destroy(pParent); | |
| 107 | 107 | |
| 108 | 108 | /* Add the symbolic branch name and the "branch" tag to identify |
| 109 | 109 | ** this as a new branch */ |
| 110 | 110 | if( zColor!=0 ){ |
| 111 | 111 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 112 | 112 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -35,12 +35,11 @@ | |
| 35 | const char *zBranch; /* Name of the new branch */ |
| 36 | char *zDate; /* Date that branch was created */ |
| 37 | char *zComment; /* Check-in comment for the new branch */ |
| 38 | const char *zColor; /* Color of the new branch */ |
| 39 | Blob branch; /* manifest for the new branch */ |
| 40 | Blob parent; /* root check-in manifest */ |
| 41 | Manifest mParent; /* Parsed parent manifest */ |
| 42 | Blob mcksum; /* Self-checksum on the manifest */ |
| 43 | const char *zDateOvrd; /* Override date string */ |
| 44 | const char *zUserOvrd; /* Override user name */ |
| 45 | |
| 46 | noSign = find_option("nosign","",0)!=0; |
| @@ -71,41 +70,42 @@ | |
| 71 | db_begin_transaction(); |
| 72 | rootid = name_to_rid(g.argv[4]); |
| 73 | if( rootid==0 ){ |
| 74 | fossil_fatal("unable to locate check-in off of which to branch"); |
| 75 | } |
| 76 | |
| 77 | /* Create a manifest for the new branch */ |
| 78 | blob_zero(&branch); |
| 79 | zComment = mprintf("Create new branch named \"%h\"", zBranch); |
| 80 | blob_appendf(&branch, "C %F\n", zComment); |
| 81 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 82 | zDate[10] = 'T'; |
| 83 | blob_appendf(&branch, "D %s\n", zDate); |
| 84 | |
| 85 | /* Copy all of the content from the parent into the branch */ |
| 86 | content_get(rootid, &parent); |
| 87 | manifest_parse(&mParent, &parent); |
| 88 | if( mParent.type!=CFTYPE_MANIFEST ){ |
| 89 | fossil_fatal("%s is not a valid check-in", g.argv[4]); |
| 90 | } |
| 91 | for(i=0; i<mParent.nFile; ++i){ |
| 92 | if( mParent.aFile[i].zPerm[0] ){ |
| 93 | blob_appendf(&branch, "F %F %s %s\n", |
| 94 | mParent.aFile[i].zName, |
| 95 | mParent.aFile[i].zUuid, |
| 96 | mParent.aFile[i].zPerm); |
| 97 | }else{ |
| 98 | blob_appendf(&branch, "F %F %s\n", |
| 99 | mParent.aFile[i].zName, |
| 100 | mParent.aFile[i].zUuid); |
| 101 | } |
| 102 | } |
| 103 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); |
| 104 | blob_appendf(&branch, "P %s\n", zUuid); |
| 105 | blob_appendf(&branch, "R %s\n", mParent.zRepoCksum); |
| 106 | manifest_clear(&mParent); |
| 107 | |
| 108 | /* Add the symbolic branch name and the "branch" tag to identify |
| 109 | ** this as a new branch */ |
| 110 | if( zColor!=0 ){ |
| 111 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 112 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -35,12 +35,11 @@ | |
| 35 | const char *zBranch; /* Name of the new branch */ |
| 36 | char *zDate; /* Date that branch was created */ |
| 37 | char *zComment; /* Check-in comment for the new branch */ |
| 38 | const char *zColor; /* Color of the new branch */ |
| 39 | Blob branch; /* manifest for the new branch */ |
| 40 | Manifest *pParent; /* Parsed parent manifest */ |
| 41 | Blob mcksum; /* Self-checksum on the manifest */ |
| 42 | const char *zDateOvrd; /* Override date string */ |
| 43 | const char *zUserOvrd; /* Override user name */ |
| 44 | |
| 45 | noSign = find_option("nosign","",0)!=0; |
| @@ -71,41 +70,42 @@ | |
| 70 | db_begin_transaction(); |
| 71 | rootid = name_to_rid(g.argv[4]); |
| 72 | if( rootid==0 ){ |
| 73 | fossil_fatal("unable to locate check-in off of which to branch"); |
| 74 | } |
| 75 | |
| 76 | pParent = manifest_get(rootid, CFTYPE_MANIFEST); |
| 77 | if( pParent==0 ){ |
| 78 | fossil_fatal("%s is not a valid check-in", g.argv[4]); |
| 79 | } |
| 80 | |
| 81 | /* Create a manifest for the new branch */ |
| 82 | blob_zero(&branch); |
| 83 | if( pParent->zBaseline ){ |
| 84 | blob_appendf(&branch, "B %s\n", pParent->zBaseline); |
| 85 | } |
| 86 | zComment = mprintf("Create new branch named \"%h\"", zBranch); |
| 87 | blob_appendf(&branch, "C %F\n", zComment); |
| 88 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 89 | zDate[10] = 'T'; |
| 90 | blob_appendf(&branch, "D %s\n", zDate); |
| 91 | |
| 92 | /* Copy all of the content from the parent into the branch */ |
| 93 | for(i=0; i<pParent->nFile; ++i){ |
| 94 | blob_appendf(&branch, "F %F", pParent->aFile[i].zName); |
| 95 | if( pParent->aFile[i].zUuid ){ |
| 96 | blob_appendf(&branch, " %s", pParent->aFile[i].zUuid); |
| 97 | if( pParent->aFile[i].zPerm && pParent->aFile[i].zPerm[0] ){ |
| 98 | blob_appendf(&branch, " %s", pParent->aFile[i].zPerm); |
| 99 | } |
| 100 | } |
| 101 | blob_append(&branch, "\n", 1); |
| 102 | } |
| 103 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid); |
| 104 | blob_appendf(&branch, "P %s\n", zUuid); |
| 105 | blob_appendf(&branch, "R %s\n", pParent->zRepoCksum); |
| 106 | manifest_destroy(pParent); |
| 107 | |
| 108 | /* Add the symbolic branch name and the "branch" tag to identify |
| 109 | ** this as a new branch */ |
| 110 | if( zColor!=0 ){ |
| 111 | blob_appendf(&branch, "T *bgcolor * %F\n", zColor); |
| 112 |
+61
-40
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -71,19 +71,24 @@ | ||
| 71 | 71 | ** There is no hyperlink on the file element of the path. |
| 72 | 72 | ** |
| 73 | 73 | ** The computed string is appended to the pOut blob. pOut should |
| 74 | 74 | ** have already been initialized. |
| 75 | 75 | */ |
| 76 | -void hyperlinked_path(const char *zPath, Blob *pOut){ | |
| 76 | +void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){ | |
| 77 | 77 | int i, j; |
| 78 | 78 | char *zSep = ""; |
| 79 | 79 | |
| 80 | 80 | for(i=0; zPath[i]; i=j){ |
| 81 | 81 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 82 | 82 | if( zPath[j] && g.okHistory ){ |
| 83 | - blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", | |
| 84 | - zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); | |
| 83 | + if( zCI ){ | |
| 84 | + blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&name=%#T\">%#h</a>", | |
| 85 | + zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]); | |
| 86 | + }else{ | |
| 87 | + blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", | |
| 88 | + zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); | |
| 89 | + } | |
| 85 | 90 | }else{ |
| 86 | 91 | blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); |
| 87 | 92 | } |
| 88 | 93 | zSep = "/"; |
| 89 | 94 | while( zPath[j]=='/' ){ j++; } |
| @@ -99,21 +104,21 @@ | ||
| 99 | 104 | ** name=PATH Directory to display. Required. |
| 100 | 105 | ** ci=LABEL Show only files in this check-in. Optional. |
| 101 | 106 | */ |
| 102 | 107 | void page_dir(void){ |
| 103 | 108 | const char *zD = P("name"); |
| 109 | + int nD = zD ? strlen(zD)+1 : 0; | |
| 104 | 110 | int mxLen; |
| 105 | 111 | int nCol, nRow; |
| 106 | 112 | int cnt, i; |
| 107 | 113 | char *zPrefix; |
| 108 | 114 | Stmt q; |
| 109 | 115 | const char *zCI = P("ci"); |
| 110 | 116 | int rid = 0; |
| 111 | 117 | char *zUuid = 0; |
| 112 | - Blob content; | |
| 113 | 118 | Blob dirname; |
| 114 | - Manifest m; | |
| 119 | + Manifest *pM = 0; | |
| 115 | 120 | const char *zSubdirLink; |
| 116 | 121 | |
| 117 | 122 | login_check_credentials(); |
| 118 | 123 | if( !g.okHistory ){ login_needed(); return; } |
| 119 | 124 | style_header("File List"); |
| @@ -126,25 +131,24 @@ | ||
| 126 | 131 | /* If a specific check-in is requested, fetch and parse it. If the |
| 127 | 132 | ** specific check-in does not exist, clear zCI. zCI==0 will cause all |
| 128 | 133 | ** files from all check-ins to be displayed. |
| 129 | 134 | */ |
| 130 | 135 | if( zCI ){ |
| 131 | - if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){ | |
| 132 | - zCI = 0; /* No artifact named zCI exists */ | |
| 133 | - }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ | |
| 134 | - zCI = 0; /* The artifact exists but is not a manifest */ | |
| 136 | + pM = manifest_get_by_name(zCI, &rid); | |
| 137 | + if( pM ){ | |
| 138 | + zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 135 | 139 | }else{ |
| 136 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 140 | + zCI = 0; | |
| 137 | 141 | } |
| 138 | 142 | } |
| 139 | 143 | |
| 140 | 144 | |
| 141 | 145 | /* Compute the title of the page */ |
| 142 | 146 | blob_zero(&dirname); |
| 143 | 147 | if( zD ){ |
| 144 | 148 | blob_append(&dirname, "in directory ", -1); |
| 145 | - hyperlinked_path(zD, &dirname); | |
| 149 | + hyperlinked_path(zD, &dirname, zCI); | |
| 146 | 150 | zPrefix = mprintf("%h/", zD); |
| 147 | 151 | }else{ |
| 148 | 152 | blob_append(&dirname, "in the top-level directory", -1); |
| 149 | 153 | zPrefix = ""; |
| 150 | 154 | } |
| @@ -160,22 +164,30 @@ | ||
| 160 | 164 | style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD); |
| 161 | 165 | }else{ |
| 162 | 166 | style_submenu_element("All", "All", "%s/dir", g.zBaseURL); |
| 163 | 167 | } |
| 164 | 168 | }else{ |
| 169 | + int hasTrunk; | |
| 165 | 170 | @ <h2>The union of all files from all check-ins |
| 166 | 171 | @ %s(blob_str(&dirname))</h2> |
| 172 | + hasTrunk = db_exists( | |
| 173 | + "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'", | |
| 174 | + TAG_BRANCH); | |
| 167 | 175 | zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); |
| 168 | 176 | if( zD ){ |
| 169 | 177 | style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL); |
| 170 | 178 | style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip", |
| 171 | 179 | g.zBaseURL, zD); |
| 172 | - style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", | |
| 173 | - g.zBaseURL,zD); | |
| 180 | + if( hasTrunk ){ | |
| 181 | + style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", | |
| 182 | + g.zBaseURL,zD); | |
| 183 | + } | |
| 174 | 184 | }else{ |
| 175 | 185 | style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL); |
| 176 | - style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); | |
| 186 | + if( hasTrunk ){ | |
| 187 | + style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); | |
| 188 | + } | |
| 177 | 189 | } |
| 178 | 190 | } |
| 179 | 191 | |
| 180 | 192 | /* Compute the temporary table "localfiles" containing the names |
| 181 | 193 | ** of all files and subdirectories in the zD[] directory. |
| @@ -184,39 +196,47 @@ | ||
| 184 | 196 | ** first and it also gives us an easy way to distinguish files |
| 185 | 197 | ** from directories in the loop that follows. |
| 186 | 198 | */ |
| 187 | 199 | db_multi_exec( |
| 188 | 200 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 189 | - "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);" | |
| 190 | 201 | ); |
| 191 | 202 | if( zCI ){ |
| 192 | 203 | Stmt ins; |
| 193 | - int i; | |
| 194 | - db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)"); | |
| 195 | - for(i=0; i<m.nFile; i++){ | |
| 196 | - db_bind_text(&ins, ":x", m.aFile[i].zName); | |
| 197 | - db_bind_text(&ins, ":u", m.aFile[i].zUuid); | |
| 198 | - db_step(&ins); | |
| 199 | - db_reset(&ins); | |
| 200 | - } | |
| 201 | - db_finalize(&ins); | |
| 202 | - }else{ | |
| 203 | - db_multi_exec( | |
| 204 | - "INSERT INTO allfiles SELECT name, NULL FROM filename" | |
| 205 | - ); | |
| 206 | - } | |
| 207 | - if( zD ){ | |
| 208 | - db_multi_exec( | |
| 209 | - "INSERT OR IGNORE INTO localfiles " | |
| 210 | - " SELECT pathelement(x,%d), u FROM allfiles" | |
| 211 | - " WHERE x GLOB '%q/*'", | |
| 212 | - strlen(zD)+1, zD | |
| 213 | - ); | |
| 214 | - }else{ | |
| 215 | - db_multi_exec( | |
| 216 | - "INSERT OR IGNORE INTO localfiles " | |
| 217 | - " SELECT pathelement(x,0), u FROM allfiles" | |
| 204 | + ManifestFile *pFile; | |
| 205 | + ManifestFile *pPrev = 0; | |
| 206 | + int nPrev = 0; | |
| 207 | + int c; | |
| 208 | + | |
| 209 | + db_prepare(&ins, | |
| 210 | + "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" | |
| 211 | + ); | |
| 212 | + manifest_file_rewind(pM); | |
| 213 | + while( (pFile = manifest_file_next(pM,0))!=0 ){ | |
| 214 | + if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue; | |
| 215 | + if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){ | |
| 216 | + continue; | |
| 217 | + } | |
| 218 | + db_bind_text(&ins, ":x", &pFile->zName[nD]); | |
| 219 | + db_bind_text(&ins, ":u", pFile->zUuid); | |
| 220 | + db_step(&ins); | |
| 221 | + db_reset(&ins); | |
| 222 | + pPrev = pFile; | |
| 223 | + for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} | |
| 224 | + if( c=='/' ) nPrev++; | |
| 225 | + } | |
| 226 | + db_finalize(&ins); | |
| 227 | + }else if( zD ){ | |
| 228 | + db_multi_exec( | |
| 229 | + "INSERT OR IGNORE INTO localfiles" | |
| 230 | + " SELECT pathelement(name,%d), NULL FROM filename" | |
| 231 | + " WHERE name GLOB '%q/*'", | |
| 232 | + nD, zD | |
| 233 | + ); | |
| 234 | + }else{ | |
| 235 | + db_multi_exec( | |
| 236 | + "INSERT OR IGNORE INTO localfiles" | |
| 237 | + " SELECT pathelement(name,0), NULL FROM filename" | |
| 218 | 238 | ); |
| 219 | 239 | } |
| 220 | 240 | |
| 221 | 241 | /* Generate a multi-column table listing the contents of zD[] |
| 222 | 242 | ** directory. |
| @@ -246,8 +266,9 @@ | ||
| 246 | 266 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN) |
| 247 | 267 | @ </a></li> |
| 248 | 268 | } |
| 249 | 269 | } |
| 250 | 270 | db_finalize(&q); |
| 271 | + manifest_destroy(pM); | |
| 251 | 272 | @ </ul></td></tr></table> |
| 252 | 273 | style_footer(); |
| 253 | 274 | } |
| 254 | 275 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -71,19 +71,24 @@ | |
| 71 | ** There is no hyperlink on the file element of the path. |
| 72 | ** |
| 73 | ** The computed string is appended to the pOut blob. pOut should |
| 74 | ** have already been initialized. |
| 75 | */ |
| 76 | void hyperlinked_path(const char *zPath, Blob *pOut){ |
| 77 | int i, j; |
| 78 | char *zSep = ""; |
| 79 | |
| 80 | for(i=0; zPath[i]; i=j){ |
| 81 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 82 | if( zPath[j] && g.okHistory ){ |
| 83 | blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", |
| 84 | zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); |
| 85 | }else{ |
| 86 | blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); |
| 87 | } |
| 88 | zSep = "/"; |
| 89 | while( zPath[j]=='/' ){ j++; } |
| @@ -99,21 +104,21 @@ | |
| 99 | ** name=PATH Directory to display. Required. |
| 100 | ** ci=LABEL Show only files in this check-in. Optional. |
| 101 | */ |
| 102 | void page_dir(void){ |
| 103 | const char *zD = P("name"); |
| 104 | int mxLen; |
| 105 | int nCol, nRow; |
| 106 | int cnt, i; |
| 107 | char *zPrefix; |
| 108 | Stmt q; |
| 109 | const char *zCI = P("ci"); |
| 110 | int rid = 0; |
| 111 | char *zUuid = 0; |
| 112 | Blob content; |
| 113 | Blob dirname; |
| 114 | Manifest m; |
| 115 | const char *zSubdirLink; |
| 116 | |
| 117 | login_check_credentials(); |
| 118 | if( !g.okHistory ){ login_needed(); return; } |
| 119 | style_header("File List"); |
| @@ -126,25 +131,24 @@ | |
| 126 | /* If a specific check-in is requested, fetch and parse it. If the |
| 127 | ** specific check-in does not exist, clear zCI. zCI==0 will cause all |
| 128 | ** files from all check-ins to be displayed. |
| 129 | */ |
| 130 | if( zCI ){ |
| 131 | if( (rid = name_to_rid(zCI))==0 || content_get(rid, &content)==0 ){ |
| 132 | zCI = 0; /* No artifact named zCI exists */ |
| 133 | }else if( !manifest_parse(&m, &content) || m.type!=CFTYPE_MANIFEST ){ |
| 134 | zCI = 0; /* The artifact exists but is not a manifest */ |
| 135 | }else{ |
| 136 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | |
| 141 | /* Compute the title of the page */ |
| 142 | blob_zero(&dirname); |
| 143 | if( zD ){ |
| 144 | blob_append(&dirname, "in directory ", -1); |
| 145 | hyperlinked_path(zD, &dirname); |
| 146 | zPrefix = mprintf("%h/", zD); |
| 147 | }else{ |
| 148 | blob_append(&dirname, "in the top-level directory", -1); |
| 149 | zPrefix = ""; |
| 150 | } |
| @@ -160,22 +164,30 @@ | |
| 160 | style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD); |
| 161 | }else{ |
| 162 | style_submenu_element("All", "All", "%s/dir", g.zBaseURL); |
| 163 | } |
| 164 | }else{ |
| 165 | @ <h2>The union of all files from all check-ins |
| 166 | @ %s(blob_str(&dirname))</h2> |
| 167 | zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); |
| 168 | if( zD ){ |
| 169 | style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL); |
| 170 | style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip", |
| 171 | g.zBaseURL, zD); |
| 172 | style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", |
| 173 | g.zBaseURL,zD); |
| 174 | }else{ |
| 175 | style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL); |
| 176 | style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | /* Compute the temporary table "localfiles" containing the names |
| 181 | ** of all files and subdirectories in the zD[] directory. |
| @@ -184,39 +196,47 @@ | |
| 184 | ** first and it also gives us an easy way to distinguish files |
| 185 | ** from directories in the loop that follows. |
| 186 | */ |
| 187 | db_multi_exec( |
| 188 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 189 | "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);" |
| 190 | ); |
| 191 | if( zCI ){ |
| 192 | Stmt ins; |
| 193 | int i; |
| 194 | db_prepare(&ins, "INSERT INTO allfiles VALUES(:x, :u)"); |
| 195 | for(i=0; i<m.nFile; i++){ |
| 196 | db_bind_text(&ins, ":x", m.aFile[i].zName); |
| 197 | db_bind_text(&ins, ":u", m.aFile[i].zUuid); |
| 198 | db_step(&ins); |
| 199 | db_reset(&ins); |
| 200 | } |
| 201 | db_finalize(&ins); |
| 202 | }else{ |
| 203 | db_multi_exec( |
| 204 | "INSERT INTO allfiles SELECT name, NULL FROM filename" |
| 205 | ); |
| 206 | } |
| 207 | if( zD ){ |
| 208 | db_multi_exec( |
| 209 | "INSERT OR IGNORE INTO localfiles " |
| 210 | " SELECT pathelement(x,%d), u FROM allfiles" |
| 211 | " WHERE x GLOB '%q/*'", |
| 212 | strlen(zD)+1, zD |
| 213 | ); |
| 214 | }else{ |
| 215 | db_multi_exec( |
| 216 | "INSERT OR IGNORE INTO localfiles " |
| 217 | " SELECT pathelement(x,0), u FROM allfiles" |
| 218 | ); |
| 219 | } |
| 220 | |
| 221 | /* Generate a multi-column table listing the contents of zD[] |
| 222 | ** directory. |
| @@ -246,8 +266,9 @@ | |
| 246 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN) |
| 247 | @ </a></li> |
| 248 | } |
| 249 | } |
| 250 | db_finalize(&q); |
| 251 | @ </ul></td></tr></table> |
| 252 | style_footer(); |
| 253 | } |
| 254 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -71,19 +71,24 @@ | |
| 71 | ** There is no hyperlink on the file element of the path. |
| 72 | ** |
| 73 | ** The computed string is appended to the pOut blob. pOut should |
| 74 | ** have already been initialized. |
| 75 | */ |
| 76 | void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){ |
| 77 | int i, j; |
| 78 | char *zSep = ""; |
| 79 | |
| 80 | for(i=0; zPath[i]; i=j){ |
| 81 | for(j=i; zPath[j] && zPath[j]!='/'; j++){} |
| 82 | if( zPath[j] && g.okHistory ){ |
| 83 | if( zCI ){ |
| 84 | blob_appendf(pOut, "%s<a href=\"%s/dir?ci=%S&name=%#T\">%#h</a>", |
| 85 | zSep, g.zBaseURL, zCI, j, zPath, j-i, &zPath[i]); |
| 86 | }else{ |
| 87 | blob_appendf(pOut, "%s<a href=\"%s/dir?name=%#T\">%#h</a>", |
| 88 | zSep, g.zBaseURL, j, zPath, j-i, &zPath[i]); |
| 89 | } |
| 90 | }else{ |
| 91 | blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); |
| 92 | } |
| 93 | zSep = "/"; |
| 94 | while( zPath[j]=='/' ){ j++; } |
| @@ -99,21 +104,21 @@ | |
| 104 | ** name=PATH Directory to display. Required. |
| 105 | ** ci=LABEL Show only files in this check-in. Optional. |
| 106 | */ |
| 107 | void page_dir(void){ |
| 108 | const char *zD = P("name"); |
| 109 | int nD = zD ? strlen(zD)+1 : 0; |
| 110 | int mxLen; |
| 111 | int nCol, nRow; |
| 112 | int cnt, i; |
| 113 | char *zPrefix; |
| 114 | Stmt q; |
| 115 | const char *zCI = P("ci"); |
| 116 | int rid = 0; |
| 117 | char *zUuid = 0; |
| 118 | Blob dirname; |
| 119 | Manifest *pM = 0; |
| 120 | const char *zSubdirLink; |
| 121 | |
| 122 | login_check_credentials(); |
| 123 | if( !g.okHistory ){ login_needed(); return; } |
| 124 | style_header("File List"); |
| @@ -126,25 +131,24 @@ | |
| 131 | /* If a specific check-in is requested, fetch and parse it. If the |
| 132 | ** specific check-in does not exist, clear zCI. zCI==0 will cause all |
| 133 | ** files from all check-ins to be displayed. |
| 134 | */ |
| 135 | if( zCI ){ |
| 136 | pM = manifest_get_by_name(zCI, &rid); |
| 137 | if( pM ){ |
| 138 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 139 | }else{ |
| 140 | zCI = 0; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | |
| 145 | /* Compute the title of the page */ |
| 146 | blob_zero(&dirname); |
| 147 | if( zD ){ |
| 148 | blob_append(&dirname, "in directory ", -1); |
| 149 | hyperlinked_path(zD, &dirname, zCI); |
| 150 | zPrefix = mprintf("%h/", zD); |
| 151 | }else{ |
| 152 | blob_append(&dirname, "in the top-level directory", -1); |
| 153 | zPrefix = ""; |
| 154 | } |
| @@ -160,22 +164,30 @@ | |
| 164 | style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD); |
| 165 | }else{ |
| 166 | style_submenu_element("All", "All", "%s/dir", g.zBaseURL); |
| 167 | } |
| 168 | }else{ |
| 169 | int hasTrunk; |
| 170 | @ <h2>The union of all files from all check-ins |
| 171 | @ %s(blob_str(&dirname))</h2> |
| 172 | hasTrunk = db_exists( |
| 173 | "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'", |
| 174 | TAG_BRANCH); |
| 175 | zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix); |
| 176 | if( zD ){ |
| 177 | style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL); |
| 178 | style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip", |
| 179 | g.zBaseURL, zD); |
| 180 | if( hasTrunk ){ |
| 181 | style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk", |
| 182 | g.zBaseURL,zD); |
| 183 | } |
| 184 | }else{ |
| 185 | style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL); |
| 186 | if( hasTrunk ){ |
| 187 | style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL); |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | /* Compute the temporary table "localfiles" containing the names |
| 193 | ** of all files and subdirectories in the zD[] directory. |
| @@ -184,39 +196,47 @@ | |
| 196 | ** first and it also gives us an easy way to distinguish files |
| 197 | ** from directories in the loop that follows. |
| 198 | */ |
| 199 | db_multi_exec( |
| 200 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 201 | ); |
| 202 | if( zCI ){ |
| 203 | Stmt ins; |
| 204 | ManifestFile *pFile; |
| 205 | ManifestFile *pPrev = 0; |
| 206 | int nPrev = 0; |
| 207 | int c; |
| 208 | |
| 209 | db_prepare(&ins, |
| 210 | "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" |
| 211 | ); |
| 212 | manifest_file_rewind(pM); |
| 213 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 214 | if( nD>0 && memcmp(pFile->zName, zD, nD-1)!=0 ) continue; |
| 215 | if( pPrev && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 ){ |
| 216 | continue; |
| 217 | } |
| 218 | db_bind_text(&ins, ":x", &pFile->zName[nD]); |
| 219 | db_bind_text(&ins, ":u", pFile->zUuid); |
| 220 | db_step(&ins); |
| 221 | db_reset(&ins); |
| 222 | pPrev = pFile; |
| 223 | for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} |
| 224 | if( c=='/' ) nPrev++; |
| 225 | } |
| 226 | db_finalize(&ins); |
| 227 | }else if( zD ){ |
| 228 | db_multi_exec( |
| 229 | "INSERT OR IGNORE INTO localfiles" |
| 230 | " SELECT pathelement(name,%d), NULL FROM filename" |
| 231 | " WHERE name GLOB '%q/*'", |
| 232 | nD, zD |
| 233 | ); |
| 234 | }else{ |
| 235 | db_multi_exec( |
| 236 | "INSERT OR IGNORE INTO localfiles" |
| 237 | " SELECT pathelement(name,0), NULL FROM filename" |
| 238 | ); |
| 239 | } |
| 240 | |
| 241 | /* Generate a multi-column table listing the contents of zD[] |
| 242 | ** directory. |
| @@ -246,8 +266,9 @@ | |
| 266 | @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN) |
| 267 | @ </a></li> |
| 268 | } |
| 269 | } |
| 270 | db_finalize(&q); |
| 271 | manifest_destroy(pM); |
| 272 | @ </ul></td></tr></table> |
| 273 | style_footer(); |
| 274 | } |
| 275 |
+3
-3
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -69,11 +69,11 @@ | ||
| 69 | 69 | ** Render an 8-character hexadecimal string as ascii art. |
| 70 | 70 | ** Space to hold the result is obtained from malloc() and should be freed |
| 71 | 71 | ** by the caller. |
| 72 | 72 | */ |
| 73 | 73 | char *captcha_render(const char *zPw){ |
| 74 | - char *z = malloc( 500 ); | |
| 74 | + char *z = fossil_malloc( 500 ); | |
| 75 | 75 | int i, j, k, m; |
| 76 | 76 | |
| 77 | 77 | k = 0; |
| 78 | 78 | for(i=0; i<6; i++){ |
| 79 | 79 | for(j=0; j<8; j++){ |
| @@ -202,11 +202,11 @@ | ||
| 202 | 202 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 203 | 203 | ** Space to hold the result is obtained from malloc() and should be freed |
| 204 | 204 | ** by the caller. |
| 205 | 205 | */ |
| 206 | 206 | char *captcha_render(const char *zPw){ |
| 207 | - char *z = malloc( 300 ); | |
| 207 | + char *z = fossil_malloc( 300 ); | |
| 208 | 208 | int i, j, k, m; |
| 209 | 209 | const char *zChar; |
| 210 | 210 | |
| 211 | 211 | k = 0; |
| 212 | 212 | for(i=0; i<4; i++){ |
| @@ -359,11 +359,11 @@ | ||
| 359 | 359 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 360 | 360 | ** Space to hold the result is obtained from malloc() and should be freed |
| 361 | 361 | ** by the caller. |
| 362 | 362 | */ |
| 363 | 363 | char *captcha_render(const char *zPw){ |
| 364 | - char *z = malloc( 600 ); | |
| 364 | + char *z = fossil_malloc( 600 ); | |
| 365 | 365 | int i, j, k, m; |
| 366 | 366 | const char *zChar; |
| 367 | 367 | |
| 368 | 368 | k = 0; |
| 369 | 369 | for(i=0; i<6; i++){ |
| 370 | 370 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -69,11 +69,11 @@ | |
| 69 | ** Render an 8-character hexadecimal string as ascii art. |
| 70 | ** Space to hold the result is obtained from malloc() and should be freed |
| 71 | ** by the caller. |
| 72 | */ |
| 73 | char *captcha_render(const char *zPw){ |
| 74 | char *z = malloc( 500 ); |
| 75 | int i, j, k, m; |
| 76 | |
| 77 | k = 0; |
| 78 | for(i=0; i<6; i++){ |
| 79 | for(j=0; j<8; j++){ |
| @@ -202,11 +202,11 @@ | |
| 202 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 203 | ** Space to hold the result is obtained from malloc() and should be freed |
| 204 | ** by the caller. |
| 205 | */ |
| 206 | char *captcha_render(const char *zPw){ |
| 207 | char *z = malloc( 300 ); |
| 208 | int i, j, k, m; |
| 209 | const char *zChar; |
| 210 | |
| 211 | k = 0; |
| 212 | for(i=0; i<4; i++){ |
| @@ -359,11 +359,11 @@ | |
| 359 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 360 | ** Space to hold the result is obtained from malloc() and should be freed |
| 361 | ** by the caller. |
| 362 | */ |
| 363 | char *captcha_render(const char *zPw){ |
| 364 | char *z = malloc( 600 ); |
| 365 | int i, j, k, m; |
| 366 | const char *zChar; |
| 367 | |
| 368 | k = 0; |
| 369 | for(i=0; i<6; i++){ |
| 370 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -69,11 +69,11 @@ | |
| 69 | ** Render an 8-character hexadecimal string as ascii art. |
| 70 | ** Space to hold the result is obtained from malloc() and should be freed |
| 71 | ** by the caller. |
| 72 | */ |
| 73 | char *captcha_render(const char *zPw){ |
| 74 | char *z = fossil_malloc( 500 ); |
| 75 | int i, j, k, m; |
| 76 | |
| 77 | k = 0; |
| 78 | for(i=0; i<6; i++){ |
| 79 | for(j=0; j<8; j++){ |
| @@ -202,11 +202,11 @@ | |
| 202 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 203 | ** Space to hold the result is obtained from malloc() and should be freed |
| 204 | ** by the caller. |
| 205 | */ |
| 206 | char *captcha_render(const char *zPw){ |
| 207 | char *z = fossil_malloc( 300 ); |
| 208 | int i, j, k, m; |
| 209 | const char *zChar; |
| 210 | |
| 211 | k = 0; |
| 212 | for(i=0; i<4; i++){ |
| @@ -359,11 +359,11 @@ | |
| 359 | ** Render an 8-digit hexadecimal string as ascii arg. |
| 360 | ** Space to hold the result is obtained from malloc() and should be freed |
| 361 | ** by the caller. |
| 362 | */ |
| 363 | char *captcha_render(const char *zPw){ |
| 364 | char *z = fossil_malloc( 600 ); |
| 365 | int i, j, k, m; |
| 366 | const char *zChar; |
| 367 | |
| 368 | k = 0; |
| 369 | for(i=0; i<6; i++){ |
| 370 |
+23
-24
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -387,12 +387,11 @@ | ||
| 387 | 387 | ** deallocated after this routine returns. |
| 388 | 388 | */ |
| 389 | 389 | void cgi_set_parameter_nocopy(const char *zName, const char *zValue){ |
| 390 | 390 | if( nAllocQP<=nUsedQP ){ |
| 391 | 391 | nAllocQP = nAllocQP*2 + 10; |
| 392 | - aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); | |
| 393 | - if( aParamQP==0 ) fossil_exit(1); | |
| 392 | + aParamQP = fossil_realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); | |
| 394 | 393 | } |
| 395 | 394 | aParamQP[nUsedQP].zName = zName; |
| 396 | 395 | aParamQP[nUsedQP].zValue = zValue; |
| 397 | 396 | if( g.fHttpTrace ){ |
| 398 | 397 | fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); |
| @@ -463,11 +462,11 @@ | ||
| 463 | 462 | */ |
| 464 | 463 | static void add_param_list(char *z, int terminator){ |
| 465 | 464 | while( *z ){ |
| 466 | 465 | char *zName; |
| 467 | 466 | char *zValue; |
| 468 | - while( isspace(*z) ){ z++; } | |
| 467 | + while( fossil_isspace(*z) ){ z++; } | |
| 469 | 468 | zName = z; |
| 470 | 469 | while( *z && *z!='=' && *z!=terminator ){ z++; } |
| 471 | 470 | if( *z=='=' ){ |
| 472 | 471 | *z = 0; |
| 473 | 472 | z++; |
| @@ -480,11 +479,11 @@ | ||
| 480 | 479 | dehttpize(zValue); |
| 481 | 480 | }else{ |
| 482 | 481 | if( *z ){ *z++ = 0; } |
| 483 | 482 | zValue = ""; |
| 484 | 483 | } |
| 485 | - if( islower(zName[0]) ){ | |
| 484 | + if( fossil_islower(zName[0]) ){ | |
| 486 | 485 | cgi_set_parameter_nocopy(zName, zValue); |
| 487 | 486 | } |
| 488 | 487 | } |
| 489 | 488 | } |
| 490 | 489 | |
| @@ -575,11 +574,11 @@ | ||
| 575 | 574 | ** in the example above. |
| 576 | 575 | */ |
| 577 | 576 | static int tokenize_line(char *z, int mxArg, char **azArg){ |
| 578 | 577 | int i = 0; |
| 579 | 578 | while( *z ){ |
| 580 | - while( isspace(*z) || *z==';' ){ z++; } | |
| 579 | + while( fossil_isspace(*z) || *z==';' ){ z++; } | |
| 581 | 580 | if( *z=='"' && z[1] ){ |
| 582 | 581 | *z = 0; |
| 583 | 582 | z++; |
| 584 | 583 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 585 | 584 | while( *z && *z!='"' ){ z++; } |
| @@ -586,11 +585,11 @@ | ||
| 586 | 585 | if( *z==0 ) break; |
| 587 | 586 | *z = 0; |
| 588 | 587 | z++; |
| 589 | 588 | }else{ |
| 590 | 589 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 591 | - while( *z && !isspace(*z) && *z!=';' && *z!='"' ){ z++; } | |
| 590 | + while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; } | |
| 592 | 591 | if( *z && *z!='"' ){ |
| 593 | 592 | *z = 0; |
| 594 | 593 | z++; |
| 595 | 594 | } |
| 596 | 595 | } |
| @@ -621,11 +620,11 @@ | ||
| 621 | 620 | if( zBoundry==0 ) return; |
| 622 | 621 | while( (zLine = get_line_from_string(&z, &len))!=0 ){ |
| 623 | 622 | if( zLine[0]==0 ){ |
| 624 | 623 | int nContent = 0; |
| 625 | 624 | zValue = get_bounded_content(&z, &len, zBoundry, &nContent); |
| 626 | - if( zName && zValue && islower(zName[0]) ){ | |
| 625 | + if( zName && zValue && fossil_islower(zName[0]) ){ | |
| 627 | 626 | cgi_set_parameter_nocopy(zName, zValue); |
| 628 | 627 | if( showBytes ){ |
| 629 | 628 | cgi_set_parameter_nocopy(mprintf("%s:bytes", zName), |
| 630 | 629 | mprintf("%d",nContent)); |
| 631 | 630 | } |
| @@ -633,25 +632,25 @@ | ||
| 633 | 632 | zName = 0; |
| 634 | 633 | showBytes = 0; |
| 635 | 634 | }else{ |
| 636 | 635 | nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg); |
| 637 | 636 | for(i=0; i<nArg; i++){ |
| 638 | - int c = tolower(azArg[i][0]); | |
| 637 | + int c = fossil_tolower(azArg[i][0]); | |
| 639 | 638 | int n = strlen(azArg[i]); |
| 640 | 639 | if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){ |
| 641 | 640 | i++; |
| 642 | 641 | }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){ |
| 643 | 642 | zName = azArg[++i]; |
| 644 | 643 | }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){ |
| 645 | 644 | char *z = azArg[++i]; |
| 646 | - if( zName && z && islower(zName[0]) ){ | |
| 645 | + if( zName && z && fossil_islower(zName[0]) ){ | |
| 647 | 646 | cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z); |
| 648 | 647 | } |
| 649 | 648 | showBytes = 1; |
| 650 | 649 | }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){ |
| 651 | 650 | char *z = azArg[++i]; |
| 652 | - if( zName && z && islower(zName[0]) ){ | |
| 651 | + if( zName && z && fossil_islower(zName[0]) ){ | |
| 653 | 652 | cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z); |
| 654 | 653 | } |
| 655 | 654 | } |
| 656 | 655 | } |
| 657 | 656 | } |
| @@ -681,12 +680,11 @@ | ||
| 681 | 680 | g.zContentType = zType = P("CONTENT_TYPE"); |
| 682 | 681 | if( len>0 && zType ){ |
| 683 | 682 | blob_zero(&g.cgiIn); |
| 684 | 683 | if( strcmp(zType,"application/x-www-form-urlencoded")==0 |
| 685 | 684 | || strncmp(zType,"multipart/form-data",19)==0 ){ |
| 686 | - z = malloc( len+1 ); | |
| 687 | - if( z==0 ) fossil_exit(1); | |
| 685 | + z = fossil_malloc( len+1 ); | |
| 688 | 686 | len = fread(z, 1, len, g.httpIn); |
| 689 | 687 | z[len] = 0; |
| 690 | 688 | if( zType[0]=='a' ){ |
| 691 | 689 | add_param_list(z, '&'); |
| 692 | 690 | }else{ |
| @@ -773,11 +771,11 @@ | ||
| 773 | 771 | |
| 774 | 772 | /* If no match is found and the name begins with an upper-case |
| 775 | 773 | ** letter, then check to see if there is an environment variable |
| 776 | 774 | ** with the given name. |
| 777 | 775 | */ |
| 778 | - if( isupper(zName[0]) ){ | |
| 776 | + if( fossil_isupper(zName[0]) ){ | |
| 779 | 777 | const char *zValue = getenv(zName); |
| 780 | 778 | if( zValue ){ |
| 781 | 779 | cgi_set_parameter_nocopy(zName, zValue); |
| 782 | 780 | CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); |
| 783 | 781 | return zValue; |
| @@ -916,17 +914,17 @@ | ||
| 916 | 914 | char *zResult = 0; |
| 917 | 915 | if( zInput==0 ){ |
| 918 | 916 | if( zLeftOver ) *zLeftOver = 0; |
| 919 | 917 | return 0; |
| 920 | 918 | } |
| 921 | - while( isspace(*zInput) ){ zInput++; } | |
| 919 | + while( fossil_isspace(*zInput) ){ zInput++; } | |
| 922 | 920 | zResult = zInput; |
| 923 | - while( *zInput && !isspace(*zInput) ){ zInput++; } | |
| 921 | + while( *zInput && !fossil_isspace(*zInput) ){ zInput++; } | |
| 924 | 922 | if( *zInput ){ |
| 925 | 923 | *zInput = 0; |
| 926 | 924 | zInput++; |
| 927 | - while( isspace(*zInput) ){ zInput++; } | |
| 925 | + while( fossil_isspace(*zInput) ){ zInput++; } | |
| 928 | 926 | } |
| 929 | 927 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 930 | 928 | return zResult; |
| 931 | 929 | } |
| 932 | 930 | |
| @@ -941,11 +939,11 @@ | ||
| 941 | 939 | */ |
| 942 | 940 | void cgi_handle_http_request(const char *zIpAddr){ |
| 943 | 941 | char *z, *zToken; |
| 944 | 942 | int i; |
| 945 | 943 | struct sockaddr_in remoteName; |
| 946 | - size_t size = sizeof(struct sockaddr_in); | |
| 944 | + socklen_t size = sizeof(struct sockaddr_in); | |
| 947 | 945 | char zLine[2000]; /* A single line of input. */ |
| 948 | 946 | |
| 949 | 947 | g.fullHttpReply = 1; |
| 950 | 948 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 951 | 949 | malformed_request(); |
| @@ -969,11 +967,11 @@ | ||
| 969 | 967 | if( zToken[i] ) zToken[i++] = 0; |
| 970 | 968 | cgi_setenv("PATH_INFO", zToken); |
| 971 | 969 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 972 | 970 | if( zIpAddr==0 && |
| 973 | 971 | getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 974 | - (socklen_t*)&size)>=0 | |
| 972 | + &size)>=0 | |
| 975 | 973 | ){ |
| 976 | 974 | zIpAddr = inet_ntoa(remoteName.sin_addr); |
| 977 | 975 | } |
| 978 | 976 | if( zIpAddr ){ |
| 979 | 977 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| @@ -986,15 +984,17 @@ | ||
| 986 | 984 | char *zFieldName; |
| 987 | 985 | char *zVal; |
| 988 | 986 | |
| 989 | 987 | zFieldName = extract_token(zLine,&zVal); |
| 990 | 988 | if( zFieldName==0 || *zFieldName==0 ) break; |
| 991 | - while( isspace(*zVal) ){ zVal++; } | |
| 989 | + while( fossil_isspace(*zVal) ){ zVal++; } | |
| 992 | 990 | i = strlen(zVal); |
| 993 | - while( i>0 && isspace(zVal[i-1]) ){ i--; } | |
| 991 | + while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } | |
| 994 | 992 | zVal[i] = 0; |
| 995 | - for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); } | |
| 993 | + for(i=0; zFieldName[i]; i++){ | |
| 994 | + zFieldName[i] = fossil_tolower(zFieldName[i]); | |
| 995 | + } | |
| 996 | 996 | if( strcmp(zFieldName,"content-length:")==0 ){ |
| 997 | 997 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 998 | 998 | }else if( strcmp(zFieldName,"content-type:")==0 ){ |
| 999 | 999 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1000 | 1000 | }else if( strcmp(zFieldName,"cookie:")==0 ){ |
| @@ -1050,11 +1050,11 @@ | ||
| 1050 | 1050 | fossil_exit(1); |
| 1051 | 1051 | #else |
| 1052 | 1052 | int listener = -1; /* The server socket */ |
| 1053 | 1053 | int connection; /* A socket for each individual connection */ |
| 1054 | 1054 | fd_set readfds; /* Set of file descriptors for select() */ |
| 1055 | - size_t lenaddr; /* Length of the inaddr structure */ | |
| 1055 | + socklen_t lenaddr; /* Length of the inaddr structure */ | |
| 1056 | 1056 | int child; /* PID of the child process */ |
| 1057 | 1057 | int nchildren = 0; /* Number of child processes */ |
| 1058 | 1058 | struct timeval delay; /* How long to wait inside select() */ |
| 1059 | 1059 | struct sockaddr_in inaddr; /* The socket address */ |
| 1060 | 1060 | int opt = 1; /* setsockopt flag */ |
| @@ -1113,12 +1113,11 @@ | ||
| 1113 | 1113 | FD_ZERO(&readfds); |
| 1114 | 1114 | FD_SET( listener, &readfds); |
| 1115 | 1115 | select( listener+1, &readfds, 0, 0, &delay); |
| 1116 | 1116 | if( FD_ISSET(listener, &readfds) ){ |
| 1117 | 1117 | lenaddr = sizeof(inaddr); |
| 1118 | - connection = accept(listener, (struct sockaddr*)&inaddr, | |
| 1119 | - (socklen_t*) &lenaddr); | |
| 1118 | + connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); | |
| 1120 | 1119 | if( connection>=0 ){ |
| 1121 | 1120 | child = fork(); |
| 1122 | 1121 | if( child!=0 ){ |
| 1123 | 1122 | if( child>0 ) nchildren++; |
| 1124 | 1123 | close(connection); |
| 1125 | 1124 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -387,12 +387,11 @@ | |
| 387 | ** deallocated after this routine returns. |
| 388 | */ |
| 389 | void cgi_set_parameter_nocopy(const char *zName, const char *zValue){ |
| 390 | if( nAllocQP<=nUsedQP ){ |
| 391 | nAllocQP = nAllocQP*2 + 10; |
| 392 | aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); |
| 393 | if( aParamQP==0 ) fossil_exit(1); |
| 394 | } |
| 395 | aParamQP[nUsedQP].zName = zName; |
| 396 | aParamQP[nUsedQP].zValue = zValue; |
| 397 | if( g.fHttpTrace ){ |
| 398 | fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); |
| @@ -463,11 +462,11 @@ | |
| 463 | */ |
| 464 | static void add_param_list(char *z, int terminator){ |
| 465 | while( *z ){ |
| 466 | char *zName; |
| 467 | char *zValue; |
| 468 | while( isspace(*z) ){ z++; } |
| 469 | zName = z; |
| 470 | while( *z && *z!='=' && *z!=terminator ){ z++; } |
| 471 | if( *z=='=' ){ |
| 472 | *z = 0; |
| 473 | z++; |
| @@ -480,11 +479,11 @@ | |
| 480 | dehttpize(zValue); |
| 481 | }else{ |
| 482 | if( *z ){ *z++ = 0; } |
| 483 | zValue = ""; |
| 484 | } |
| 485 | if( islower(zName[0]) ){ |
| 486 | cgi_set_parameter_nocopy(zName, zValue); |
| 487 | } |
| 488 | } |
| 489 | } |
| 490 | |
| @@ -575,11 +574,11 @@ | |
| 575 | ** in the example above. |
| 576 | */ |
| 577 | static int tokenize_line(char *z, int mxArg, char **azArg){ |
| 578 | int i = 0; |
| 579 | while( *z ){ |
| 580 | while( isspace(*z) || *z==';' ){ z++; } |
| 581 | if( *z=='"' && z[1] ){ |
| 582 | *z = 0; |
| 583 | z++; |
| 584 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 585 | while( *z && *z!='"' ){ z++; } |
| @@ -586,11 +585,11 @@ | |
| 586 | if( *z==0 ) break; |
| 587 | *z = 0; |
| 588 | z++; |
| 589 | }else{ |
| 590 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 591 | while( *z && !isspace(*z) && *z!=';' && *z!='"' ){ z++; } |
| 592 | if( *z && *z!='"' ){ |
| 593 | *z = 0; |
| 594 | z++; |
| 595 | } |
| 596 | } |
| @@ -621,11 +620,11 @@ | |
| 621 | if( zBoundry==0 ) return; |
| 622 | while( (zLine = get_line_from_string(&z, &len))!=0 ){ |
| 623 | if( zLine[0]==0 ){ |
| 624 | int nContent = 0; |
| 625 | zValue = get_bounded_content(&z, &len, zBoundry, &nContent); |
| 626 | if( zName && zValue && islower(zName[0]) ){ |
| 627 | cgi_set_parameter_nocopy(zName, zValue); |
| 628 | if( showBytes ){ |
| 629 | cgi_set_parameter_nocopy(mprintf("%s:bytes", zName), |
| 630 | mprintf("%d",nContent)); |
| 631 | } |
| @@ -633,25 +632,25 @@ | |
| 633 | zName = 0; |
| 634 | showBytes = 0; |
| 635 | }else{ |
| 636 | nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg); |
| 637 | for(i=0; i<nArg; i++){ |
| 638 | int c = tolower(azArg[i][0]); |
| 639 | int n = strlen(azArg[i]); |
| 640 | if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){ |
| 641 | i++; |
| 642 | }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){ |
| 643 | zName = azArg[++i]; |
| 644 | }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){ |
| 645 | char *z = azArg[++i]; |
| 646 | if( zName && z && islower(zName[0]) ){ |
| 647 | cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z); |
| 648 | } |
| 649 | showBytes = 1; |
| 650 | }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){ |
| 651 | char *z = azArg[++i]; |
| 652 | if( zName && z && islower(zName[0]) ){ |
| 653 | cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z); |
| 654 | } |
| 655 | } |
| 656 | } |
| 657 | } |
| @@ -681,12 +680,11 @@ | |
| 681 | g.zContentType = zType = P("CONTENT_TYPE"); |
| 682 | if( len>0 && zType ){ |
| 683 | blob_zero(&g.cgiIn); |
| 684 | if( strcmp(zType,"application/x-www-form-urlencoded")==0 |
| 685 | || strncmp(zType,"multipart/form-data",19)==0 ){ |
| 686 | z = malloc( len+1 ); |
| 687 | if( z==0 ) fossil_exit(1); |
| 688 | len = fread(z, 1, len, g.httpIn); |
| 689 | z[len] = 0; |
| 690 | if( zType[0]=='a' ){ |
| 691 | add_param_list(z, '&'); |
| 692 | }else{ |
| @@ -773,11 +771,11 @@ | |
| 773 | |
| 774 | /* If no match is found and the name begins with an upper-case |
| 775 | ** letter, then check to see if there is an environment variable |
| 776 | ** with the given name. |
| 777 | */ |
| 778 | if( isupper(zName[0]) ){ |
| 779 | const char *zValue = getenv(zName); |
| 780 | if( zValue ){ |
| 781 | cgi_set_parameter_nocopy(zName, zValue); |
| 782 | CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); |
| 783 | return zValue; |
| @@ -916,17 +914,17 @@ | |
| 916 | char *zResult = 0; |
| 917 | if( zInput==0 ){ |
| 918 | if( zLeftOver ) *zLeftOver = 0; |
| 919 | return 0; |
| 920 | } |
| 921 | while( isspace(*zInput) ){ zInput++; } |
| 922 | zResult = zInput; |
| 923 | while( *zInput && !isspace(*zInput) ){ zInput++; } |
| 924 | if( *zInput ){ |
| 925 | *zInput = 0; |
| 926 | zInput++; |
| 927 | while( isspace(*zInput) ){ zInput++; } |
| 928 | } |
| 929 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 930 | return zResult; |
| 931 | } |
| 932 | |
| @@ -941,11 +939,11 @@ | |
| 941 | */ |
| 942 | void cgi_handle_http_request(const char *zIpAddr){ |
| 943 | char *z, *zToken; |
| 944 | int i; |
| 945 | struct sockaddr_in remoteName; |
| 946 | size_t size = sizeof(struct sockaddr_in); |
| 947 | char zLine[2000]; /* A single line of input. */ |
| 948 | |
| 949 | g.fullHttpReply = 1; |
| 950 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 951 | malformed_request(); |
| @@ -969,11 +967,11 @@ | |
| 969 | if( zToken[i] ) zToken[i++] = 0; |
| 970 | cgi_setenv("PATH_INFO", zToken); |
| 971 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 972 | if( zIpAddr==0 && |
| 973 | getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 974 | (socklen_t*)&size)>=0 |
| 975 | ){ |
| 976 | zIpAddr = inet_ntoa(remoteName.sin_addr); |
| 977 | } |
| 978 | if( zIpAddr ){ |
| 979 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| @@ -986,15 +984,17 @@ | |
| 986 | char *zFieldName; |
| 987 | char *zVal; |
| 988 | |
| 989 | zFieldName = extract_token(zLine,&zVal); |
| 990 | if( zFieldName==0 || *zFieldName==0 ) break; |
| 991 | while( isspace(*zVal) ){ zVal++; } |
| 992 | i = strlen(zVal); |
| 993 | while( i>0 && isspace(zVal[i-1]) ){ i--; } |
| 994 | zVal[i] = 0; |
| 995 | for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); } |
| 996 | if( strcmp(zFieldName,"content-length:")==0 ){ |
| 997 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 998 | }else if( strcmp(zFieldName,"content-type:")==0 ){ |
| 999 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1000 | }else if( strcmp(zFieldName,"cookie:")==0 ){ |
| @@ -1050,11 +1050,11 @@ | |
| 1050 | fossil_exit(1); |
| 1051 | #else |
| 1052 | int listener = -1; /* The server socket */ |
| 1053 | int connection; /* A socket for each individual connection */ |
| 1054 | fd_set readfds; /* Set of file descriptors for select() */ |
| 1055 | size_t lenaddr; /* Length of the inaddr structure */ |
| 1056 | int child; /* PID of the child process */ |
| 1057 | int nchildren = 0; /* Number of child processes */ |
| 1058 | struct timeval delay; /* How long to wait inside select() */ |
| 1059 | struct sockaddr_in inaddr; /* The socket address */ |
| 1060 | int opt = 1; /* setsockopt flag */ |
| @@ -1113,12 +1113,11 @@ | |
| 1113 | FD_ZERO(&readfds); |
| 1114 | FD_SET( listener, &readfds); |
| 1115 | select( listener+1, &readfds, 0, 0, &delay); |
| 1116 | if( FD_ISSET(listener, &readfds) ){ |
| 1117 | lenaddr = sizeof(inaddr); |
| 1118 | connection = accept(listener, (struct sockaddr*)&inaddr, |
| 1119 | (socklen_t*) &lenaddr); |
| 1120 | if( connection>=0 ){ |
| 1121 | child = fork(); |
| 1122 | if( child!=0 ){ |
| 1123 | if( child>0 ) nchildren++; |
| 1124 | close(connection); |
| 1125 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -387,12 +387,11 @@ | |
| 387 | ** deallocated after this routine returns. |
| 388 | */ |
| 389 | void cgi_set_parameter_nocopy(const char *zName, const char *zValue){ |
| 390 | if( nAllocQP<=nUsedQP ){ |
| 391 | nAllocQP = nAllocQP*2 + 10; |
| 392 | aParamQP = fossil_realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) ); |
| 393 | } |
| 394 | aParamQP[nUsedQP].zName = zName; |
| 395 | aParamQP[nUsedQP].zValue = zValue; |
| 396 | if( g.fHttpTrace ){ |
| 397 | fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); |
| @@ -463,11 +462,11 @@ | |
| 462 | */ |
| 463 | static void add_param_list(char *z, int terminator){ |
| 464 | while( *z ){ |
| 465 | char *zName; |
| 466 | char *zValue; |
| 467 | while( fossil_isspace(*z) ){ z++; } |
| 468 | zName = z; |
| 469 | while( *z && *z!='=' && *z!=terminator ){ z++; } |
| 470 | if( *z=='=' ){ |
| 471 | *z = 0; |
| 472 | z++; |
| @@ -480,11 +479,11 @@ | |
| 479 | dehttpize(zValue); |
| 480 | }else{ |
| 481 | if( *z ){ *z++ = 0; } |
| 482 | zValue = ""; |
| 483 | } |
| 484 | if( fossil_islower(zName[0]) ){ |
| 485 | cgi_set_parameter_nocopy(zName, zValue); |
| 486 | } |
| 487 | } |
| 488 | } |
| 489 | |
| @@ -575,11 +574,11 @@ | |
| 574 | ** in the example above. |
| 575 | */ |
| 576 | static int tokenize_line(char *z, int mxArg, char **azArg){ |
| 577 | int i = 0; |
| 578 | while( *z ){ |
| 579 | while( fossil_isspace(*z) || *z==';' ){ z++; } |
| 580 | if( *z=='"' && z[1] ){ |
| 581 | *z = 0; |
| 582 | z++; |
| 583 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 584 | while( *z && *z!='"' ){ z++; } |
| @@ -586,11 +585,11 @@ | |
| 585 | if( *z==0 ) break; |
| 586 | *z = 0; |
| 587 | z++; |
| 588 | }else{ |
| 589 | if( i<mxArg-1 ){ azArg[i++] = z; } |
| 590 | while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; } |
| 591 | if( *z && *z!='"' ){ |
| 592 | *z = 0; |
| 593 | z++; |
| 594 | } |
| 595 | } |
| @@ -621,11 +620,11 @@ | |
| 620 | if( zBoundry==0 ) return; |
| 621 | while( (zLine = get_line_from_string(&z, &len))!=0 ){ |
| 622 | if( zLine[0]==0 ){ |
| 623 | int nContent = 0; |
| 624 | zValue = get_bounded_content(&z, &len, zBoundry, &nContent); |
| 625 | if( zName && zValue && fossil_islower(zName[0]) ){ |
| 626 | cgi_set_parameter_nocopy(zName, zValue); |
| 627 | if( showBytes ){ |
| 628 | cgi_set_parameter_nocopy(mprintf("%s:bytes", zName), |
| 629 | mprintf("%d",nContent)); |
| 630 | } |
| @@ -633,25 +632,25 @@ | |
| 632 | zName = 0; |
| 633 | showBytes = 0; |
| 634 | }else{ |
| 635 | nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg); |
| 636 | for(i=0; i<nArg; i++){ |
| 637 | int c = fossil_tolower(azArg[i][0]); |
| 638 | int n = strlen(azArg[i]); |
| 639 | if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){ |
| 640 | i++; |
| 641 | }else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){ |
| 642 | zName = azArg[++i]; |
| 643 | }else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){ |
| 644 | char *z = azArg[++i]; |
| 645 | if( zName && z && fossil_islower(zName[0]) ){ |
| 646 | cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z); |
| 647 | } |
| 648 | showBytes = 1; |
| 649 | }else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){ |
| 650 | char *z = azArg[++i]; |
| 651 | if( zName && z && fossil_islower(zName[0]) ){ |
| 652 | cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z); |
| 653 | } |
| 654 | } |
| 655 | } |
| 656 | } |
| @@ -681,12 +680,11 @@ | |
| 680 | g.zContentType = zType = P("CONTENT_TYPE"); |
| 681 | if( len>0 && zType ){ |
| 682 | blob_zero(&g.cgiIn); |
| 683 | if( strcmp(zType,"application/x-www-form-urlencoded")==0 |
| 684 | || strncmp(zType,"multipart/form-data",19)==0 ){ |
| 685 | z = fossil_malloc( len+1 ); |
| 686 | len = fread(z, 1, len, g.httpIn); |
| 687 | z[len] = 0; |
| 688 | if( zType[0]=='a' ){ |
| 689 | add_param_list(z, '&'); |
| 690 | }else{ |
| @@ -773,11 +771,11 @@ | |
| 771 | |
| 772 | /* If no match is found and the name begins with an upper-case |
| 773 | ** letter, then check to see if there is an environment variable |
| 774 | ** with the given name. |
| 775 | */ |
| 776 | if( fossil_isupper(zName[0]) ){ |
| 777 | const char *zValue = getenv(zName); |
| 778 | if( zValue ){ |
| 779 | cgi_set_parameter_nocopy(zName, zValue); |
| 780 | CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); |
| 781 | return zValue; |
| @@ -916,17 +914,17 @@ | |
| 914 | char *zResult = 0; |
| 915 | if( zInput==0 ){ |
| 916 | if( zLeftOver ) *zLeftOver = 0; |
| 917 | return 0; |
| 918 | } |
| 919 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 920 | zResult = zInput; |
| 921 | while( *zInput && !fossil_isspace(*zInput) ){ zInput++; } |
| 922 | if( *zInput ){ |
| 923 | *zInput = 0; |
| 924 | zInput++; |
| 925 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 926 | } |
| 927 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 928 | return zResult; |
| 929 | } |
| 930 | |
| @@ -941,11 +939,11 @@ | |
| 939 | */ |
| 940 | void cgi_handle_http_request(const char *zIpAddr){ |
| 941 | char *z, *zToken; |
| 942 | int i; |
| 943 | struct sockaddr_in remoteName; |
| 944 | socklen_t size = sizeof(struct sockaddr_in); |
| 945 | char zLine[2000]; /* A single line of input. */ |
| 946 | |
| 947 | g.fullHttpReply = 1; |
| 948 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 949 | malformed_request(); |
| @@ -969,11 +967,11 @@ | |
| 967 | if( zToken[i] ) zToken[i++] = 0; |
| 968 | cgi_setenv("PATH_INFO", zToken); |
| 969 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 970 | if( zIpAddr==0 && |
| 971 | getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 972 | &size)>=0 |
| 973 | ){ |
| 974 | zIpAddr = inet_ntoa(remoteName.sin_addr); |
| 975 | } |
| 976 | if( zIpAddr ){ |
| 977 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| @@ -986,15 +984,17 @@ | |
| 984 | char *zFieldName; |
| 985 | char *zVal; |
| 986 | |
| 987 | zFieldName = extract_token(zLine,&zVal); |
| 988 | if( zFieldName==0 || *zFieldName==0 ) break; |
| 989 | while( fossil_isspace(*zVal) ){ zVal++; } |
| 990 | i = strlen(zVal); |
| 991 | while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } |
| 992 | zVal[i] = 0; |
| 993 | for(i=0; zFieldName[i]; i++){ |
| 994 | zFieldName[i] = fossil_tolower(zFieldName[i]); |
| 995 | } |
| 996 | if( strcmp(zFieldName,"content-length:")==0 ){ |
| 997 | cgi_setenv("CONTENT_LENGTH", zVal); |
| 998 | }else if( strcmp(zFieldName,"content-type:")==0 ){ |
| 999 | cgi_setenv("CONTENT_TYPE", zVal); |
| 1000 | }else if( strcmp(zFieldName,"cookie:")==0 ){ |
| @@ -1050,11 +1050,11 @@ | |
| 1050 | fossil_exit(1); |
| 1051 | #else |
| 1052 | int listener = -1; /* The server socket */ |
| 1053 | int connection; /* A socket for each individual connection */ |
| 1054 | fd_set readfds; /* Set of file descriptors for select() */ |
| 1055 | socklen_t lenaddr; /* Length of the inaddr structure */ |
| 1056 | int child; /* PID of the child process */ |
| 1057 | int nchildren = 0; /* Number of child processes */ |
| 1058 | struct timeval delay; /* How long to wait inside select() */ |
| 1059 | struct sockaddr_in inaddr; /* The socket address */ |
| 1060 | int opt = 1; /* setsockopt flag */ |
| @@ -1113,12 +1113,11 @@ | |
| 1113 | FD_ZERO(&readfds); |
| 1114 | FD_SET( listener, &readfds); |
| 1115 | select( listener+1, &readfds, 0, 0, &delay); |
| 1116 | if( FD_ISSET(listener, &readfds) ){ |
| 1117 | lenaddr = sizeof(inaddr); |
| 1118 | connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); |
| 1119 | if( connection>=0 ){ |
| 1120 | child = fork(); |
| 1121 | if( child!=0 ){ |
| 1122 | if( child>0 ) nchildren++; |
| 1123 | close(connection); |
| 1124 |
+304
-160
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -215,21 +215,21 @@ | ||
| 215 | 215 | int cTerm; |
| 216 | 216 | |
| 217 | 217 | if( zGlobList==0 || zGlobList[0]==0 ) return "0"; |
| 218 | 218 | blob_zero(&expr); |
| 219 | 219 | while( zGlobList[0] ){ |
| 220 | - while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; | |
| 220 | + while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; | |
| 221 | 221 | if( zGlobList[0]==0 ) break; |
| 222 | 222 | if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ |
| 223 | 223 | cTerm = zGlobList[0]; |
| 224 | 224 | zGlobList++; |
| 225 | 225 | }else{ |
| 226 | 226 | cTerm = ','; |
| 227 | 227 | } |
| 228 | 228 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){} |
| 229 | 229 | if( cTerm==',' ){ |
| 230 | - while( i>0 && isspace(zGlobList[i-1]) ){ i--; } | |
| 230 | + while( i>0 && fossil_isspace(zGlobList[i-1]) ){ i--; } | |
| 231 | 231 | } |
| 232 | 232 | blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList); |
| 233 | 233 | zSep = " OR "; |
| 234 | 234 | if( cTerm!=',' && zGlobList[i] ) i++; |
| 235 | 235 | zGlobList += i; |
| @@ -259,10 +259,11 @@ | ||
| 259 | 259 | Blob repo; |
| 260 | 260 | Stmt q; |
| 261 | 261 | int n; |
| 262 | 262 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 263 | 263 | int allFlag = find_option("dotfiles",0,0)!=0; |
| 264 | + int outputManifest = db_get_boolean("manifest",0); | |
| 264 | 265 | |
| 265 | 266 | db_must_be_within_tree(); |
| 266 | 267 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); |
| 267 | 268 | n = strlen(g.zLocalRoot); |
| 268 | 269 | blob_init(&path, g.zLocalRoot, n-1); |
| @@ -270,16 +271,18 @@ | ||
| 270 | 271 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 271 | 272 | } |
| 272 | 273 | vfile_scan(0, &path, blob_size(&path), allFlag); |
| 273 | 274 | db_prepare(&q, |
| 274 | 275 | "SELECT x FROM sfile" |
| 275 | - " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_'," | |
| 276 | + " WHERE x NOT IN ('%s','%s','_FOSSIL_'," | |
| 276 | 277 | "'_FOSSIL_-journal','.fos','.fos-journal'," |
| 277 | 278 | "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal'," |
| 278 | 279 | "'.fos-shm')" |
| 279 | 280 | " AND NOT %s" |
| 280 | 281 | " ORDER BY 1", |
| 282 | + outputManifest ? "manifest" : "_FOSSIL_", | |
| 283 | + outputManifest ? "manifest.uuid" : "_FOSSIL_", | |
| 281 | 284 | glob_expr("x", zIgnoreFlag) |
| 282 | 285 | ); |
| 283 | 286 | if( file_tree_name(g.zRepositoryName, &repo, 0) ){ |
| 284 | 287 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 285 | 288 | } |
| @@ -415,11 +418,11 @@ | ||
| 415 | 418 | blob_add_cr(&text); |
| 416 | 419 | #endif |
| 417 | 420 | blob_write_to_file(&text, zFile); |
| 418 | 421 | zCmd = mprintf("%s \"%s\"", zEditor, zFile); |
| 419 | 422 | printf("%s\n", zCmd); |
| 420 | - if( portable_system(zCmd) ){ | |
| 423 | + if( fossil_system(zCmd) ){ | |
| 421 | 424 | fossil_panic("editor aborted"); |
| 422 | 425 | } |
| 423 | 426 | blob_reset(&text); |
| 424 | 427 | blob_read_from_file(&text, zFile); |
| 425 | 428 | blob_remove_cr(&text); |
| @@ -429,20 +432,20 @@ | ||
| 429 | 432 | while( blob_line(&text, &line) ){ |
| 430 | 433 | int i, n; |
| 431 | 434 | char *z; |
| 432 | 435 | n = blob_size(&line); |
| 433 | 436 | z = blob_buffer(&line); |
| 434 | - for(i=0; i<n && isspace(z[i]); i++){} | |
| 437 | + for(i=0; i<n && fossil_isspace(z[i]); i++){} | |
| 435 | 438 | if( i<n && z[i]=='#' ) continue; |
| 436 | 439 | if( i<n || blob_size(pComment)>0 ){ |
| 437 | 440 | blob_appendf(pComment, "%b", &line); |
| 438 | 441 | } |
| 439 | 442 | } |
| 440 | 443 | blob_reset(&text); |
| 441 | 444 | zComment = blob_str(pComment); |
| 442 | 445 | i = strlen(zComment); |
| 443 | - while( i>0 && isspace(zComment[i-1]) ){ i--; } | |
| 446 | + while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; } | |
| 444 | 447 | blob_resize(pComment, i); |
| 445 | 448 | } |
| 446 | 449 | |
| 447 | 450 | /* |
| 448 | 451 | ** Populate the Global.aCommitFile[] based on the command line arguments |
| @@ -461,11 +464,11 @@ | ||
| 461 | 464 | void select_commit_files(void){ |
| 462 | 465 | if( g.argc>2 ){ |
| 463 | 466 | int ii; |
| 464 | 467 | Blob b; |
| 465 | 468 | blob_zero(&b); |
| 466 | - g.aCommitFile = malloc(sizeof(int)*(g.argc-1)); | |
| 469 | + g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); | |
| 467 | 470 | |
| 468 | 471 | for(ii=2; ii<g.argc; ii++){ |
| 469 | 472 | int iId; |
| 470 | 473 | file_tree_name(g.argv[ii], &b, 1); |
| 471 | 474 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| @@ -513,11 +516,11 @@ | ||
| 513 | 516 | " WHERE datetime(mtime)>=%Q" |
| 514 | 517 | " AND type='ci' AND objid=%d", |
| 515 | 518 | zDate, rid |
| 516 | 519 | ); |
| 517 | 520 | if( b ){ |
| 518 | - fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)" | |
| 521 | + fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)" | |
| 519 | 522 | " Use -f to override.", zUuid, zDate); |
| 520 | 523 | } |
| 521 | 524 | #endif |
| 522 | 525 | } |
| 523 | 526 | |
| @@ -537,41 +540,177 @@ | ||
| 537 | 540 | zDate[10] = 'T'; |
| 538 | 541 | return zDate; |
| 539 | 542 | } |
| 540 | 543 | |
| 541 | 544 | /* |
| 542 | -** Return TRUE (non-zero) if a file named "zFilename" exists in | |
| 543 | -** the checkout identified by vid. | |
| 544 | -** | |
| 545 | -** The original purpose of this routine was to check for the presence of | |
| 546 | -** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid | |
| 547 | -** overwriting that file with automatically generated files. | |
| 545 | +** Create a manifest. | |
| 548 | 546 | */ |
| 549 | -int file_exists_in_checkout(int vid, const char *zFilename){ | |
| 550 | - return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q", | |
| 551 | - vid, zFilename); | |
| 547 | +static void create_manifest( | |
| 548 | + Blob *pOut, /* Write the manifest here */ | |
| 549 | + const char *zBaselineUuid, /* UUID of baseline, or zero */ | |
| 550 | + Manifest *pBaseline, /* Make it a delta manifest if not zero */ | |
| 551 | + Blob *pComment, /* Check-in comment text */ | |
| 552 | + int vid, /* blob-id of the parent manifest */ | |
| 553 | + int verifyDate, /* Verify that child is younger */ | |
| 554 | + Blob *pCksum, /* Repository checksum. May be 0 */ | |
| 555 | + const char *zDateOvrd, /* Date override. If 0 then use 'now' */ | |
| 556 | + const char *zUserOvrd, /* User override. If 0 then use g.zLogin */ | |
| 557 | + const char *zBranch, /* Branch name. May be 0 */ | |
| 558 | + const char *zBgColor, /* Background color. May be 0 */ | |
| 559 | + int *pnFBcard /* Number of generated B- and F-cards */ | |
| 560 | +){ | |
| 561 | + char *zDate; /* Date of the check-in */ | |
| 562 | + char *zParentUuid; /* UUID of parent check-in */ | |
| 563 | + Blob filename; /* A single filename */ | |
| 564 | + int nBasename; /* Size of base filename */ | |
| 565 | + Stmt q; /* Query of files changed */ | |
| 566 | + Stmt q2; /* Query of merge parents */ | |
| 567 | + Blob mcksum; /* Manifest checksum */ | |
| 568 | + ManifestFile *pFile; /* File from the baseline */ | |
| 569 | + int nFBcard = 0; /* Number of B-cards and F-cards */ | |
| 570 | + | |
| 571 | + assert( pBaseline==0 || pBaseline->zBaseline==0 ); | |
| 572 | + assert( pBaseline==0 || zBaselineUuid!=0 ); | |
| 573 | + blob_zero(pOut); | |
| 574 | + zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 575 | + if( pBaseline ){ | |
| 576 | + blob_appendf(pOut, "B %s\n", zBaselineUuid); | |
| 577 | + manifest_file_rewind(pBaseline); | |
| 578 | + pFile = manifest_file_next(pBaseline, 0); | |
| 579 | + nFBcard++; | |
| 580 | + }else{ | |
| 581 | + pFile = 0; | |
| 582 | + } | |
| 583 | + blob_appendf(pOut, "C %F\n", blob_str(pComment)); | |
| 584 | + zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); | |
| 585 | + blob_appendf(pOut, "D %s\n", zDate); | |
| 586 | + zDate[10] = ' '; | |
| 587 | + db_prepare(&q, | |
| 588 | + "SELECT pathname, uuid, origname, blob.rid, isexe" | |
| 589 | + " FROM vfile JOIN blob ON vfile.mrid=blob.rid" | |
| 590 | + " WHERE NOT deleted AND vfile.vid=%d" | |
| 591 | + " ORDER BY 1", vid); | |
| 592 | + blob_zero(&filename); | |
| 593 | + blob_appendf(&filename, "%s", g.zLocalRoot); | |
| 594 | + nBasename = blob_size(&filename); | |
| 595 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 596 | + const char *zName = db_column_text(&q, 0); | |
| 597 | + const char *zUuid = db_column_text(&q, 1); | |
| 598 | + const char *zOrig = db_column_text(&q, 2); | |
| 599 | + int frid = db_column_int(&q, 3); | |
| 600 | + int isexe = db_column_int(&q, 4); | |
| 601 | + const char *zPerm; | |
| 602 | + int cmp; | |
| 603 | + blob_append(&filename, zName, -1); | |
| 604 | +#if !defined(_WIN32) | |
| 605 | + /* For unix, extract the "executable" permission bit directly from | |
| 606 | + ** the filesystem. On windows, the "executable" bit is retained | |
| 607 | + ** unchanged from the original. */ | |
| 608 | + isexe = file_isexe(blob_str(&filename)); | |
| 609 | +#endif | |
| 610 | + if( isexe ){ | |
| 611 | + zPerm = " x"; | |
| 612 | + }else{ | |
| 613 | + zPerm = ""; | |
| 614 | + } | |
| 615 | + if( !g.markPrivate ) content_make_public(frid); | |
| 616 | + while( pFile && strcmp(pFile->zName,zName)<0 ){ | |
| 617 | + blob_appendf(pOut, "F %F\n", pFile->zName); | |
| 618 | + pFile = manifest_file_next(pBaseline, 0); | |
| 619 | + nFBcard++; | |
| 620 | + } | |
| 621 | + cmp = 1; | |
| 622 | + if( pFile==0 | |
| 623 | + || (cmp = strcmp(pFile->zName,zName))!=0 | |
| 624 | + || strcmp(pFile->zUuid, zUuid)!=0 | |
| 625 | + ){ | |
| 626 | + blob_resize(&filename, nBasename); | |
| 627 | + if( zOrig==0 || strcmp(zOrig,zName)==0 ){ | |
| 628 | + blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm); | |
| 629 | + }else{ | |
| 630 | + if( zPerm[0]==0 ){ zPerm = " w"; } | |
| 631 | + blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); | |
| 632 | + } | |
| 633 | + nFBcard++; | |
| 634 | + } | |
| 635 | + if( cmp==0 ) pFile = manifest_file_next(pBaseline,0); | |
| 636 | + } | |
| 637 | + blob_reset(&filename); | |
| 638 | + db_finalize(&q); | |
| 639 | + while( pFile ){ | |
| 640 | + blob_appendf(pOut, "F %F\n", pFile->zName); | |
| 641 | + pFile = manifest_file_next(pBaseline, 0); | |
| 642 | + nFBcard++; | |
| 643 | + } | |
| 644 | + blob_appendf(pOut, "P %s", zParentUuid); | |
| 645 | + if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate); | |
| 646 | + free(zParentUuid); | |
| 647 | + db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); | |
| 648 | + db_bind_int(&q2, ":id", 0); | |
| 649 | + while( db_step(&q2)==SQLITE_ROW ){ | |
| 650 | + char *zMergeUuid; | |
| 651 | + int mid = db_column_int(&q2, 0); | |
| 652 | + if( !g.markPrivate && content_is_private(mid) ) continue; | |
| 653 | + zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); | |
| 654 | + if( zMergeUuid ){ | |
| 655 | + blob_appendf(pOut, " %s", zMergeUuid); | |
| 656 | + if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate); | |
| 657 | + free(zMergeUuid); | |
| 658 | + } | |
| 659 | + } | |
| 660 | + db_finalize(&q2); | |
| 661 | + free(zDate); | |
| 662 | + | |
| 663 | + blob_appendf(pOut, "\n"); | |
| 664 | + if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum); | |
| 665 | + if( zBranch && zBranch[0] ){ | |
| 666 | + Stmt q; | |
| 667 | + if( zBgColor && zBgColor[0] ){ | |
| 668 | + blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor); | |
| 669 | + } | |
| 670 | + blob_appendf(pOut, "T *branch * %F\n", zBranch); | |
| 671 | + blob_appendf(pOut, "T *sym-%F *\n", zBranch); | |
| 672 | + | |
| 673 | + /* Cancel all other symbolic tags */ | |
| 674 | + db_prepare(&q, | |
| 675 | + "SELECT tagname FROM tagxref, tag" | |
| 676 | + " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" | |
| 677 | + " AND tagtype>0 AND tagname GLOB 'sym-*'" | |
| 678 | + " AND tagname!='sym-'||%Q" | |
| 679 | + " ORDER BY tagname", | |
| 680 | + vid, zBranch); | |
| 681 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 682 | + const char *zTag = db_column_text(&q, 0); | |
| 683 | + blob_appendf(pOut, "T -%F *\n", zTag); | |
| 684 | + } | |
| 685 | + db_finalize(&q); | |
| 686 | + } | |
| 687 | + blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); | |
| 688 | + md5sum_blob(pOut, &mcksum); | |
| 689 | + blob_appendf(pOut, "Z %b\n", &mcksum); | |
| 690 | + if( pnFBcard ) *pnFBcard = nFBcard; | |
| 552 | 691 | } |
| 692 | + | |
| 553 | 693 | |
| 554 | 694 | /* |
| 555 | 695 | ** COMMAND: ci |
| 556 | 696 | ** COMMAND: commit |
| 557 | 697 | ** |
| 558 | 698 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 559 | 699 | ** |
| 560 | 700 | ** Create a new version containing all of the changes in the current |
| 561 | 701 | ** checkout. You will be prompted to enter a check-in comment unless |
| 562 | -** the comment has been specified on the command-line using "-m". | |
| 563 | -** The editor defined in the "editor" fossil option (see %fossil help set) | |
| 564 | -** will be used, or from the "VISUAL" or "EDITOR" environment variables | |
| 565 | -** (in that order) if no editor is set. | |
| 566 | -** | |
| 567 | -** You will be prompted for your GPG passphrase in order to sign the | |
| 568 | -** new manifest unless the "--nosign" option is used. All files that | |
| 569 | -** have changed will be committed unless some subset of files is | |
| 570 | -** specified on the command line. | |
| 571 | -** | |
| 572 | -** The --branch option followed by a branch name cases the new check-in | |
| 702 | +** the comment has been specified on the command-line using "-m" or a | |
| 703 | +** file containing the comment using -M. The editor defined in the | |
| 704 | +** "editor" fossil option (see %fossil help set) will be used, or from | |
| 705 | +** the "VISUAL" or "EDITOR" environment variables (in that order) if | |
| 706 | +** no editor is set. | |
| 707 | +** | |
| 708 | +** All files that have changed will be committed unless some subset of | |
| 709 | +** files is specified on the command line. | |
| 710 | +** | |
| 711 | +** The --branch option followed by a branch name causes the new check-in | |
| 573 | 712 | ** to be placed in the named branch. The --bgcolor option can be followed |
| 574 | 713 | ** by a color name (ex: '#ffc0c0') to specify the background color of |
| 575 | 714 | ** entries in the new branch when shown in the web timeline interface. |
| 576 | 715 | ** |
| 577 | 716 | ** A check-in is not permitted to fork unless the --force or -f |
| @@ -581,44 +720,58 @@ | ||
| 581 | 720 | ** Children of private check-ins are automatically private. |
| 582 | 721 | ** |
| 583 | 722 | ** Options: |
| 584 | 723 | ** |
| 585 | 724 | ** --comment|-m COMMENT-TEXT |
| 725 | +** --message-file|-M COMMENT-FILE | |
| 586 | 726 | ** --branch NEW-BRANCH-NAME |
| 587 | 727 | ** --bgcolor COLOR |
| 588 | 728 | ** --nosign |
| 589 | 729 | ** --force|-f |
| 590 | 730 | ** --private |
| 731 | +** --baseline | |
| 732 | +** --delta | |
| 591 | 733 | ** |
| 592 | 734 | */ |
| 593 | 735 | void commit_cmd(void){ |
| 594 | - int rc; | |
| 595 | - int vid, nrid, nvid; | |
| 596 | - Blob comment; | |
| 597 | - const char *zComment; | |
| 598 | - Stmt q; | |
| 599 | - Stmt q2; | |
| 600 | - char *zUuid, *zDate; | |
| 736 | + int hasChanges; /* True if unsaved changes exist */ | |
| 737 | + int vid; /* blob-id of parent version */ | |
| 738 | + int nrid; /* blob-id of a modified file */ | |
| 739 | + int nvid; /* Blob-id of the new check-in */ | |
| 740 | + Blob comment; /* Check-in comment */ | |
| 741 | + const char *zComment; /* Check-in comment */ | |
| 742 | + Stmt q; /* Query to find files that have been modified */ | |
| 743 | + char *zUuid; /* UUID of the new check-in */ | |
| 601 | 744 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 602 | 745 | int isAMerge = 0; /* True if checking in a merge */ |
| 603 | 746 | int forceFlag = 0; /* Force a fork */ |
| 747 | + int forceDelta = 0; /* Force a delta-manifest */ | |
| 748 | + int forceBaseline = 0; /* Force a baseline-manifest */ | |
| 604 | 749 | char *zManifestFile; /* Name of the manifest file */ |
| 605 | - int nBasename; /* Length of "g.zLocalRoot/" */ | |
| 750 | + int useCksum; /* True if checksums should be computed and verified */ | |
| 751 | + int outputManifest; /* True to output "manifest" and "manifest.uuid" */ | |
| 752 | + int testRun; /* True for a test run. Debugging only */ | |
| 606 | 753 | const char *zBranch; /* Create a new branch with this name */ |
| 607 | 754 | const char *zBgColor; /* Set background color when branching */ |
| 608 | 755 | const char *zDateOvrd; /* Override date string */ |
| 609 | 756 | const char *zUserOvrd; /* Override user name */ |
| 610 | 757 | const char *zComFile; /* Read commit message from this file */ |
| 611 | - Blob filename; /* complete filename */ | |
| 612 | - Blob manifest; | |
| 758 | + Blob manifest; /* Manifest in baseline form */ | |
| 613 | 759 | Blob muuid; /* Manifest uuid */ |
| 614 | - Blob mcksum; /* Self-checksum on the manifest */ | |
| 615 | 760 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 616 | 761 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 762 | + int szD; /* Size of the delta manifest */ | |
| 763 | + int szB; /* Size of the baseline manifest */ | |
| 617 | 764 | |
| 618 | 765 | url_proxy_options(); |
| 619 | 766 | noSign = find_option("nosign",0,0)!=0; |
| 767 | + forceDelta = find_option("delta",0,0)!=0; | |
| 768 | + forceBaseline = find_option("baseline",0,0)!=0; | |
| 769 | + if( forceDelta && forceBaseline ){ | |
| 770 | + fossil_fatal("cannot use --delta and --baseline together"); | |
| 771 | + } | |
| 772 | + testRun = find_option("test",0,0)!=0; | |
| 620 | 773 | zComment = find_option("comment","m",1); |
| 621 | 774 | forceFlag = find_option("force", "f", 0)!=0; |
| 622 | 775 | zBranch = find_option("branch","b",1); |
| 623 | 776 | zBgColor = find_option("bgcolor",0,1); |
| 624 | 777 | zComFile = find_option("message-file", "M", 1); |
| @@ -630,11 +783,23 @@ | ||
| 630 | 783 | zDateOvrd = find_option("date-override",0,1); |
| 631 | 784 | zUserOvrd = find_option("user-override",0,1); |
| 632 | 785 | db_must_be_within_tree(); |
| 633 | 786 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 634 | 787 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 788 | + useCksum = db_get_boolean("repo-cksum", 1); | |
| 789 | + outputManifest = db_get_boolean("manifest", 0); | |
| 635 | 790 | verify_all_options(); |
| 791 | + | |
| 792 | + /* So that older versions of Fossil (that do not understand delta- | |
| 793 | + ** manifest) can continue to use this repository, do not create a new | |
| 794 | + ** delta-manifest unless this repository already contains one or more | |
| 795 | + ** delta-manifets, or unless the delta-manifest is explicitly requested | |
| 796 | + ** by the --delta option. | |
| 797 | + */ | |
| 798 | + if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ | |
| 799 | + forceBaseline = 1; | |
| 800 | + } | |
| 636 | 801 | |
| 637 | 802 | /* Get the ID of the parent manifest artifact */ |
| 638 | 803 | vid = db_lget_int("checkout", 0); |
| 639 | 804 | if( content_is_private(vid) ){ |
| 640 | 805 | g.markPrivate = 1; |
| @@ -681,15 +846,15 @@ | ||
| 681 | 846 | */ |
| 682 | 847 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 683 | 848 | fossil_fatal("no such user: %s", g.zLogin); |
| 684 | 849 | } |
| 685 | 850 | |
| 851 | + hasChanges = unsaved_changes(); | |
| 686 | 852 | db_begin_transaction(); |
| 687 | 853 | db_record_repository_filename(0); |
| 688 | - rc = unsaved_changes(); | |
| 689 | - if( rc==0 && !isAMerge && !forceFlag ){ | |
| 690 | - fossil_panic("nothing has changed"); | |
| 854 | + if( hasChanges==0 && !isAMerge && !forceFlag ){ | |
| 855 | + fossil_fatal("nothing has changed"); | |
| 691 | 856 | } |
| 692 | 857 | |
| 693 | 858 | /* If one or more files that were named on the command line have not |
| 694 | 859 | ** been modified, bail out now. |
| 695 | 860 | */ |
| @@ -720,11 +885,11 @@ | ||
| 720 | 885 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 721 | 886 | TAG_CLOSED, vid) ){ |
| 722 | 887 | fossil_fatal("cannot commit against a closed leaf"); |
| 723 | 888 | } |
| 724 | 889 | |
| 725 | - vfile_aggregate_checksum_disk(vid, &cksum1); | |
| 890 | + if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1); | |
| 726 | 891 | if( zComment ){ |
| 727 | 892 | blob_zero(&comment); |
| 728 | 893 | blob_append(&comment, zComment, -1); |
| 729 | 894 | }else if( zComFile ){ |
| 730 | 895 | blob_zero(&comment); |
| @@ -775,113 +940,86 @@ | ||
| 775 | 940 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 776 | 941 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 777 | 942 | } |
| 778 | 943 | db_finalize(&q); |
| 779 | 944 | |
| 780 | - /* Create the manifest */ | |
| 781 | - blob_zero(&manifest); | |
| 945 | + /* Create the new manifest */ | |
| 782 | 946 | if( blob_size(&comment)==0 ){ |
| 783 | 947 | blob_append(&comment, "(no comment)", -1); |
| 784 | 948 | } |
| 785 | - blob_appendf(&manifest, "C %F\n", blob_str(&comment)); | |
| 786 | - zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); | |
| 787 | - blob_appendf(&manifest, "D %s\n", zDate); | |
| 788 | - zDate[10] = ' '; | |
| 789 | - db_prepare(&q, | |
| 790 | - "SELECT pathname, uuid, origname, blob.rid, isexe" | |
| 791 | - " FROM vfile JOIN blob ON vfile.mrid=blob.rid" | |
| 792 | - " WHERE NOT deleted AND vfile.vid=%d" | |
| 793 | - " ORDER BY 1", vid); | |
| 794 | - blob_zero(&filename); | |
| 795 | - blob_appendf(&filename, "%s", g.zLocalRoot); | |
| 796 | - nBasename = blob_size(&filename); | |
| 797 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 798 | - const char *zName = db_column_text(&q, 0); | |
| 799 | - const char *zUuid = db_column_text(&q, 1); | |
| 800 | - const char *zOrig = db_column_text(&q, 2); | |
| 801 | - int frid = db_column_int(&q, 3); | |
| 802 | - int isexe = db_column_int(&q, 4); | |
| 803 | - const char *zPerm; | |
| 804 | - blob_append(&filename, zName, -1); | |
| 805 | -#if !defined(_WIN32) | |
| 806 | - /* For unix, extract the "executable" permission bit directly from | |
| 807 | - ** the filesystem. On windows, the "executable" bit is retained | |
| 808 | - ** unchanged from the original. */ | |
| 809 | - isexe = file_isexe(blob_str(&filename)); | |
| 810 | -#endif | |
| 811 | - if( isexe ){ | |
| 812 | - zPerm = " x"; | |
| 813 | - }else{ | |
| 814 | - zPerm = ""; | |
| 815 | - } | |
| 816 | - blob_resize(&filename, nBasename); | |
| 817 | - if( zOrig==0 || strcmp(zOrig,zName)==0 ){ | |
| 818 | - blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm); | |
| 819 | - }else{ | |
| 820 | - if( zPerm[0]==0 ){ zPerm = " w"; } | |
| 821 | - blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); | |
| 822 | - } | |
| 823 | - if( !g.markPrivate ) content_make_public(frid); | |
| 824 | - } | |
| 825 | - blob_reset(&filename); | |
| 826 | - db_finalize(&q); | |
| 827 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 828 | - blob_appendf(&manifest, "P %s", zUuid); | |
| 829 | - | |
| 830 | - if( !forceFlag ){ | |
| 831 | - checkin_verify_younger(vid, zUuid, zDate); | |
| 832 | - db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); | |
| 833 | - db_bind_int(&q2, ":id", 0); | |
| 834 | - while( db_step(&q2)==SQLITE_ROW ){ | |
| 835 | - int mid = db_column_int(&q2, 0); | |
| 836 | - if( !g.markPrivate && content_is_private(mid) ) continue; | |
| 837 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); | |
| 838 | - if( zUuid ){ | |
| 839 | - blob_appendf(&manifest, " %s", zUuid); | |
| 840 | - checkin_verify_younger(mid, zUuid, zDate); | |
| 841 | - free(zUuid); | |
| 842 | - } | |
| 843 | - } | |
| 844 | - db_finalize(&q2); | |
| 845 | - } | |
| 846 | - | |
| 847 | - blob_appendf(&manifest, "\n"); | |
| 848 | - blob_appendf(&manifest, "R %b\n", &cksum1); | |
| 849 | - if( zBranch && zBranch[0] ){ | |
| 850 | - Stmt q; | |
| 851 | - if( zBgColor && zBgColor[0] ){ | |
| 852 | - blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor); | |
| 853 | - } | |
| 854 | - blob_appendf(&manifest, "T *branch * %F\n", zBranch); | |
| 855 | - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); | |
| 856 | - | |
| 857 | - /* Cancel all other symbolic tags */ | |
| 858 | - db_prepare(&q, | |
| 859 | - "SELECT tagname FROM tagxref, tag" | |
| 860 | - " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" | |
| 861 | - " AND tagtype>0 AND tagname GLOB 'sym-*'" | |
| 862 | - " AND tagname!='sym-'||%Q" | |
| 863 | - " ORDER BY tagname", | |
| 864 | - vid, zBranch); | |
| 865 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 866 | - const char *zTag = db_column_text(&q, 0); | |
| 867 | - blob_appendf(&manifest, "T -%F *\n", zTag); | |
| 868 | - } | |
| 869 | - db_finalize(&q); | |
| 870 | - } | |
| 871 | - blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); | |
| 872 | - md5sum_blob(&manifest, &mcksum); | |
| 873 | - blob_appendf(&manifest, "Z %b\n", &mcksum); | |
| 949 | + if( forceDelta ){ | |
| 950 | + blob_zero(&manifest); | |
| 951 | + }else{ | |
| 952 | + create_manifest(&manifest, 0, 0, &comment, vid, | |
| 953 | + !forceFlag, useCksum ? &cksum1 : 0, | |
| 954 | + zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB); | |
| 955 | + } | |
| 956 | + | |
| 957 | + /* See if a delta-manifest would be more appropriate */ | |
| 958 | + if( !forceBaseline ){ | |
| 959 | + const char *zBaselineUuid; | |
| 960 | + Manifest *pParent; | |
| 961 | + Manifest *pBaseline; | |
| 962 | + pParent = manifest_get(vid, CFTYPE_MANIFEST); | |
| 963 | + if( pParent && pParent->zBaseline ){ | |
| 964 | + zBaselineUuid = pParent->zBaseline; | |
| 965 | + pBaseline = manifest_get_by_name(zBaselineUuid, 0); | |
| 966 | + }else{ | |
| 967 | + zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 968 | + pBaseline = pParent; | |
| 969 | + } | |
| 970 | + if( pBaseline ){ | |
| 971 | + Blob delta; | |
| 972 | + create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid, | |
| 973 | + !forceFlag, useCksum ? &cksum1 : 0, | |
| 974 | + zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD); | |
| 975 | + /* | |
| 976 | + ** At this point, two manifests have been constructed, either of | |
| 977 | + ** which would work for this checkin. The first manifest (held | |
| 978 | + ** in the "manifest" variable) is a baseline manifest and the second | |
| 979 | + ** (held in variable named "delta") is a delta manifest. The | |
| 980 | + ** question now is: which manifest should we use? | |
| 981 | + ** | |
| 982 | + ** Let B be the number of F-cards in the baseline manifest and | |
| 983 | + ** let D be the number of F-cards in the delta manifest, plus one for | |
| 984 | + ** the B-card. (B is held in the szB variable and D is held in the | |
| 985 | + ** szD variable.) Assume that all delta manifests adds X new F-cards. | |
| 986 | + ** Then to minimize the total number of F- and B-cards in the repository, | |
| 987 | + ** we should use the delta manifest if and only if: | |
| 988 | + ** | |
| 989 | + ** D*D < B*X - X*X | |
| 990 | + ** | |
| 991 | + ** X is an unknown here, but for most repositories, we will not be | |
| 992 | + ** far wrong if we assume X=3. | |
| 993 | + */ | |
| 994 | + if( forceDelta || (szD*szD)<(szB*3-9) ){ | |
| 995 | + blob_reset(&manifest); | |
| 996 | + manifest = delta; | |
| 997 | + }else{ | |
| 998 | + blob_reset(&delta); | |
| 999 | + } | |
| 1000 | + }else if( forceDelta ){ | |
| 1001 | + fossil_panic("unable to find a baseline-manifest for the delta"); | |
| 1002 | + } | |
| 1003 | + } | |
| 874 | 1004 | if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ |
| 875 | 1005 | Blob ans; |
| 876 | 1006 | blob_zero(&ans); |
| 877 | 1007 | prompt_user("unable to sign manifest. continue (y/N)? ", &ans); |
| 878 | 1008 | if( blob_str(&ans)[0]!='y' ){ |
| 879 | 1009 | fossil_exit(1); |
| 880 | 1010 | } |
| 881 | 1011 | } |
| 882 | - if( !file_exists_in_checkout(vid, "manifest") ){ | |
| 1012 | + | |
| 1013 | + /* If the --test option is specified, output the manifest file | |
| 1014 | + ** and rollback the transaction. | |
| 1015 | + */ | |
| 1016 | + if( testRun ){ | |
| 1017 | + blob_write_to_file(&manifest, ""); | |
| 1018 | + } | |
| 1019 | + | |
| 1020 | + if( outputManifest ){ | |
| 883 | 1021 | zManifestFile = mprintf("%smanifest", g.zLocalRoot); |
| 884 | 1022 | blob_write_to_file(&manifest, zManifestFile); |
| 885 | 1023 | blob_reset(&manifest); |
| 886 | 1024 | blob_read_from_file(&manifest, zManifestFile); |
| 887 | 1025 | free(zManifestFile); |
| @@ -893,11 +1031,11 @@ | ||
| 893 | 1031 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 894 | 1032 | manifest_crosslink(nvid, &manifest); |
| 895 | 1033 | content_deltify(vid, nvid, 0); |
| 896 | 1034 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 897 | 1035 | printf("New_Version: %s\n", zUuid); |
| 898 | - if( !file_exists_in_checkout(vid, "manifest.uuid") ){ | |
| 1036 | + if( outputManifest ){ | |
| 899 | 1037 | zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 900 | 1038 | blob_zero(&muuid); |
| 901 | 1039 | blob_appendf(&muuid, "%s\n", zUuid); |
| 902 | 1040 | blob_write_to_file(&muuid, zManifestFile); |
| 903 | 1041 | free(zManifestFile); |
| @@ -914,45 +1052,51 @@ | ||
| 914 | 1052 | " WHERE file_is_selected(id);" |
| 915 | 1053 | , vid, nvid |
| 916 | 1054 | ); |
| 917 | 1055 | db_lset_int("checkout", nvid); |
| 918 | 1056 | |
| 919 | - /* Verify that the repository checksum matches the expected checksum | |
| 920 | - ** calculated before the checkin started (and stored as the R record | |
| 921 | - ** of the manifest file). | |
| 922 | - */ | |
| 923 | - vfile_aggregate_checksum_repository(nvid, &cksum2); | |
| 924 | - if( blob_compare(&cksum1, &cksum2) ){ | |
| 925 | - fossil_panic("tree checksum does not match repository after commit"); | |
| 926 | - } | |
| 927 | - | |
| 928 | - /* Verify that the manifest checksum matches the expected checksum */ | |
| 929 | - vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); | |
| 930 | - if( blob_compare(&cksum1, &cksum1b) ){ | |
| 931 | - fossil_panic("manifest checksum does not agree with manifest: " | |
| 932 | - "%b versus %b", &cksum1, &cksum1b); | |
| 933 | - } | |
| 934 | - if( blob_compare(&cksum1, &cksum2) ){ | |
| 935 | - fossil_panic("tree checksum does not match manifest after commit: " | |
| 936 | - "%b versus %b", &cksum1, &cksum2); | |
| 937 | - } | |
| 938 | - | |
| 939 | - /* Verify that the commit did not modify any disk images. */ | |
| 940 | - vfile_aggregate_checksum_disk(nvid, &cksum2); | |
| 941 | - if( blob_compare(&cksum1, &cksum2) ){ | |
| 942 | - fossil_panic("tree checksums before and after commit do not match"); | |
| 1057 | + if( useCksum ){ | |
| 1058 | + /* Verify that the repository checksum matches the expected checksum | |
| 1059 | + ** calculated before the checkin started (and stored as the R record | |
| 1060 | + ** of the manifest file). | |
| 1061 | + */ | |
| 1062 | + vfile_aggregate_checksum_repository(nvid, &cksum2); | |
| 1063 | + if( blob_compare(&cksum1, &cksum2) ){ | |
| 1064 | + fossil_panic("tree checksum does not match repository after commit"); | |
| 1065 | + } | |
| 1066 | + | |
| 1067 | + /* Verify that the manifest checksum matches the expected checksum */ | |
| 1068 | + vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); | |
| 1069 | + if( blob_compare(&cksum1, &cksum1b) ){ | |
| 1070 | + fossil_panic("manifest checksum does not agree with manifest: " | |
| 1071 | + "%b versus %b", &cksum1, &cksum1b); | |
| 1072 | + } | |
| 1073 | + if( blob_compare(&cksum1, &cksum2) ){ | |
| 1074 | + fossil_panic("tree checksum does not match manifest after commit: " | |
| 1075 | + "%b versus %b", &cksum1, &cksum2); | |
| 1076 | + } | |
| 1077 | + | |
| 1078 | + /* Verify that the commit did not modify any disk images. */ | |
| 1079 | + vfile_aggregate_checksum_disk(nvid, &cksum2); | |
| 1080 | + if( blob_compare(&cksum1, &cksum2) ){ | |
| 1081 | + fossil_panic("tree checksums before and after commit do not match"); | |
| 1082 | + } | |
| 943 | 1083 | } |
| 944 | 1084 | |
| 945 | 1085 | /* Clear the undo/redo stack */ |
| 946 | 1086 | undo_reset(); |
| 947 | 1087 | |
| 948 | 1088 | /* Commit */ |
| 949 | 1089 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 1090 | + if( testRun ){ | |
| 1091 | + db_end_transaction(1); | |
| 1092 | + exit(1); | |
| 1093 | + } | |
| 950 | 1094 | db_end_transaction(0); |
| 951 | 1095 | |
| 952 | 1096 | if( !g.markPrivate ){ |
| 953 | 1097 | autosync(AUTOSYNC_PUSH); |
| 954 | 1098 | } |
| 955 | 1099 | if( count_nonbranch_children(vid)>1 ){ |
| 956 | 1100 | printf("**** warning: a fork has occurred *****\n"); |
| 957 | 1101 | } |
| 958 | 1102 | } |
| 959 | 1103 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -215,21 +215,21 @@ | |
| 215 | int cTerm; |
| 216 | |
| 217 | if( zGlobList==0 || zGlobList[0]==0 ) return "0"; |
| 218 | blob_zero(&expr); |
| 219 | while( zGlobList[0] ){ |
| 220 | while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; |
| 221 | if( zGlobList[0]==0 ) break; |
| 222 | if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ |
| 223 | cTerm = zGlobList[0]; |
| 224 | zGlobList++; |
| 225 | }else{ |
| 226 | cTerm = ','; |
| 227 | } |
| 228 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){} |
| 229 | if( cTerm==',' ){ |
| 230 | while( i>0 && isspace(zGlobList[i-1]) ){ i--; } |
| 231 | } |
| 232 | blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList); |
| 233 | zSep = " OR "; |
| 234 | if( cTerm!=',' && zGlobList[i] ) i++; |
| 235 | zGlobList += i; |
| @@ -259,10 +259,11 @@ | |
| 259 | Blob repo; |
| 260 | Stmt q; |
| 261 | int n; |
| 262 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 263 | int allFlag = find_option("dotfiles",0,0)!=0; |
| 264 | |
| 265 | db_must_be_within_tree(); |
| 266 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); |
| 267 | n = strlen(g.zLocalRoot); |
| 268 | blob_init(&path, g.zLocalRoot, n-1); |
| @@ -270,16 +271,18 @@ | |
| 270 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 271 | } |
| 272 | vfile_scan(0, &path, blob_size(&path), allFlag); |
| 273 | db_prepare(&q, |
| 274 | "SELECT x FROM sfile" |
| 275 | " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_'," |
| 276 | "'_FOSSIL_-journal','.fos','.fos-journal'," |
| 277 | "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal'," |
| 278 | "'.fos-shm')" |
| 279 | " AND NOT %s" |
| 280 | " ORDER BY 1", |
| 281 | glob_expr("x", zIgnoreFlag) |
| 282 | ); |
| 283 | if( file_tree_name(g.zRepositoryName, &repo, 0) ){ |
| 284 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 285 | } |
| @@ -415,11 +418,11 @@ | |
| 415 | blob_add_cr(&text); |
| 416 | #endif |
| 417 | blob_write_to_file(&text, zFile); |
| 418 | zCmd = mprintf("%s \"%s\"", zEditor, zFile); |
| 419 | printf("%s\n", zCmd); |
| 420 | if( portable_system(zCmd) ){ |
| 421 | fossil_panic("editor aborted"); |
| 422 | } |
| 423 | blob_reset(&text); |
| 424 | blob_read_from_file(&text, zFile); |
| 425 | blob_remove_cr(&text); |
| @@ -429,20 +432,20 @@ | |
| 429 | while( blob_line(&text, &line) ){ |
| 430 | int i, n; |
| 431 | char *z; |
| 432 | n = blob_size(&line); |
| 433 | z = blob_buffer(&line); |
| 434 | for(i=0; i<n && isspace(z[i]); i++){} |
| 435 | if( i<n && z[i]=='#' ) continue; |
| 436 | if( i<n || blob_size(pComment)>0 ){ |
| 437 | blob_appendf(pComment, "%b", &line); |
| 438 | } |
| 439 | } |
| 440 | blob_reset(&text); |
| 441 | zComment = blob_str(pComment); |
| 442 | i = strlen(zComment); |
| 443 | while( i>0 && isspace(zComment[i-1]) ){ i--; } |
| 444 | blob_resize(pComment, i); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** Populate the Global.aCommitFile[] based on the command line arguments |
| @@ -461,11 +464,11 @@ | |
| 461 | void select_commit_files(void){ |
| 462 | if( g.argc>2 ){ |
| 463 | int ii; |
| 464 | Blob b; |
| 465 | blob_zero(&b); |
| 466 | g.aCommitFile = malloc(sizeof(int)*(g.argc-1)); |
| 467 | |
| 468 | for(ii=2; ii<g.argc; ii++){ |
| 469 | int iId; |
| 470 | file_tree_name(g.argv[ii], &b, 1); |
| 471 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| @@ -513,11 +516,11 @@ | |
| 513 | " WHERE datetime(mtime)>=%Q" |
| 514 | " AND type='ci' AND objid=%d", |
| 515 | zDate, rid |
| 516 | ); |
| 517 | if( b ){ |
| 518 | fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)" |
| 519 | " Use -f to override.", zUuid, zDate); |
| 520 | } |
| 521 | #endif |
| 522 | } |
| 523 | |
| @@ -537,41 +540,177 @@ | |
| 537 | zDate[10] = 'T'; |
| 538 | return zDate; |
| 539 | } |
| 540 | |
| 541 | /* |
| 542 | ** Return TRUE (non-zero) if a file named "zFilename" exists in |
| 543 | ** the checkout identified by vid. |
| 544 | ** |
| 545 | ** The original purpose of this routine was to check for the presence of |
| 546 | ** a "checked-in" file named "manifest" or "manifest.uuid" so as to avoid |
| 547 | ** overwriting that file with automatically generated files. |
| 548 | */ |
| 549 | int file_exists_in_checkout(int vid, const char *zFilename){ |
| 550 | return db_exists("SELECT 1 FROM vfile WHERE vid=%d AND pathname=%Q", |
| 551 | vid, zFilename); |
| 552 | } |
| 553 | |
| 554 | /* |
| 555 | ** COMMAND: ci |
| 556 | ** COMMAND: commit |
| 557 | ** |
| 558 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 559 | ** |
| 560 | ** Create a new version containing all of the changes in the current |
| 561 | ** checkout. You will be prompted to enter a check-in comment unless |
| 562 | ** the comment has been specified on the command-line using "-m". |
| 563 | ** The editor defined in the "editor" fossil option (see %fossil help set) |
| 564 | ** will be used, or from the "VISUAL" or "EDITOR" environment variables |
| 565 | ** (in that order) if no editor is set. |
| 566 | ** |
| 567 | ** You will be prompted for your GPG passphrase in order to sign the |
| 568 | ** new manifest unless the "--nosign" option is used. All files that |
| 569 | ** have changed will be committed unless some subset of files is |
| 570 | ** specified on the command line. |
| 571 | ** |
| 572 | ** The --branch option followed by a branch name cases the new check-in |
| 573 | ** to be placed in the named branch. The --bgcolor option can be followed |
| 574 | ** by a color name (ex: '#ffc0c0') to specify the background color of |
| 575 | ** entries in the new branch when shown in the web timeline interface. |
| 576 | ** |
| 577 | ** A check-in is not permitted to fork unless the --force or -f |
| @@ -581,44 +720,58 @@ | |
| 581 | ** Children of private check-ins are automatically private. |
| 582 | ** |
| 583 | ** Options: |
| 584 | ** |
| 585 | ** --comment|-m COMMENT-TEXT |
| 586 | ** --branch NEW-BRANCH-NAME |
| 587 | ** --bgcolor COLOR |
| 588 | ** --nosign |
| 589 | ** --force|-f |
| 590 | ** --private |
| 591 | ** |
| 592 | */ |
| 593 | void commit_cmd(void){ |
| 594 | int rc; |
| 595 | int vid, nrid, nvid; |
| 596 | Blob comment; |
| 597 | const char *zComment; |
| 598 | Stmt q; |
| 599 | Stmt q2; |
| 600 | char *zUuid, *zDate; |
| 601 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 602 | int isAMerge = 0; /* True if checking in a merge */ |
| 603 | int forceFlag = 0; /* Force a fork */ |
| 604 | char *zManifestFile; /* Name of the manifest file */ |
| 605 | int nBasename; /* Length of "g.zLocalRoot/" */ |
| 606 | const char *zBranch; /* Create a new branch with this name */ |
| 607 | const char *zBgColor; /* Set background color when branching */ |
| 608 | const char *zDateOvrd; /* Override date string */ |
| 609 | const char *zUserOvrd; /* Override user name */ |
| 610 | const char *zComFile; /* Read commit message from this file */ |
| 611 | Blob filename; /* complete filename */ |
| 612 | Blob manifest; |
| 613 | Blob muuid; /* Manifest uuid */ |
| 614 | Blob mcksum; /* Self-checksum on the manifest */ |
| 615 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 616 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 617 | |
| 618 | url_proxy_options(); |
| 619 | noSign = find_option("nosign",0,0)!=0; |
| 620 | zComment = find_option("comment","m",1); |
| 621 | forceFlag = find_option("force", "f", 0)!=0; |
| 622 | zBranch = find_option("branch","b",1); |
| 623 | zBgColor = find_option("bgcolor",0,1); |
| 624 | zComFile = find_option("message-file", "M", 1); |
| @@ -630,11 +783,23 @@ | |
| 630 | zDateOvrd = find_option("date-override",0,1); |
| 631 | zUserOvrd = find_option("user-override",0,1); |
| 632 | db_must_be_within_tree(); |
| 633 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 634 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 635 | verify_all_options(); |
| 636 | |
| 637 | /* Get the ID of the parent manifest artifact */ |
| 638 | vid = db_lget_int("checkout", 0); |
| 639 | if( content_is_private(vid) ){ |
| 640 | g.markPrivate = 1; |
| @@ -681,15 +846,15 @@ | |
| 681 | */ |
| 682 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 683 | fossil_fatal("no such user: %s", g.zLogin); |
| 684 | } |
| 685 | |
| 686 | db_begin_transaction(); |
| 687 | db_record_repository_filename(0); |
| 688 | rc = unsaved_changes(); |
| 689 | if( rc==0 && !isAMerge && !forceFlag ){ |
| 690 | fossil_panic("nothing has changed"); |
| 691 | } |
| 692 | |
| 693 | /* If one or more files that were named on the command line have not |
| 694 | ** been modified, bail out now. |
| 695 | */ |
| @@ -720,11 +885,11 @@ | |
| 720 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 721 | TAG_CLOSED, vid) ){ |
| 722 | fossil_fatal("cannot commit against a closed leaf"); |
| 723 | } |
| 724 | |
| 725 | vfile_aggregate_checksum_disk(vid, &cksum1); |
| 726 | if( zComment ){ |
| 727 | blob_zero(&comment); |
| 728 | blob_append(&comment, zComment, -1); |
| 729 | }else if( zComFile ){ |
| 730 | blob_zero(&comment); |
| @@ -775,113 +940,86 @@ | |
| 775 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 776 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 777 | } |
| 778 | db_finalize(&q); |
| 779 | |
| 780 | /* Create the manifest */ |
| 781 | blob_zero(&manifest); |
| 782 | if( blob_size(&comment)==0 ){ |
| 783 | blob_append(&comment, "(no comment)", -1); |
| 784 | } |
| 785 | blob_appendf(&manifest, "C %F\n", blob_str(&comment)); |
| 786 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 787 | blob_appendf(&manifest, "D %s\n", zDate); |
| 788 | zDate[10] = ' '; |
| 789 | db_prepare(&q, |
| 790 | "SELECT pathname, uuid, origname, blob.rid, isexe" |
| 791 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 792 | " WHERE NOT deleted AND vfile.vid=%d" |
| 793 | " ORDER BY 1", vid); |
| 794 | blob_zero(&filename); |
| 795 | blob_appendf(&filename, "%s", g.zLocalRoot); |
| 796 | nBasename = blob_size(&filename); |
| 797 | while( db_step(&q)==SQLITE_ROW ){ |
| 798 | const char *zName = db_column_text(&q, 0); |
| 799 | const char *zUuid = db_column_text(&q, 1); |
| 800 | const char *zOrig = db_column_text(&q, 2); |
| 801 | int frid = db_column_int(&q, 3); |
| 802 | int isexe = db_column_int(&q, 4); |
| 803 | const char *zPerm; |
| 804 | blob_append(&filename, zName, -1); |
| 805 | #if !defined(_WIN32) |
| 806 | /* For unix, extract the "executable" permission bit directly from |
| 807 | ** the filesystem. On windows, the "executable" bit is retained |
| 808 | ** unchanged from the original. */ |
| 809 | isexe = file_isexe(blob_str(&filename)); |
| 810 | #endif |
| 811 | if( isexe ){ |
| 812 | zPerm = " x"; |
| 813 | }else{ |
| 814 | zPerm = ""; |
| 815 | } |
| 816 | blob_resize(&filename, nBasename); |
| 817 | if( zOrig==0 || strcmp(zOrig,zName)==0 ){ |
| 818 | blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm); |
| 819 | }else{ |
| 820 | if( zPerm[0]==0 ){ zPerm = " w"; } |
| 821 | blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); |
| 822 | } |
| 823 | if( !g.markPrivate ) content_make_public(frid); |
| 824 | } |
| 825 | blob_reset(&filename); |
| 826 | db_finalize(&q); |
| 827 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 828 | blob_appendf(&manifest, "P %s", zUuid); |
| 829 | |
| 830 | if( !forceFlag ){ |
| 831 | checkin_verify_younger(vid, zUuid, zDate); |
| 832 | db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); |
| 833 | db_bind_int(&q2, ":id", 0); |
| 834 | while( db_step(&q2)==SQLITE_ROW ){ |
| 835 | int mid = db_column_int(&q2, 0); |
| 836 | if( !g.markPrivate && content_is_private(mid) ) continue; |
| 837 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); |
| 838 | if( zUuid ){ |
| 839 | blob_appendf(&manifest, " %s", zUuid); |
| 840 | checkin_verify_younger(mid, zUuid, zDate); |
| 841 | free(zUuid); |
| 842 | } |
| 843 | } |
| 844 | db_finalize(&q2); |
| 845 | } |
| 846 | |
| 847 | blob_appendf(&manifest, "\n"); |
| 848 | blob_appendf(&manifest, "R %b\n", &cksum1); |
| 849 | if( zBranch && zBranch[0] ){ |
| 850 | Stmt q; |
| 851 | if( zBgColor && zBgColor[0] ){ |
| 852 | blob_appendf(&manifest, "T *bgcolor * %F\n", zBgColor); |
| 853 | } |
| 854 | blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 855 | blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 856 | |
| 857 | /* Cancel all other symbolic tags */ |
| 858 | db_prepare(&q, |
| 859 | "SELECT tagname FROM tagxref, tag" |
| 860 | " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
| 861 | " AND tagtype>0 AND tagname GLOB 'sym-*'" |
| 862 | " AND tagname!='sym-'||%Q" |
| 863 | " ORDER BY tagname", |
| 864 | vid, zBranch); |
| 865 | while( db_step(&q)==SQLITE_ROW ){ |
| 866 | const char *zTag = db_column_text(&q, 0); |
| 867 | blob_appendf(&manifest, "T -%F *\n", zTag); |
| 868 | } |
| 869 | db_finalize(&q); |
| 870 | } |
| 871 | blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 872 | md5sum_blob(&manifest, &mcksum); |
| 873 | blob_appendf(&manifest, "Z %b\n", &mcksum); |
| 874 | if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ |
| 875 | Blob ans; |
| 876 | blob_zero(&ans); |
| 877 | prompt_user("unable to sign manifest. continue (y/N)? ", &ans); |
| 878 | if( blob_str(&ans)[0]!='y' ){ |
| 879 | fossil_exit(1); |
| 880 | } |
| 881 | } |
| 882 | if( !file_exists_in_checkout(vid, "manifest") ){ |
| 883 | zManifestFile = mprintf("%smanifest", g.zLocalRoot); |
| 884 | blob_write_to_file(&manifest, zManifestFile); |
| 885 | blob_reset(&manifest); |
| 886 | blob_read_from_file(&manifest, zManifestFile); |
| 887 | free(zManifestFile); |
| @@ -893,11 +1031,11 @@ | |
| 893 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 894 | manifest_crosslink(nvid, &manifest); |
| 895 | content_deltify(vid, nvid, 0); |
| 896 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 897 | printf("New_Version: %s\n", zUuid); |
| 898 | if( !file_exists_in_checkout(vid, "manifest.uuid") ){ |
| 899 | zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 900 | blob_zero(&muuid); |
| 901 | blob_appendf(&muuid, "%s\n", zUuid); |
| 902 | blob_write_to_file(&muuid, zManifestFile); |
| 903 | free(zManifestFile); |
| @@ -914,45 +1052,51 @@ | |
| 914 | " WHERE file_is_selected(id);" |
| 915 | , vid, nvid |
| 916 | ); |
| 917 | db_lset_int("checkout", nvid); |
| 918 | |
| 919 | /* Verify that the repository checksum matches the expected checksum |
| 920 | ** calculated before the checkin started (and stored as the R record |
| 921 | ** of the manifest file). |
| 922 | */ |
| 923 | vfile_aggregate_checksum_repository(nvid, &cksum2); |
| 924 | if( blob_compare(&cksum1, &cksum2) ){ |
| 925 | fossil_panic("tree checksum does not match repository after commit"); |
| 926 | } |
| 927 | |
| 928 | /* Verify that the manifest checksum matches the expected checksum */ |
| 929 | vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); |
| 930 | if( blob_compare(&cksum1, &cksum1b) ){ |
| 931 | fossil_panic("manifest checksum does not agree with manifest: " |
| 932 | "%b versus %b", &cksum1, &cksum1b); |
| 933 | } |
| 934 | if( blob_compare(&cksum1, &cksum2) ){ |
| 935 | fossil_panic("tree checksum does not match manifest after commit: " |
| 936 | "%b versus %b", &cksum1, &cksum2); |
| 937 | } |
| 938 | |
| 939 | /* Verify that the commit did not modify any disk images. */ |
| 940 | vfile_aggregate_checksum_disk(nvid, &cksum2); |
| 941 | if( blob_compare(&cksum1, &cksum2) ){ |
| 942 | fossil_panic("tree checksums before and after commit do not match"); |
| 943 | } |
| 944 | |
| 945 | /* Clear the undo/redo stack */ |
| 946 | undo_reset(); |
| 947 | |
| 948 | /* Commit */ |
| 949 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 950 | db_end_transaction(0); |
| 951 | |
| 952 | if( !g.markPrivate ){ |
| 953 | autosync(AUTOSYNC_PUSH); |
| 954 | } |
| 955 | if( count_nonbranch_children(vid)>1 ){ |
| 956 | printf("**** warning: a fork has occurred *****\n"); |
| 957 | } |
| 958 | } |
| 959 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -215,21 +215,21 @@ | |
| 215 | int cTerm; |
| 216 | |
| 217 | if( zGlobList==0 || zGlobList[0]==0 ) return "0"; |
| 218 | blob_zero(&expr); |
| 219 | while( zGlobList[0] ){ |
| 220 | while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++; |
| 221 | if( zGlobList[0]==0 ) break; |
| 222 | if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ |
| 223 | cTerm = zGlobList[0]; |
| 224 | zGlobList++; |
| 225 | }else{ |
| 226 | cTerm = ','; |
| 227 | } |
| 228 | for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){} |
| 229 | if( cTerm==',' ){ |
| 230 | while( i>0 && fossil_isspace(zGlobList[i-1]) ){ i--; } |
| 231 | } |
| 232 | blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList); |
| 233 | zSep = " OR "; |
| 234 | if( cTerm!=',' && zGlobList[i] ) i++; |
| 235 | zGlobList += i; |
| @@ -259,10 +259,11 @@ | |
| 259 | Blob repo; |
| 260 | Stmt q; |
| 261 | int n; |
| 262 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 263 | int allFlag = find_option("dotfiles",0,0)!=0; |
| 264 | int outputManifest = db_get_boolean("manifest",0); |
| 265 | |
| 266 | db_must_be_within_tree(); |
| 267 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); |
| 268 | n = strlen(g.zLocalRoot); |
| 269 | blob_init(&path, g.zLocalRoot, n-1); |
| @@ -270,16 +271,18 @@ | |
| 271 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 272 | } |
| 273 | vfile_scan(0, &path, blob_size(&path), allFlag); |
| 274 | db_prepare(&q, |
| 275 | "SELECT x FROM sfile" |
| 276 | " WHERE x NOT IN ('%s','%s','_FOSSIL_'," |
| 277 | "'_FOSSIL_-journal','.fos','.fos-journal'," |
| 278 | "'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal'," |
| 279 | "'.fos-shm')" |
| 280 | " AND NOT %s" |
| 281 | " ORDER BY 1", |
| 282 | outputManifest ? "manifest" : "_FOSSIL_", |
| 283 | outputManifest ? "manifest.uuid" : "_FOSSIL_", |
| 284 | glob_expr("x", zIgnoreFlag) |
| 285 | ); |
| 286 | if( file_tree_name(g.zRepositoryName, &repo, 0) ){ |
| 287 | db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); |
| 288 | } |
| @@ -415,11 +418,11 @@ | |
| 418 | blob_add_cr(&text); |
| 419 | #endif |
| 420 | blob_write_to_file(&text, zFile); |
| 421 | zCmd = mprintf("%s \"%s\"", zEditor, zFile); |
| 422 | printf("%s\n", zCmd); |
| 423 | if( fossil_system(zCmd) ){ |
| 424 | fossil_panic("editor aborted"); |
| 425 | } |
| 426 | blob_reset(&text); |
| 427 | blob_read_from_file(&text, zFile); |
| 428 | blob_remove_cr(&text); |
| @@ -429,20 +432,20 @@ | |
| 432 | while( blob_line(&text, &line) ){ |
| 433 | int i, n; |
| 434 | char *z; |
| 435 | n = blob_size(&line); |
| 436 | z = blob_buffer(&line); |
| 437 | for(i=0; i<n && fossil_isspace(z[i]); i++){} |
| 438 | if( i<n && z[i]=='#' ) continue; |
| 439 | if( i<n || blob_size(pComment)>0 ){ |
| 440 | blob_appendf(pComment, "%b", &line); |
| 441 | } |
| 442 | } |
| 443 | blob_reset(&text); |
| 444 | zComment = blob_str(pComment); |
| 445 | i = strlen(zComment); |
| 446 | while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; } |
| 447 | blob_resize(pComment, i); |
| 448 | } |
| 449 | |
| 450 | /* |
| 451 | ** Populate the Global.aCommitFile[] based on the command line arguments |
| @@ -461,11 +464,11 @@ | |
| 464 | void select_commit_files(void){ |
| 465 | if( g.argc>2 ){ |
| 466 | int ii; |
| 467 | Blob b; |
| 468 | blob_zero(&b); |
| 469 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 470 | |
| 471 | for(ii=2; ii<g.argc; ii++){ |
| 472 | int iId; |
| 473 | file_tree_name(g.argv[ii], &b, 1); |
| 474 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| @@ -513,11 +516,11 @@ | |
| 516 | " WHERE datetime(mtime)>=%Q" |
| 517 | " AND type='ci' AND objid=%d", |
| 518 | zDate, rid |
| 519 | ); |
| 520 | if( b ){ |
| 521 | fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)" |
| 522 | " Use -f to override.", zUuid, zDate); |
| 523 | } |
| 524 | #endif |
| 525 | } |
| 526 | |
| @@ -537,41 +540,177 @@ | |
| 540 | zDate[10] = 'T'; |
| 541 | return zDate; |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Create a manifest. |
| 546 | */ |
| 547 | static void create_manifest( |
| 548 | Blob *pOut, /* Write the manifest here */ |
| 549 | const char *zBaselineUuid, /* UUID of baseline, or zero */ |
| 550 | Manifest *pBaseline, /* Make it a delta manifest if not zero */ |
| 551 | Blob *pComment, /* Check-in comment text */ |
| 552 | int vid, /* blob-id of the parent manifest */ |
| 553 | int verifyDate, /* Verify that child is younger */ |
| 554 | Blob *pCksum, /* Repository checksum. May be 0 */ |
| 555 | const char *zDateOvrd, /* Date override. If 0 then use 'now' */ |
| 556 | const char *zUserOvrd, /* User override. If 0 then use g.zLogin */ |
| 557 | const char *zBranch, /* Branch name. May be 0 */ |
| 558 | const char *zBgColor, /* Background color. May be 0 */ |
| 559 | int *pnFBcard /* Number of generated B- and F-cards */ |
| 560 | ){ |
| 561 | char *zDate; /* Date of the check-in */ |
| 562 | char *zParentUuid; /* UUID of parent check-in */ |
| 563 | Blob filename; /* A single filename */ |
| 564 | int nBasename; /* Size of base filename */ |
| 565 | Stmt q; /* Query of files changed */ |
| 566 | Stmt q2; /* Query of merge parents */ |
| 567 | Blob mcksum; /* Manifest checksum */ |
| 568 | ManifestFile *pFile; /* File from the baseline */ |
| 569 | int nFBcard = 0; /* Number of B-cards and F-cards */ |
| 570 | |
| 571 | assert( pBaseline==0 || pBaseline->zBaseline==0 ); |
| 572 | assert( pBaseline==0 || zBaselineUuid!=0 ); |
| 573 | blob_zero(pOut); |
| 574 | zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 575 | if( pBaseline ){ |
| 576 | blob_appendf(pOut, "B %s\n", zBaselineUuid); |
| 577 | manifest_file_rewind(pBaseline); |
| 578 | pFile = manifest_file_next(pBaseline, 0); |
| 579 | nFBcard++; |
| 580 | }else{ |
| 581 | pFile = 0; |
| 582 | } |
| 583 | blob_appendf(pOut, "C %F\n", blob_str(pComment)); |
| 584 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 585 | blob_appendf(pOut, "D %s\n", zDate); |
| 586 | zDate[10] = ' '; |
| 587 | db_prepare(&q, |
| 588 | "SELECT pathname, uuid, origname, blob.rid, isexe" |
| 589 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 590 | " WHERE NOT deleted AND vfile.vid=%d" |
| 591 | " ORDER BY 1", vid); |
| 592 | blob_zero(&filename); |
| 593 | blob_appendf(&filename, "%s", g.zLocalRoot); |
| 594 | nBasename = blob_size(&filename); |
| 595 | while( db_step(&q)==SQLITE_ROW ){ |
| 596 | const char *zName = db_column_text(&q, 0); |
| 597 | const char *zUuid = db_column_text(&q, 1); |
| 598 | const char *zOrig = db_column_text(&q, 2); |
| 599 | int frid = db_column_int(&q, 3); |
| 600 | int isexe = db_column_int(&q, 4); |
| 601 | const char *zPerm; |
| 602 | int cmp; |
| 603 | blob_append(&filename, zName, -1); |
| 604 | #if !defined(_WIN32) |
| 605 | /* For unix, extract the "executable" permission bit directly from |
| 606 | ** the filesystem. On windows, the "executable" bit is retained |
| 607 | ** unchanged from the original. */ |
| 608 | isexe = file_isexe(blob_str(&filename)); |
| 609 | #endif |
| 610 | if( isexe ){ |
| 611 | zPerm = " x"; |
| 612 | }else{ |
| 613 | zPerm = ""; |
| 614 | } |
| 615 | if( !g.markPrivate ) content_make_public(frid); |
| 616 | while( pFile && strcmp(pFile->zName,zName)<0 ){ |
| 617 | blob_appendf(pOut, "F %F\n", pFile->zName); |
| 618 | pFile = manifest_file_next(pBaseline, 0); |
| 619 | nFBcard++; |
| 620 | } |
| 621 | cmp = 1; |
| 622 | if( pFile==0 |
| 623 | || (cmp = strcmp(pFile->zName,zName))!=0 |
| 624 | || strcmp(pFile->zUuid, zUuid)!=0 |
| 625 | ){ |
| 626 | blob_resize(&filename, nBasename); |
| 627 | if( zOrig==0 || strcmp(zOrig,zName)==0 ){ |
| 628 | blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm); |
| 629 | }else{ |
| 630 | if( zPerm[0]==0 ){ zPerm = " w"; } |
| 631 | blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); |
| 632 | } |
| 633 | nFBcard++; |
| 634 | } |
| 635 | if( cmp==0 ) pFile = manifest_file_next(pBaseline,0); |
| 636 | } |
| 637 | blob_reset(&filename); |
| 638 | db_finalize(&q); |
| 639 | while( pFile ){ |
| 640 | blob_appendf(pOut, "F %F\n", pFile->zName); |
| 641 | pFile = manifest_file_next(pBaseline, 0); |
| 642 | nFBcard++; |
| 643 | } |
| 644 | blob_appendf(pOut, "P %s", zParentUuid); |
| 645 | if( verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate); |
| 646 | free(zParentUuid); |
| 647 | db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); |
| 648 | db_bind_int(&q2, ":id", 0); |
| 649 | while( db_step(&q2)==SQLITE_ROW ){ |
| 650 | char *zMergeUuid; |
| 651 | int mid = db_column_int(&q2, 0); |
| 652 | if( !g.markPrivate && content_is_private(mid) ) continue; |
| 653 | zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); |
| 654 | if( zMergeUuid ){ |
| 655 | blob_appendf(pOut, " %s", zMergeUuid); |
| 656 | if( verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate); |
| 657 | free(zMergeUuid); |
| 658 | } |
| 659 | } |
| 660 | db_finalize(&q2); |
| 661 | free(zDate); |
| 662 | |
| 663 | blob_appendf(pOut, "\n"); |
| 664 | if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum); |
| 665 | if( zBranch && zBranch[0] ){ |
| 666 | Stmt q; |
| 667 | if( zBgColor && zBgColor[0] ){ |
| 668 | blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor); |
| 669 | } |
| 670 | blob_appendf(pOut, "T *branch * %F\n", zBranch); |
| 671 | blob_appendf(pOut, "T *sym-%F *\n", zBranch); |
| 672 | |
| 673 | /* Cancel all other symbolic tags */ |
| 674 | db_prepare(&q, |
| 675 | "SELECT tagname FROM tagxref, tag" |
| 676 | " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
| 677 | " AND tagtype>0 AND tagname GLOB 'sym-*'" |
| 678 | " AND tagname!='sym-'||%Q" |
| 679 | " ORDER BY tagname", |
| 680 | vid, zBranch); |
| 681 | while( db_step(&q)==SQLITE_ROW ){ |
| 682 | const char *zTag = db_column_text(&q, 0); |
| 683 | blob_appendf(pOut, "T -%F *\n", zTag); |
| 684 | } |
| 685 | db_finalize(&q); |
| 686 | } |
| 687 | blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 688 | md5sum_blob(pOut, &mcksum); |
| 689 | blob_appendf(pOut, "Z %b\n", &mcksum); |
| 690 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 691 | } |
| 692 | |
| 693 | |
| 694 | /* |
| 695 | ** COMMAND: ci |
| 696 | ** COMMAND: commit |
| 697 | ** |
| 698 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 699 | ** |
| 700 | ** Create a new version containing all of the changes in the current |
| 701 | ** checkout. You will be prompted to enter a check-in comment unless |
| 702 | ** the comment has been specified on the command-line using "-m" or a |
| 703 | ** file containing the comment using -M. The editor defined in the |
| 704 | ** "editor" fossil option (see %fossil help set) will be used, or from |
| 705 | ** the "VISUAL" or "EDITOR" environment variables (in that order) if |
| 706 | ** no editor is set. |
| 707 | ** |
| 708 | ** All files that have changed will be committed unless some subset of |
| 709 | ** files is specified on the command line. |
| 710 | ** |
| 711 | ** The --branch option followed by a branch name causes the new check-in |
| 712 | ** to be placed in the named branch. The --bgcolor option can be followed |
| 713 | ** by a color name (ex: '#ffc0c0') to specify the background color of |
| 714 | ** entries in the new branch when shown in the web timeline interface. |
| 715 | ** |
| 716 | ** A check-in is not permitted to fork unless the --force or -f |
| @@ -581,44 +720,58 @@ | |
| 720 | ** Children of private check-ins are automatically private. |
| 721 | ** |
| 722 | ** Options: |
| 723 | ** |
| 724 | ** --comment|-m COMMENT-TEXT |
| 725 | ** --message-file|-M COMMENT-FILE |
| 726 | ** --branch NEW-BRANCH-NAME |
| 727 | ** --bgcolor COLOR |
| 728 | ** --nosign |
| 729 | ** --force|-f |
| 730 | ** --private |
| 731 | ** --baseline |
| 732 | ** --delta |
| 733 | ** |
| 734 | */ |
| 735 | void commit_cmd(void){ |
| 736 | int hasChanges; /* True if unsaved changes exist */ |
| 737 | int vid; /* blob-id of parent version */ |
| 738 | int nrid; /* blob-id of a modified file */ |
| 739 | int nvid; /* Blob-id of the new check-in */ |
| 740 | Blob comment; /* Check-in comment */ |
| 741 | const char *zComment; /* Check-in comment */ |
| 742 | Stmt q; /* Query to find files that have been modified */ |
| 743 | char *zUuid; /* UUID of the new check-in */ |
| 744 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 745 | int isAMerge = 0; /* True if checking in a merge */ |
| 746 | int forceFlag = 0; /* Force a fork */ |
| 747 | int forceDelta = 0; /* Force a delta-manifest */ |
| 748 | int forceBaseline = 0; /* Force a baseline-manifest */ |
| 749 | char *zManifestFile; /* Name of the manifest file */ |
| 750 | int useCksum; /* True if checksums should be computed and verified */ |
| 751 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 752 | int testRun; /* True for a test run. Debugging only */ |
| 753 | const char *zBranch; /* Create a new branch with this name */ |
| 754 | const char *zBgColor; /* Set background color when branching */ |
| 755 | const char *zDateOvrd; /* Override date string */ |
| 756 | const char *zUserOvrd; /* Override user name */ |
| 757 | const char *zComFile; /* Read commit message from this file */ |
| 758 | Blob manifest; /* Manifest in baseline form */ |
| 759 | Blob muuid; /* Manifest uuid */ |
| 760 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 761 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 762 | int szD; /* Size of the delta manifest */ |
| 763 | int szB; /* Size of the baseline manifest */ |
| 764 | |
| 765 | url_proxy_options(); |
| 766 | noSign = find_option("nosign",0,0)!=0; |
| 767 | forceDelta = find_option("delta",0,0)!=0; |
| 768 | forceBaseline = find_option("baseline",0,0)!=0; |
| 769 | if( forceDelta && forceBaseline ){ |
| 770 | fossil_fatal("cannot use --delta and --baseline together"); |
| 771 | } |
| 772 | testRun = find_option("test",0,0)!=0; |
| 773 | zComment = find_option("comment","m",1); |
| 774 | forceFlag = find_option("force", "f", 0)!=0; |
| 775 | zBranch = find_option("branch","b",1); |
| 776 | zBgColor = find_option("bgcolor",0,1); |
| 777 | zComFile = find_option("message-file", "M", 1); |
| @@ -630,11 +783,23 @@ | |
| 783 | zDateOvrd = find_option("date-override",0,1); |
| 784 | zUserOvrd = find_option("user-override",0,1); |
| 785 | db_must_be_within_tree(); |
| 786 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 787 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 788 | useCksum = db_get_boolean("repo-cksum", 1); |
| 789 | outputManifest = db_get_boolean("manifest", 0); |
| 790 | verify_all_options(); |
| 791 | |
| 792 | /* So that older versions of Fossil (that do not understand delta- |
| 793 | ** manifest) can continue to use this repository, do not create a new |
| 794 | ** delta-manifest unless this repository already contains one or more |
| 795 | ** delta-manifets, or unless the delta-manifest is explicitly requested |
| 796 | ** by the --delta option. |
| 797 | */ |
| 798 | if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ |
| 799 | forceBaseline = 1; |
| 800 | } |
| 801 | |
| 802 | /* Get the ID of the parent manifest artifact */ |
| 803 | vid = db_lget_int("checkout", 0); |
| 804 | if( content_is_private(vid) ){ |
| 805 | g.markPrivate = 1; |
| @@ -681,15 +846,15 @@ | |
| 846 | */ |
| 847 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 848 | fossil_fatal("no such user: %s", g.zLogin); |
| 849 | } |
| 850 | |
| 851 | hasChanges = unsaved_changes(); |
| 852 | db_begin_transaction(); |
| 853 | db_record_repository_filename(0); |
| 854 | if( hasChanges==0 && !isAMerge && !forceFlag ){ |
| 855 | fossil_fatal("nothing has changed"); |
| 856 | } |
| 857 | |
| 858 | /* If one or more files that were named on the command line have not |
| 859 | ** been modified, bail out now. |
| 860 | */ |
| @@ -720,11 +885,11 @@ | |
| 885 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 886 | TAG_CLOSED, vid) ){ |
| 887 | fossil_fatal("cannot commit against a closed leaf"); |
| 888 | } |
| 889 | |
| 890 | if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1); |
| 891 | if( zComment ){ |
| 892 | blob_zero(&comment); |
| 893 | blob_append(&comment, zComment, -1); |
| 894 | }else if( zComFile ){ |
| 895 | blob_zero(&comment); |
| @@ -775,113 +940,86 @@ | |
| 940 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 941 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 942 | } |
| 943 | db_finalize(&q); |
| 944 | |
| 945 | /* Create the new manifest */ |
| 946 | if( blob_size(&comment)==0 ){ |
| 947 | blob_append(&comment, "(no comment)", -1); |
| 948 | } |
| 949 | if( forceDelta ){ |
| 950 | blob_zero(&manifest); |
| 951 | }else{ |
| 952 | create_manifest(&manifest, 0, 0, &comment, vid, |
| 953 | !forceFlag, useCksum ? &cksum1 : 0, |
| 954 | zDateOvrd, zUserOvrd, zBranch, zBgColor, &szB); |
| 955 | } |
| 956 | |
| 957 | /* See if a delta-manifest would be more appropriate */ |
| 958 | if( !forceBaseline ){ |
| 959 | const char *zBaselineUuid; |
| 960 | Manifest *pParent; |
| 961 | Manifest *pBaseline; |
| 962 | pParent = manifest_get(vid, CFTYPE_MANIFEST); |
| 963 | if( pParent && pParent->zBaseline ){ |
| 964 | zBaselineUuid = pParent->zBaseline; |
| 965 | pBaseline = manifest_get_by_name(zBaselineUuid, 0); |
| 966 | }else{ |
| 967 | zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 968 | pBaseline = pParent; |
| 969 | } |
| 970 | if( pBaseline ){ |
| 971 | Blob delta; |
| 972 | create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid, |
| 973 | !forceFlag, useCksum ? &cksum1 : 0, |
| 974 | zDateOvrd, zUserOvrd, zBranch, zBgColor, &szD); |
| 975 | /* |
| 976 | ** At this point, two manifests have been constructed, either of |
| 977 | ** which would work for this checkin. The first manifest (held |
| 978 | ** in the "manifest" variable) is a baseline manifest and the second |
| 979 | ** (held in variable named "delta") is a delta manifest. The |
| 980 | ** question now is: which manifest should we use? |
| 981 | ** |
| 982 | ** Let B be the number of F-cards in the baseline manifest and |
| 983 | ** let D be the number of F-cards in the delta manifest, plus one for |
| 984 | ** the B-card. (B is held in the szB variable and D is held in the |
| 985 | ** szD variable.) Assume that all delta manifests adds X new F-cards. |
| 986 | ** Then to minimize the total number of F- and B-cards in the repository, |
| 987 | ** we should use the delta manifest if and only if: |
| 988 | ** |
| 989 | ** D*D < B*X - X*X |
| 990 | ** |
| 991 | ** X is an unknown here, but for most repositories, we will not be |
| 992 | ** far wrong if we assume X=3. |
| 993 | */ |
| 994 | if( forceDelta || (szD*szD)<(szB*3-9) ){ |
| 995 | blob_reset(&manifest); |
| 996 | manifest = delta; |
| 997 | }else{ |
| 998 | blob_reset(&delta); |
| 999 | } |
| 1000 | }else if( forceDelta ){ |
| 1001 | fossil_panic("unable to find a baseline-manifest for the delta"); |
| 1002 | } |
| 1003 | } |
| 1004 | if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ |
| 1005 | Blob ans; |
| 1006 | blob_zero(&ans); |
| 1007 | prompt_user("unable to sign manifest. continue (y/N)? ", &ans); |
| 1008 | if( blob_str(&ans)[0]!='y' ){ |
| 1009 | fossil_exit(1); |
| 1010 | } |
| 1011 | } |
| 1012 | |
| 1013 | /* If the --test option is specified, output the manifest file |
| 1014 | ** and rollback the transaction. |
| 1015 | */ |
| 1016 | if( testRun ){ |
| 1017 | blob_write_to_file(&manifest, ""); |
| 1018 | } |
| 1019 | |
| 1020 | if( outputManifest ){ |
| 1021 | zManifestFile = mprintf("%smanifest", g.zLocalRoot); |
| 1022 | blob_write_to_file(&manifest, zManifestFile); |
| 1023 | blob_reset(&manifest); |
| 1024 | blob_read_from_file(&manifest, zManifestFile); |
| 1025 | free(zManifestFile); |
| @@ -893,11 +1031,11 @@ | |
| 1031 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1032 | manifest_crosslink(nvid, &manifest); |
| 1033 | content_deltify(vid, nvid, 0); |
| 1034 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1035 | printf("New_Version: %s\n", zUuid); |
| 1036 | if( outputManifest ){ |
| 1037 | zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 1038 | blob_zero(&muuid); |
| 1039 | blob_appendf(&muuid, "%s\n", zUuid); |
| 1040 | blob_write_to_file(&muuid, zManifestFile); |
| 1041 | free(zManifestFile); |
| @@ -914,45 +1052,51 @@ | |
| 1052 | " WHERE file_is_selected(id);" |
| 1053 | , vid, nvid |
| 1054 | ); |
| 1055 | db_lset_int("checkout", nvid); |
| 1056 | |
| 1057 | if( useCksum ){ |
| 1058 | /* Verify that the repository checksum matches the expected checksum |
| 1059 | ** calculated before the checkin started (and stored as the R record |
| 1060 | ** of the manifest file). |
| 1061 | */ |
| 1062 | vfile_aggregate_checksum_repository(nvid, &cksum2); |
| 1063 | if( blob_compare(&cksum1, &cksum2) ){ |
| 1064 | fossil_panic("tree checksum does not match repository after commit"); |
| 1065 | } |
| 1066 | |
| 1067 | /* Verify that the manifest checksum matches the expected checksum */ |
| 1068 | vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); |
| 1069 | if( blob_compare(&cksum1, &cksum1b) ){ |
| 1070 | fossil_panic("manifest checksum does not agree with manifest: " |
| 1071 | "%b versus %b", &cksum1, &cksum1b); |
| 1072 | } |
| 1073 | if( blob_compare(&cksum1, &cksum2) ){ |
| 1074 | fossil_panic("tree checksum does not match manifest after commit: " |
| 1075 | "%b versus %b", &cksum1, &cksum2); |
| 1076 | } |
| 1077 | |
| 1078 | /* Verify that the commit did not modify any disk images. */ |
| 1079 | vfile_aggregate_checksum_disk(nvid, &cksum2); |
| 1080 | if( blob_compare(&cksum1, &cksum2) ){ |
| 1081 | fossil_panic("tree checksums before and after commit do not match"); |
| 1082 | } |
| 1083 | } |
| 1084 | |
| 1085 | /* Clear the undo/redo stack */ |
| 1086 | undo_reset(); |
| 1087 | |
| 1088 | /* Commit */ |
| 1089 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 1090 | if( testRun ){ |
| 1091 | db_end_transaction(1); |
| 1092 | exit(1); |
| 1093 | } |
| 1094 | db_end_transaction(0); |
| 1095 | |
| 1096 | if( !g.markPrivate ){ |
| 1097 | autosync(AUTOSYNC_PUSH); |
| 1098 | } |
| 1099 | if( count_nonbranch_children(vid)>1 ){ |
| 1100 | printf("**** warning: a fork has occurred *****\n"); |
| 1101 | } |
| 1102 | } |
| 1103 |
+60
-47
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -78,87 +78,99 @@ | ||
| 78 | 78 | |
| 79 | 79 | /* |
| 80 | 80 | ** Load a vfile from a record ID. |
| 81 | 81 | */ |
| 82 | 82 | void load_vfile_from_rid(int vid){ |
| 83 | - Blob manifest; | |
| 84 | - | |
| 85 | 83 | if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ |
| 86 | 84 | return; |
| 87 | 85 | } |
| 88 | - content_get(vid, &manifest); | |
| 89 | - vfile_build(vid, &manifest); | |
| 90 | - blob_reset(&manifest); | |
| 86 | + vfile_build(vid); | |
| 91 | 87 | } |
| 92 | 88 | |
| 93 | 89 | /* |
| 94 | 90 | ** Set or clear the vfile.isexe flag for a file. |
| 95 | 91 | */ |
| 96 | 92 | static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ |
| 97 | - db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q", | |
| 98 | - onoff, vid, zFilename); | |
| 93 | + static Stmt s; | |
| 94 | + db_static_prepare(&s, | |
| 95 | + "UPDATE vfile SET isexe=:isexe" | |
| 96 | + " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe" | |
| 97 | + ); | |
| 98 | + db_bind_int(&s, ":isexe", onoff); | |
| 99 | + db_bind_int(&s, ":vid", vid); | |
| 100 | + db_bind_text(&s, ":path", zFilename); | |
| 101 | + db_step(&s); | |
| 102 | + db_reset(&s); | |
| 99 | 103 | } |
| 100 | 104 | |
| 101 | 105 | /* |
| 102 | 106 | ** Set or clear the execute permission bit (as appropriate) for all |
| 103 | 107 | ** files in the current check-out. |
| 104 | -** | |
| 105 | -** If the checkout does not have explicit files named "manifest" and | |
| 106 | -** "manifest.uuid" then automatically generate files with those names | |
| 107 | -** containing, respectively, the text of the manifest and the artifact | |
| 108 | -** ID of the manifest. | |
| 108 | +*/ | |
| 109 | +void checkout_set_all_exe(int vid){ | |
| 110 | + Blob filename; | |
| 111 | + int baseLen; | |
| 112 | + Manifest *pManifest; | |
| 113 | + ManifestFile *pFile; | |
| 114 | + | |
| 115 | + /* Check the EXE permission status of all files | |
| 116 | + */ | |
| 117 | + pManifest = manifest_get(vid, CFTYPE_MANIFEST); | |
| 118 | + if( pManifest==0 ) return; | |
| 119 | + blob_zero(&filename); | |
| 120 | + blob_appendf(&filename, "%s/", g.zLocalRoot); | |
| 121 | + baseLen = blob_size(&filename); | |
| 122 | + manifest_file_rewind(pManifest); | |
| 123 | + while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ | |
| 124 | + int isExe; | |
| 125 | + blob_append(&filename, pFile->zName, -1); | |
| 126 | + isExe = pFile->zPerm && strstr(pFile->zPerm, "x"); | |
| 127 | + file_setexe(blob_str(&filename), isExe); | |
| 128 | + set_or_clear_isexe(pFile->zName, vid, isExe); | |
| 129 | + blob_resize(&filename, baseLen); | |
| 130 | + } | |
| 131 | + blob_reset(&filename); | |
| 132 | + manifest_destroy(pManifest); | |
| 133 | +} | |
| 134 | + | |
| 135 | + | |
| 136 | +/* | |
| 137 | +** If the "manifest" setting is true, then automatically generate | |
| 138 | +** files named "manifest" and "manifest.uuid" containing, respectively, | |
| 139 | +** the text of the manifest and the artifact ID of the manifest. | |
| 109 | 140 | */ |
| 110 | 141 | void manifest_to_disk(int vid){ |
| 111 | 142 | char *zManFile; |
| 112 | 143 | Blob manifest; |
| 113 | 144 | Blob hash; |
| 114 | - Blob filename; | |
| 115 | - int baseLen; | |
| 116 | - int i; | |
| 117 | - int seenManifest = 0; | |
| 118 | - int seenManifestUuid = 0; | |
| 119 | - Manifest m; | |
| 120 | - | |
| 121 | - /* Check the EXE permission status of all files | |
| 122 | - */ | |
| 123 | - blob_zero(&manifest); | |
| 124 | - content_get(vid, &manifest); | |
| 125 | - manifest_parse(&m, &manifest); | |
| 126 | - blob_zero(&filename); | |
| 127 | - blob_appendf(&filename, "%s/", g.zLocalRoot); | |
| 128 | - baseLen = blob_size(&filename); | |
| 129 | - for(i=0; i<m.nFile; i++){ | |
| 130 | - int isExe; | |
| 131 | - blob_append(&filename, m.aFile[i].zName, -1); | |
| 132 | - isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x"); | |
| 133 | - file_setexe(blob_str(&filename), isExe); | |
| 134 | - set_or_clear_isexe(m.aFile[i].zName, vid, isExe); | |
| 135 | - blob_resize(&filename, baseLen); | |
| 136 | - if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){ | |
| 137 | - if( m.aFile[i].zName[8]==0 ) seenManifest = 1; | |
| 138 | - if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1; | |
| 139 | - } | |
| 140 | - } | |
| 141 | - blob_reset(&filename); | |
| 142 | - manifest_clear(&m); | |
| 143 | - | |
| 144 | - blob_zero(&manifest); | |
| 145 | - content_get(vid, &manifest); | |
| 146 | - if( !seenManifest ){ | |
| 145 | + | |
| 146 | + if( db_get_boolean("manifest",0) ){ | |
| 147 | + blob_zero(&manifest); | |
| 148 | + content_get(vid, &manifest); | |
| 147 | 149 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 148 | 150 | blob_write_to_file(&manifest, zManFile); |
| 149 | 151 | free(zManFile); |
| 150 | - } | |
| 151 | - if( !seenManifestUuid ){ | |
| 152 | 152 | blob_zero(&hash); |
| 153 | 153 | sha1sum_blob(&manifest, &hash); |
| 154 | 154 | zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 155 | 155 | blob_append(&hash, "\n", 1); |
| 156 | 156 | blob_write_to_file(&hash, zManFile); |
| 157 | 157 | free(zManFile); |
| 158 | 158 | blob_reset(&hash); |
| 159 | + }else{ | |
| 160 | + if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ | |
| 161 | + zManFile = mprintf("%smanifest", g.zLocalRoot); | |
| 162 | + unlink(zManFile); | |
| 163 | + free(zManFile); | |
| 164 | + } | |
| 165 | + if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){ | |
| 166 | + zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); | |
| 167 | + unlink(zManFile); | |
| 168 | + free(zManFile); | |
| 169 | + } | |
| 159 | 170 | } |
| 171 | + | |
| 160 | 172 | } |
| 161 | 173 | |
| 162 | 174 | /* |
| 163 | 175 | ** COMMAND: checkout |
| 164 | 176 | ** COMMAND: co |
| @@ -228,10 +240,11 @@ | ||
| 228 | 240 | } |
| 229 | 241 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); |
| 230 | 242 | if( !keepFlag ){ |
| 231 | 243 | vfile_to_disk(vid, 0, 1, promptFlag); |
| 232 | 244 | } |
| 245 | + checkout_set_all_exe(vid); | |
| 233 | 246 | manifest_to_disk(vid); |
| 234 | 247 | db_lset_int("checkout", vid); |
| 235 | 248 | undo_reset(); |
| 236 | 249 | db_multi_exec("DELETE FROM vmerge"); |
| 237 | 250 | if( !keepFlag ){ |
| 238 | 251 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -78,87 +78,99 @@ | |
| 78 | |
| 79 | /* |
| 80 | ** Load a vfile from a record ID. |
| 81 | */ |
| 82 | void load_vfile_from_rid(int vid){ |
| 83 | Blob manifest; |
| 84 | |
| 85 | if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ |
| 86 | return; |
| 87 | } |
| 88 | content_get(vid, &manifest); |
| 89 | vfile_build(vid, &manifest); |
| 90 | blob_reset(&manifest); |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | ** Set or clear the vfile.isexe flag for a file. |
| 95 | */ |
| 96 | static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ |
| 97 | db_multi_exec("UPDATE vfile SET isexe=%d WHERE vid=%d and pathname=%Q", |
| 98 | onoff, vid, zFilename); |
| 99 | } |
| 100 | |
| 101 | /* |
| 102 | ** Set or clear the execute permission bit (as appropriate) for all |
| 103 | ** files in the current check-out. |
| 104 | ** |
| 105 | ** If the checkout does not have explicit files named "manifest" and |
| 106 | ** "manifest.uuid" then automatically generate files with those names |
| 107 | ** containing, respectively, the text of the manifest and the artifact |
| 108 | ** ID of the manifest. |
| 109 | */ |
| 110 | void manifest_to_disk(int vid){ |
| 111 | char *zManFile; |
| 112 | Blob manifest; |
| 113 | Blob hash; |
| 114 | Blob filename; |
| 115 | int baseLen; |
| 116 | int i; |
| 117 | int seenManifest = 0; |
| 118 | int seenManifestUuid = 0; |
| 119 | Manifest m; |
| 120 | |
| 121 | /* Check the EXE permission status of all files |
| 122 | */ |
| 123 | blob_zero(&manifest); |
| 124 | content_get(vid, &manifest); |
| 125 | manifest_parse(&m, &manifest); |
| 126 | blob_zero(&filename); |
| 127 | blob_appendf(&filename, "%s/", g.zLocalRoot); |
| 128 | baseLen = blob_size(&filename); |
| 129 | for(i=0; i<m.nFile; i++){ |
| 130 | int isExe; |
| 131 | blob_append(&filename, m.aFile[i].zName, -1); |
| 132 | isExe = m.aFile[i].zPerm && strstr(m.aFile[i].zPerm, "x"); |
| 133 | file_setexe(blob_str(&filename), isExe); |
| 134 | set_or_clear_isexe(m.aFile[i].zName, vid, isExe); |
| 135 | blob_resize(&filename, baseLen); |
| 136 | if( memcmp(m.aFile[i].zName, "manifest", 8)==0 ){ |
| 137 | if( m.aFile[i].zName[8]==0 ) seenManifest = 1; |
| 138 | if( strcmp(&m.aFile[i].zName[8], ".uuid")==0 ) seenManifestUuid = 1; |
| 139 | } |
| 140 | } |
| 141 | blob_reset(&filename); |
| 142 | manifest_clear(&m); |
| 143 | |
| 144 | blob_zero(&manifest); |
| 145 | content_get(vid, &manifest); |
| 146 | if( !seenManifest ){ |
| 147 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 148 | blob_write_to_file(&manifest, zManFile); |
| 149 | free(zManFile); |
| 150 | } |
| 151 | if( !seenManifestUuid ){ |
| 152 | blob_zero(&hash); |
| 153 | sha1sum_blob(&manifest, &hash); |
| 154 | zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 155 | blob_append(&hash, "\n", 1); |
| 156 | blob_write_to_file(&hash, zManFile); |
| 157 | free(zManFile); |
| 158 | blob_reset(&hash); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** COMMAND: checkout |
| 164 | ** COMMAND: co |
| @@ -228,10 +240,11 @@ | |
| 228 | } |
| 229 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); |
| 230 | if( !keepFlag ){ |
| 231 | vfile_to_disk(vid, 0, 1, promptFlag); |
| 232 | } |
| 233 | manifest_to_disk(vid); |
| 234 | db_lset_int("checkout", vid); |
| 235 | undo_reset(); |
| 236 | db_multi_exec("DELETE FROM vmerge"); |
| 237 | if( !keepFlag ){ |
| 238 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -78,87 +78,99 @@ | |
| 78 | |
| 79 | /* |
| 80 | ** Load a vfile from a record ID. |
| 81 | */ |
| 82 | void load_vfile_from_rid(int vid){ |
| 83 | if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ |
| 84 | return; |
| 85 | } |
| 86 | vfile_build(vid); |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | ** Set or clear the vfile.isexe flag for a file. |
| 91 | */ |
| 92 | static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ |
| 93 | static Stmt s; |
| 94 | db_static_prepare(&s, |
| 95 | "UPDATE vfile SET isexe=:isexe" |
| 96 | " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe" |
| 97 | ); |
| 98 | db_bind_int(&s, ":isexe", onoff); |
| 99 | db_bind_int(&s, ":vid", vid); |
| 100 | db_bind_text(&s, ":path", zFilename); |
| 101 | db_step(&s); |
| 102 | db_reset(&s); |
| 103 | } |
| 104 | |
| 105 | /* |
| 106 | ** Set or clear the execute permission bit (as appropriate) for all |
| 107 | ** files in the current check-out. |
| 108 | */ |
| 109 | void checkout_set_all_exe(int vid){ |
| 110 | Blob filename; |
| 111 | int baseLen; |
| 112 | Manifest *pManifest; |
| 113 | ManifestFile *pFile; |
| 114 | |
| 115 | /* Check the EXE permission status of all files |
| 116 | */ |
| 117 | pManifest = manifest_get(vid, CFTYPE_MANIFEST); |
| 118 | if( pManifest==0 ) return; |
| 119 | blob_zero(&filename); |
| 120 | blob_appendf(&filename, "%s/", g.zLocalRoot); |
| 121 | baseLen = blob_size(&filename); |
| 122 | manifest_file_rewind(pManifest); |
| 123 | while( (pFile = manifest_file_next(pManifest, 0))!=0 ){ |
| 124 | int isExe; |
| 125 | blob_append(&filename, pFile->zName, -1); |
| 126 | isExe = pFile->zPerm && strstr(pFile->zPerm, "x"); |
| 127 | file_setexe(blob_str(&filename), isExe); |
| 128 | set_or_clear_isexe(pFile->zName, vid, isExe); |
| 129 | blob_resize(&filename, baseLen); |
| 130 | } |
| 131 | blob_reset(&filename); |
| 132 | manifest_destroy(pManifest); |
| 133 | } |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | ** If the "manifest" setting is true, then automatically generate |
| 138 | ** files named "manifest" and "manifest.uuid" containing, respectively, |
| 139 | ** the text of the manifest and the artifact ID of the manifest. |
| 140 | */ |
| 141 | void manifest_to_disk(int vid){ |
| 142 | char *zManFile; |
| 143 | Blob manifest; |
| 144 | Blob hash; |
| 145 | |
| 146 | if( db_get_boolean("manifest",0) ){ |
| 147 | blob_zero(&manifest); |
| 148 | content_get(vid, &manifest); |
| 149 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 150 | blob_write_to_file(&manifest, zManFile); |
| 151 | free(zManFile); |
| 152 | blob_zero(&hash); |
| 153 | sha1sum_blob(&manifest, &hash); |
| 154 | zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 155 | blob_append(&hash, "\n", 1); |
| 156 | blob_write_to_file(&hash, zManFile); |
| 157 | free(zManFile); |
| 158 | blob_reset(&hash); |
| 159 | }else{ |
| 160 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ |
| 161 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 162 | unlink(zManFile); |
| 163 | free(zManFile); |
| 164 | } |
| 165 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){ |
| 166 | zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); |
| 167 | unlink(zManFile); |
| 168 | free(zManFile); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | ** COMMAND: checkout |
| 176 | ** COMMAND: co |
| @@ -228,10 +240,11 @@ | |
| 240 | } |
| 241 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); |
| 242 | if( !keepFlag ){ |
| 243 | vfile_to_disk(vid, 0, 1, promptFlag); |
| 244 | } |
| 245 | checkout_set_all_exe(vid); |
| 246 | manifest_to_disk(vid); |
| 247 | db_lset_int("checkout", vid); |
| 248 | undo_reset(); |
| 249 | db_multi_exec("DELETE FROM vmerge"); |
| 250 | if( !keepFlag ){ |
| 251 |
+1
-1
| --- src/clearsign.c | ||
| +++ src/clearsign.c | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | zRand = db_text(0, "SELECT hex(randomblob(10))"); |
| 40 | 40 | zOut = mprintf("out-%s", zRand); |
| 41 | 41 | zIn = mprintf("in-%z", zRand); |
| 42 | 42 | blob_write_to_file(pIn, zOut); |
| 43 | 43 | zCmd = mprintf("%s %s %s", zBase, zIn, zOut); |
| 44 | - rc = portable_system(zCmd); | |
| 44 | + rc = fossil_system(zCmd); | |
| 45 | 45 | free(zCmd); |
| 46 | 46 | if( rc==0 ){ |
| 47 | 47 | if( pOut==pIn ){ |
| 48 | 48 | blob_reset(pIn); |
| 49 | 49 | } |
| 50 | 50 |
| --- src/clearsign.c | |
| +++ src/clearsign.c | |
| @@ -39,11 +39,11 @@ | |
| 39 | zRand = db_text(0, "SELECT hex(randomblob(10))"); |
| 40 | zOut = mprintf("out-%s", zRand); |
| 41 | zIn = mprintf("in-%z", zRand); |
| 42 | blob_write_to_file(pIn, zOut); |
| 43 | zCmd = mprintf("%s %s %s", zBase, zIn, zOut); |
| 44 | rc = portable_system(zCmd); |
| 45 | free(zCmd); |
| 46 | if( rc==0 ){ |
| 47 | if( pOut==pIn ){ |
| 48 | blob_reset(pIn); |
| 49 | } |
| 50 |
| --- src/clearsign.c | |
| +++ src/clearsign.c | |
| @@ -39,11 +39,11 @@ | |
| 39 | zRand = db_text(0, "SELECT hex(randomblob(10))"); |
| 40 | zOut = mprintf("out-%s", zRand); |
| 41 | zIn = mprintf("in-%z", zRand); |
| 42 | blob_write_to_file(pIn, zOut); |
| 43 | zCmd = mprintf("%s %s %s", zBase, zIn, zOut); |
| 44 | rc = fossil_system(zCmd); |
| 45 | free(zCmd); |
| 46 | if( rc==0 ){ |
| 47 | if( pOut==pIn ){ |
| 48 | blob_reset(pIn); |
| 49 | } |
| 50 |
+3
-3
| --- src/comformat.c | ||
| +++ src/comformat.c | ||
| @@ -38,29 +38,29 @@ | ||
| 38 | 38 | int doIndent = 0; |
| 39 | 39 | char zBuf[400]; |
| 40 | 40 | int lineCnt = 0; |
| 41 | 41 | |
| 42 | 42 | for(;;){ |
| 43 | - while( isspace(zText[0]) ){ zText++; } | |
| 43 | + while( fossil_isspace(zText[0]) ){ zText++; } | |
| 44 | 44 | if( zText[0]==0 ){ |
| 45 | 45 | if( doIndent==0 ){ |
| 46 | 46 | printf("\n"); |
| 47 | 47 | lineCnt = 1; |
| 48 | 48 | } |
| 49 | 49 | return lineCnt; |
| 50 | 50 | } |
| 51 | 51 | for(sk=si=i=k=0; zText[i] && k<tlen; i++){ |
| 52 | 52 | char c = zText[i]; |
| 53 | - if( isspace(c) ){ | |
| 53 | + if( fossil_isspace(c) ){ | |
| 54 | 54 | si = i; |
| 55 | 55 | sk = k; |
| 56 | 56 | if( k==0 || zBuf[k-1]!=' ' ){ |
| 57 | 57 | zBuf[k++] = ' '; |
| 58 | 58 | } |
| 59 | 59 | }else{ |
| 60 | 60 | zBuf[k] = c; |
| 61 | - if( c=='-' && k>0 && isalpha(zBuf[k-1]) ){ | |
| 61 | + if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){ | |
| 62 | 62 | si = i+1; |
| 63 | 63 | sk = k+1; |
| 64 | 64 | } |
| 65 | 65 | k++; |
| 66 | 66 | } |
| 67 | 67 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -38,29 +38,29 @@ | |
| 38 | int doIndent = 0; |
| 39 | char zBuf[400]; |
| 40 | int lineCnt = 0; |
| 41 | |
| 42 | for(;;){ |
| 43 | while( isspace(zText[0]) ){ zText++; } |
| 44 | if( zText[0]==0 ){ |
| 45 | if( doIndent==0 ){ |
| 46 | printf("\n"); |
| 47 | lineCnt = 1; |
| 48 | } |
| 49 | return lineCnt; |
| 50 | } |
| 51 | for(sk=si=i=k=0; zText[i] && k<tlen; i++){ |
| 52 | char c = zText[i]; |
| 53 | if( isspace(c) ){ |
| 54 | si = i; |
| 55 | sk = k; |
| 56 | if( k==0 || zBuf[k-1]!=' ' ){ |
| 57 | zBuf[k++] = ' '; |
| 58 | } |
| 59 | }else{ |
| 60 | zBuf[k] = c; |
| 61 | if( c=='-' && k>0 && isalpha(zBuf[k-1]) ){ |
| 62 | si = i+1; |
| 63 | sk = k+1; |
| 64 | } |
| 65 | k++; |
| 66 | } |
| 67 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -38,29 +38,29 @@ | |
| 38 | int doIndent = 0; |
| 39 | char zBuf[400]; |
| 40 | int lineCnt = 0; |
| 41 | |
| 42 | for(;;){ |
| 43 | while( fossil_isspace(zText[0]) ){ zText++; } |
| 44 | if( zText[0]==0 ){ |
| 45 | if( doIndent==0 ){ |
| 46 | printf("\n"); |
| 47 | lineCnt = 1; |
| 48 | } |
| 49 | return lineCnt; |
| 50 | } |
| 51 | for(sk=si=i=k=0; zText[i] && k<tlen; i++){ |
| 52 | char c = zText[i]; |
| 53 | if( fossil_isspace(c) ){ |
| 54 | si = i; |
| 55 | sk = k; |
| 56 | if( k==0 || zBuf[k-1]!=' ' ){ |
| 57 | zBuf[k++] = ' '; |
| 58 | } |
| 59 | }else{ |
| 60 | zBuf[k] = c; |
| 61 | if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){ |
| 62 | si = i+1; |
| 63 | sk = k+1; |
| 64 | } |
| 65 | k++; |
| 66 | } |
| 67 |
+59
-45
| --- src/config.h | ||
| +++ src/config.h | ||
| @@ -25,21 +25,25 @@ | ||
| 25 | 25 | #ifndef _FILE_OFFSET_BITS |
| 26 | 26 | # define _FILE_OFFSET_BITS 64 |
| 27 | 27 | #endif |
| 28 | 28 | #define _LARGEFILE_SOURCE 1 |
| 29 | 29 | |
| 30 | +#ifndef _RC_COMPILE_ | |
| 30 | 31 | |
| 31 | 32 | /* |
| 32 | 33 | ** System header files used by all modules |
| 33 | 34 | */ |
| 34 | 35 | #include <unistd.h> |
| 35 | 36 | #include <stdio.h> |
| 36 | 37 | #include <stdlib.h> |
| 37 | -#include <ctype.h> | |
| 38 | +/* #include <ctype.h> // do not use - causes problems */ | |
| 38 | 39 | #include <string.h> |
| 39 | 40 | #include <stdarg.h> |
| 40 | 41 | #include <assert.h> |
| 42 | + | |
| 43 | +#endif | |
| 44 | + | |
| 41 | 45 | #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 42 | 46 | # if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 43 | 47 | typedef int socklen_t; |
| 44 | 48 | # endif |
| 45 | 49 | # ifndef _WIN32 |
| @@ -49,71 +53,79 @@ | ||
| 49 | 53 | # include <sys/types.h> |
| 50 | 54 | # include <signal.h> |
| 51 | 55 | # include <pwd.h> |
| 52 | 56 | #endif |
| 53 | 57 | |
| 58 | +/* | |
| 59 | +** Define the compiler variant, used to compile the project | |
| 60 | +*/ | |
| 61 | +#if !defined(COMPILER_NAME) | |
| 62 | +# if defined(__DMC__) | |
| 63 | +# define COMPILER_NAME "dmc" | |
| 64 | +# elif defined(__POCC__) | |
| 65 | +# if defined(_M_X64) | |
| 66 | +# define COMPILER_NAME "pellesc64" | |
| 67 | +# else | |
| 68 | +# define COMPILER_NAME "pellesc32" | |
| 69 | +# endif | |
| 70 | +# elif defined(_MSC_VER) | |
| 71 | +# define COMPILER_NAME "msc" | |
| 72 | +# elif defined(__MINGW32__) | |
| 73 | +# define COMPILER_NAME "mingw32" | |
| 74 | +# elif defined(_WIN32) | |
| 75 | +# define COMPILER_NAME "win32" | |
| 76 | +# elif defined(__GNUC__) | |
| 77 | +# define COMPILER_NAME "gcc-" __VERSION__ | |
| 78 | +# else | |
| 79 | +# define COMPILER_NAME "unknown" | |
| 80 | +# endif | |
| 81 | +#endif | |
| 82 | + | |
| 83 | +#ifndef _RC_COMPILE_ | |
| 84 | + | |
| 54 | 85 | #include "sqlite3.h" |
| 55 | 86 | |
| 56 | 87 | /* |
| 57 | 88 | ** Typedef for a 64-bit integer |
| 58 | 89 | */ |
| 59 | -typedef sqlite_int64 i64; | |
| 60 | -typedef sqlite_uint64 u64; | |
| 90 | +typedef sqlite3_int64 i64; | |
| 91 | +typedef sqlite3_uint64 u64; | |
| 61 | 92 | |
| 62 | 93 | /* |
| 63 | 94 | ** Unsigned character type |
| 64 | 95 | */ |
| 65 | 96 | typedef unsigned char u8; |
| 66 | 97 | |
| 67 | -/* | |
| 68 | -** Standard colors. These colors can also be changed using a stylesheet. | |
| 69 | -*/ | |
| 70 | - | |
| 71 | -/* A blue border and background. Used for the title bar and for dates | |
| 72 | -** in a timeline. | |
| 73 | -*/ | |
| 74 | -#define BORDER1 "#a0b5f4" /* Stylesheet class: border1 */ | |
| 75 | -#define BG1 "#d0d9f4" /* Stylesheet class: bkgnd1 */ | |
| 76 | - | |
| 77 | -/* A red border and background. Use for releases in the timeline. | |
| 78 | -*/ | |
| 79 | -#define BORDER2 "#ec9898" /* Stylesheet class: border2 */ | |
| 80 | -#define BG2 "#f7c0c0" /* Stylesheet class: bkgnd2 */ | |
| 81 | - | |
| 82 | -/* A gray background. Used for column headers in the Wiki Table of Contents | |
| 83 | -** and to highlight ticket properties. | |
| 84 | -*/ | |
| 85 | -#define BG3 "#d0d0d0" /* Stylesheet class: bkgnd3 */ | |
| 86 | - | |
| 87 | -/* A light-gray background. Used for title bar, menus, and rlog alternation | |
| 88 | -*/ | |
| 89 | -#define BG4 "#f0f0f0" /* Stylesheet class: bkgnd4 */ | |
| 90 | - | |
| 91 | -/* A deeper gray background. Used for branches | |
| 92 | -*/ | |
| 93 | -#define BG5 "#dddddd" /* Stylesheet class: bkgnd5 */ | |
| 94 | - | |
| 95 | -/* Default HTML page header */ | |
| 96 | -#define HEADER "<html>\n" \ | |
| 97 | - "<head>\n" \ | |
| 98 | - "<link rel=\"alternate\" type=\"application/rss+xml\"\n" \ | |
| 99 | - " title=\"%N Timeline Feed\" href=\"%B/timeline.rss\">\n" \ | |
| 100 | - "<title>%N: %T</title>\n</head>\n" \ | |
| 101 | - "<body bgcolor=\"white\">" | |
| 102 | - | |
| 103 | -/* Default HTML page footer */ | |
| 104 | -#define FOOTER "<div id=\"footer\"><small><small>\n" \ | |
| 105 | - "<a href=\"about\">Fossil version %V</a>\n" \ | |
| 106 | - "</small></small></div>\n" \ | |
| 107 | - "</body></html>\n" | |
| 108 | - | |
| 109 | 98 | /* In the timeline, check-in messages are truncated at the first space |
| 110 | 99 | ** that is more than MX_CKIN_MSG from the beginning, or at the first |
| 111 | 100 | ** paragraph break that is more than MN_CKIN_MSG from the beginning. |
| 112 | 101 | */ |
| 113 | 102 | #define MN_CKIN_MSG 100 |
| 114 | 103 | #define MX_CKIN_MSG 300 |
| 104 | + | |
| 105 | +/* | |
| 106 | +** The following macros are used to cast pointers to integers and | |
| 107 | +** integers to pointers. The way you do this varies from one compiler | |
| 108 | +** to the next, so we have developed the following set of #if statements | |
| 109 | +** to generate appropriate macros for a wide range of compilers. | |
| 110 | +** | |
| 111 | +** The correct "ANSI" way to do this is to use the intptr_t type. | |
| 112 | +** Unfortunately, that typedef is not available on all compilers, or | |
| 113 | +** if it is available, it requires an #include of specific headers | |
| 114 | +** that vary from one machine to the next. | |
| 115 | +*/ | |
| 116 | +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ | |
| 117 | +# define FOSSIL_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) | |
| 118 | +# define FOSSIL_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) | |
| 119 | +#elif !defined(__GNUC__) /* Works for compilers other than LLVM */ | |
| 120 | +# define FOSSIL_INT_TO_PTR(X) ((void*)&((char*)0)[X]) | |
| 121 | +# define FOSSIL_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) | |
| 122 | +#else /* Generates a warning - but it always works */ | |
| 123 | +# define FOSSIL_INT_TO_PTR(X) ((void*)(X)) | |
| 124 | +# define FOSSIL_PTR_TO_INT(X) ((int)(X)) | |
| 125 | +#endif | |
| 126 | + | |
| 115 | 127 | |
| 116 | 128 | /* Unset the following to disable internationalization code. */ |
| 117 | 129 | #ifndef FOSSIL_I18N |
| 118 | 130 | # define FOSSIL_I18N 1 |
| 119 | 131 | #endif |
| @@ -124,5 +136,7 @@ | ||
| 124 | 136 | #endif |
| 125 | 137 | #ifndef CODESET |
| 126 | 138 | # undef FOSSIL_I18N |
| 127 | 139 | # define FOSSIL_I18N 0 |
| 128 | 140 | #endif |
| 141 | + | |
| 142 | +#endif /* _RC_COMPILE_ */ | |
| 129 | 143 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -25,21 +25,25 @@ | |
| 25 | #ifndef _FILE_OFFSET_BITS |
| 26 | # define _FILE_OFFSET_BITS 64 |
| 27 | #endif |
| 28 | #define _LARGEFILE_SOURCE 1 |
| 29 | |
| 30 | |
| 31 | /* |
| 32 | ** System header files used by all modules |
| 33 | */ |
| 34 | #include <unistd.h> |
| 35 | #include <stdio.h> |
| 36 | #include <stdlib.h> |
| 37 | #include <ctype.h> |
| 38 | #include <string.h> |
| 39 | #include <stdarg.h> |
| 40 | #include <assert.h> |
| 41 | #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 42 | # if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 43 | typedef int socklen_t; |
| 44 | # endif |
| 45 | # ifndef _WIN32 |
| @@ -49,71 +53,79 @@ | |
| 49 | # include <sys/types.h> |
| 50 | # include <signal.h> |
| 51 | # include <pwd.h> |
| 52 | #endif |
| 53 | |
| 54 | #include "sqlite3.h" |
| 55 | |
| 56 | /* |
| 57 | ** Typedef for a 64-bit integer |
| 58 | */ |
| 59 | typedef sqlite_int64 i64; |
| 60 | typedef sqlite_uint64 u64; |
| 61 | |
| 62 | /* |
| 63 | ** Unsigned character type |
| 64 | */ |
| 65 | typedef unsigned char u8; |
| 66 | |
| 67 | /* |
| 68 | ** Standard colors. These colors can also be changed using a stylesheet. |
| 69 | */ |
| 70 | |
| 71 | /* A blue border and background. Used for the title bar and for dates |
| 72 | ** in a timeline. |
| 73 | */ |
| 74 | #define BORDER1 "#a0b5f4" /* Stylesheet class: border1 */ |
| 75 | #define BG1 "#d0d9f4" /* Stylesheet class: bkgnd1 */ |
| 76 | |
| 77 | /* A red border and background. Use for releases in the timeline. |
| 78 | */ |
| 79 | #define BORDER2 "#ec9898" /* Stylesheet class: border2 */ |
| 80 | #define BG2 "#f7c0c0" /* Stylesheet class: bkgnd2 */ |
| 81 | |
| 82 | /* A gray background. Used for column headers in the Wiki Table of Contents |
| 83 | ** and to highlight ticket properties. |
| 84 | */ |
| 85 | #define BG3 "#d0d0d0" /* Stylesheet class: bkgnd3 */ |
| 86 | |
| 87 | /* A light-gray background. Used for title bar, menus, and rlog alternation |
| 88 | */ |
| 89 | #define BG4 "#f0f0f0" /* Stylesheet class: bkgnd4 */ |
| 90 | |
| 91 | /* A deeper gray background. Used for branches |
| 92 | */ |
| 93 | #define BG5 "#dddddd" /* Stylesheet class: bkgnd5 */ |
| 94 | |
| 95 | /* Default HTML page header */ |
| 96 | #define HEADER "<html>\n" \ |
| 97 | "<head>\n" \ |
| 98 | "<link rel=\"alternate\" type=\"application/rss+xml\"\n" \ |
| 99 | " title=\"%N Timeline Feed\" href=\"%B/timeline.rss\">\n" \ |
| 100 | "<title>%N: %T</title>\n</head>\n" \ |
| 101 | "<body bgcolor=\"white\">" |
| 102 | |
| 103 | /* Default HTML page footer */ |
| 104 | #define FOOTER "<div id=\"footer\"><small><small>\n" \ |
| 105 | "<a href=\"about\">Fossil version %V</a>\n" \ |
| 106 | "</small></small></div>\n" \ |
| 107 | "</body></html>\n" |
| 108 | |
| 109 | /* In the timeline, check-in messages are truncated at the first space |
| 110 | ** that is more than MX_CKIN_MSG from the beginning, or at the first |
| 111 | ** paragraph break that is more than MN_CKIN_MSG from the beginning. |
| 112 | */ |
| 113 | #define MN_CKIN_MSG 100 |
| 114 | #define MX_CKIN_MSG 300 |
| 115 | |
| 116 | /* Unset the following to disable internationalization code. */ |
| 117 | #ifndef FOSSIL_I18N |
| 118 | # define FOSSIL_I18N 1 |
| 119 | #endif |
| @@ -124,5 +136,7 @@ | |
| 124 | #endif |
| 125 | #ifndef CODESET |
| 126 | # undef FOSSIL_I18N |
| 127 | # define FOSSIL_I18N 0 |
| 128 | #endif |
| 129 |
| --- src/config.h | |
| +++ src/config.h | |
| @@ -25,21 +25,25 @@ | |
| 25 | #ifndef _FILE_OFFSET_BITS |
| 26 | # define _FILE_OFFSET_BITS 64 |
| 27 | #endif |
| 28 | #define _LARGEFILE_SOURCE 1 |
| 29 | |
| 30 | #ifndef _RC_COMPILE_ |
| 31 | |
| 32 | /* |
| 33 | ** System header files used by all modules |
| 34 | */ |
| 35 | #include <unistd.h> |
| 36 | #include <stdio.h> |
| 37 | #include <stdlib.h> |
| 38 | /* #include <ctype.h> // do not use - causes problems */ |
| 39 | #include <string.h> |
| 40 | #include <stdarg.h> |
| 41 | #include <assert.h> |
| 42 | |
| 43 | #endif |
| 44 | |
| 45 | #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 46 | # if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 47 | typedef int socklen_t; |
| 48 | # endif |
| 49 | # ifndef _WIN32 |
| @@ -49,71 +53,79 @@ | |
| 53 | # include <sys/types.h> |
| 54 | # include <signal.h> |
| 55 | # include <pwd.h> |
| 56 | #endif |
| 57 | |
| 58 | /* |
| 59 | ** Define the compiler variant, used to compile the project |
| 60 | */ |
| 61 | #if !defined(COMPILER_NAME) |
| 62 | # if defined(__DMC__) |
| 63 | # define COMPILER_NAME "dmc" |
| 64 | # elif defined(__POCC__) |
| 65 | # if defined(_M_X64) |
| 66 | # define COMPILER_NAME "pellesc64" |
| 67 | # else |
| 68 | # define COMPILER_NAME "pellesc32" |
| 69 | # endif |
| 70 | # elif defined(_MSC_VER) |
| 71 | # define COMPILER_NAME "msc" |
| 72 | # elif defined(__MINGW32__) |
| 73 | # define COMPILER_NAME "mingw32" |
| 74 | # elif defined(_WIN32) |
| 75 | # define COMPILER_NAME "win32" |
| 76 | # elif defined(__GNUC__) |
| 77 | # define COMPILER_NAME "gcc-" __VERSION__ |
| 78 | # else |
| 79 | # define COMPILER_NAME "unknown" |
| 80 | # endif |
| 81 | #endif |
| 82 | |
| 83 | #ifndef _RC_COMPILE_ |
| 84 | |
| 85 | #include "sqlite3.h" |
| 86 | |
| 87 | /* |
| 88 | ** Typedef for a 64-bit integer |
| 89 | */ |
| 90 | typedef sqlite3_int64 i64; |
| 91 | typedef sqlite3_uint64 u64; |
| 92 | |
| 93 | /* |
| 94 | ** Unsigned character type |
| 95 | */ |
| 96 | typedef unsigned char u8; |
| 97 | |
| 98 | /* In the timeline, check-in messages are truncated at the first space |
| 99 | ** that is more than MX_CKIN_MSG from the beginning, or at the first |
| 100 | ** paragraph break that is more than MN_CKIN_MSG from the beginning. |
| 101 | */ |
| 102 | #define MN_CKIN_MSG 100 |
| 103 | #define MX_CKIN_MSG 300 |
| 104 | |
| 105 | /* |
| 106 | ** The following macros are used to cast pointers to integers and |
| 107 | ** integers to pointers. The way you do this varies from one compiler |
| 108 | ** to the next, so we have developed the following set of #if statements |
| 109 | ** to generate appropriate macros for a wide range of compilers. |
| 110 | ** |
| 111 | ** The correct "ANSI" way to do this is to use the intptr_t type. |
| 112 | ** Unfortunately, that typedef is not available on all compilers, or |
| 113 | ** if it is available, it requires an #include of specific headers |
| 114 | ** that vary from one machine to the next. |
| 115 | */ |
| 116 | #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ |
| 117 | # define FOSSIL_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) |
| 118 | # define FOSSIL_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) |
| 119 | #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ |
| 120 | # define FOSSIL_INT_TO_PTR(X) ((void*)&((char*)0)[X]) |
| 121 | # define FOSSIL_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) |
| 122 | #else /* Generates a warning - but it always works */ |
| 123 | # define FOSSIL_INT_TO_PTR(X) ((void*)(X)) |
| 124 | # define FOSSIL_PTR_TO_INT(X) ((int)(X)) |
| 125 | #endif |
| 126 | |
| 127 | |
| 128 | /* Unset the following to disable internationalization code. */ |
| 129 | #ifndef FOSSIL_I18N |
| 130 | # define FOSSIL_I18N 1 |
| 131 | #endif |
| @@ -124,5 +136,7 @@ | |
| 136 | #endif |
| 137 | #ifndef CODESET |
| 138 | # undef FOSSIL_I18N |
| 139 | # define FOSSIL_I18N 0 |
| 140 | #endif |
| 141 | |
| 142 | #endif /* _RC_COMPILE_ */ |
| 143 |
+2
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -74,10 +74,11 @@ | ||
| 74 | 74 | { "footer", CONFIGSET_SKIN }, |
| 75 | 75 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 76 | 76 | { "logo-image", CONFIGSET_SKIN }, |
| 77 | 77 | { "project-name", CONFIGSET_PROJ }, |
| 78 | 78 | { "project-description", CONFIGSET_PROJ }, |
| 79 | + { "manifest", CONFIGSET_PROJ }, | |
| 79 | 80 | { "index-page", CONFIGSET_SKIN }, |
| 80 | 81 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 81 | 82 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 82 | 83 | { "ticket-table", CONFIGSET_TKT }, |
| 83 | 84 | { "ticket-common", CONFIGSET_TKT }, |
| @@ -454,11 +455,11 @@ | ||
| 454 | 455 | }else{ |
| 455 | 456 | zServer = db_get("last-sync-url", 0); |
| 456 | 457 | if( zServer==0 ){ |
| 457 | 458 | fossil_fatal("no server specified"); |
| 458 | 459 | } |
| 459 | - zPw = db_get("last-sync-pw", 0); | |
| 460 | + zPw = unobscure(db_get("last-sync-pw", 0)); | |
| 460 | 461 | } |
| 461 | 462 | url_parse(zServer); |
| 462 | 463 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 463 | 464 | user_select(); |
| 464 | 465 | url_enable_proxy("via proxy: "); |
| 465 | 466 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -74,10 +74,11 @@ | |
| 74 | { "footer", CONFIGSET_SKIN }, |
| 75 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 76 | { "logo-image", CONFIGSET_SKIN }, |
| 77 | { "project-name", CONFIGSET_PROJ }, |
| 78 | { "project-description", CONFIGSET_PROJ }, |
| 79 | { "index-page", CONFIGSET_SKIN }, |
| 80 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 81 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 82 | { "ticket-table", CONFIGSET_TKT }, |
| 83 | { "ticket-common", CONFIGSET_TKT }, |
| @@ -454,11 +455,11 @@ | |
| 454 | }else{ |
| 455 | zServer = db_get("last-sync-url", 0); |
| 456 | if( zServer==0 ){ |
| 457 | fossil_fatal("no server specified"); |
| 458 | } |
| 459 | zPw = db_get("last-sync-pw", 0); |
| 460 | } |
| 461 | url_parse(zServer); |
| 462 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 463 | user_select(); |
| 464 | url_enable_proxy("via proxy: "); |
| 465 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -74,10 +74,11 @@ | |
| 74 | { "footer", CONFIGSET_SKIN }, |
| 75 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 76 | { "logo-image", CONFIGSET_SKIN }, |
| 77 | { "project-name", CONFIGSET_PROJ }, |
| 78 | { "project-description", CONFIGSET_PROJ }, |
| 79 | { "manifest", CONFIGSET_PROJ }, |
| 80 | { "index-page", CONFIGSET_SKIN }, |
| 81 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 82 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 83 | { "ticket-table", CONFIGSET_TKT }, |
| 84 | { "ticket-common", CONFIGSET_TKT }, |
| @@ -454,11 +455,11 @@ | |
| 455 | }else{ |
| 456 | zServer = db_get("last-sync-url", 0); |
| 457 | if( zServer==0 ){ |
| 458 | fossil_fatal("no server specified"); |
| 459 | } |
| 460 | zPw = unobscure(db_get("last-sync-pw", 0)); |
| 461 | } |
| 462 | url_parse(zServer); |
| 463 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 464 | user_select(); |
| 465 | url_enable_proxy("via proxy: "); |
| 466 |
+64
-13
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -80,13 +80,12 @@ | ||
| 80 | 80 | if( contentCache.n>500 || contentCache.szTotal>50000000 ){ |
| 81 | 81 | content_cache_expire_oldest(); |
| 82 | 82 | } |
| 83 | 83 | if( contentCache.n>=contentCache.nAlloc ){ |
| 84 | 84 | contentCache.nAlloc = contentCache.nAlloc*2 + 10; |
| 85 | - contentCache.a = realloc(contentCache.a, | |
| 85 | + contentCache.a = fossil_realloc(contentCache.a, | |
| 86 | 86 | contentCache.nAlloc*sizeof(contentCache.a[0])); |
| 87 | - if( contentCache.a==0 ) fossil_panic("out of memory"); | |
| 88 | 87 | } |
| 89 | 88 | p = &contentCache.a[contentCache.n++]; |
| 90 | 89 | p->rid = rid; |
| 91 | 90 | p->age = contentCache.nextAge++; |
| 92 | 91 | contentCache.szTotal += blob_size(pBlob); |
| @@ -240,22 +239,20 @@ | ||
| 240 | 239 | int nAlloc = 10; |
| 241 | 240 | int *a = 0; |
| 242 | 241 | int mx; |
| 243 | 242 | Blob delta, next; |
| 244 | 243 | |
| 245 | - a = malloc( sizeof(a[0])*nAlloc ); | |
| 246 | - if( a==0 ) fossil_panic("out of memory"); | |
| 244 | + a = fossil_malloc( sizeof(a[0])*nAlloc ); | |
| 247 | 245 | a[0] = rid; |
| 248 | 246 | a[1] = nextRid; |
| 249 | 247 | n = 1; |
| 250 | 248 | while( !bag_find(&contentCache.inCache, nextRid) |
| 251 | 249 | && (nextRid = findSrcid(nextRid))>0 ){ |
| 252 | 250 | n++; |
| 253 | 251 | if( n>=nAlloc ){ |
| 254 | 252 | nAlloc = nAlloc*2 + 10; |
| 255 | - a = realloc(a, nAlloc*sizeof(a[0])); | |
| 256 | - if( a==0 ) fossil_panic("out of memory"); | |
| 253 | + a = fossil_realloc(a, nAlloc*sizeof(a[0])); | |
| 257 | 254 | } |
| 258 | 255 | a[n] = nextRid; |
| 259 | 256 | } |
| 260 | 257 | mx = n; |
| 261 | 258 | rc = content_get(a[n], pBlob); |
| @@ -284,25 +281,29 @@ | ||
| 284 | 281 | } |
| 285 | 282 | return rc; |
| 286 | 283 | } |
| 287 | 284 | |
| 288 | 285 | /* |
| 289 | -** COMMAND: artifact | |
| 286 | +** COMMAND: artifact | |
| 290 | 287 | ** |
| 291 | -** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? | |
| 288 | +** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? | |
| 292 | 289 | ** |
| 293 | 290 | ** Extract an artifact by its SHA1 hash and write the results on |
| 294 | 291 | ** standard output, or if the optional 4th argument is given, in |
| 295 | 292 | ** the named output file. |
| 293 | +** | |
| 294 | +** Options: | |
| 295 | +** | |
| 296 | +** -R|--repository FILE Extract artifacts from repository FILE | |
| 296 | 297 | */ |
| 297 | 298 | void artifact_cmd(void){ |
| 298 | 299 | int rid; |
| 299 | 300 | Blob content; |
| 300 | 301 | const char *zFile; |
| 301 | - if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?"); | |
| 302 | + db_find_and_open_repository(1); | |
| 303 | + if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); | |
| 302 | 304 | zFile = g.argc==4 ? g.argv[3] : "-"; |
| 303 | - db_must_be_within_tree(); | |
| 304 | 305 | rid = name_to_rid(g.argv[2]); |
| 305 | 306 | content_get(rid, &content); |
| 306 | 307 | blob_write_to_file(&content, zFile); |
| 307 | 308 | } |
| 308 | 309 | |
| @@ -324,50 +325,100 @@ | ||
| 324 | 325 | db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); |
| 325 | 326 | blob_uncompress(&content, &content); |
| 326 | 327 | blob_write_to_file(&content, zFile); |
| 327 | 328 | } |
| 328 | 329 | |
| 330 | +/* | |
| 331 | +** The following flag is set to disable the automatic calls to | |
| 332 | +** manifest_crosslink() when a record is dephantomized. This | |
| 333 | +** flag can be set (for example) when doing a clone when we know | |
| 334 | +** that rebuild will be run over all records at the conclusion | |
| 335 | +** of the operation. | |
| 336 | +*/ | |
| 337 | +static int ignoreDephantomizations = 0; | |
| 338 | + | |
| 329 | 339 | /* |
| 330 | 340 | ** When a record is converted from a phantom to a real record, |
| 331 | 341 | ** if that record has other records that are derived by delta, |
| 332 | 342 | ** then call manifest_crosslink() on those other records. |
| 343 | +** | |
| 344 | +** If the formerly phantom record or any of the other records | |
| 345 | +** derived by delta from the former phantom are a baseline manifest, | |
| 346 | +** then also invoke manifest_crosslink() on the delta-manifests | |
| 347 | +** associated with that baseline. | |
| 333 | 348 | ** |
| 334 | 349 | ** Tail recursion is used to minimize stack depth. |
| 335 | 350 | */ |
| 336 | 351 | void after_dephantomize(int rid, int linkFlag){ |
| 337 | 352 | Stmt q; |
| 338 | 353 | int nChildAlloc = 0; |
| 339 | 354 | int *aChild = 0; |
| 355 | + Blob content; | |
| 340 | 356 | |
| 357 | + if( ignoreDephantomizations ) return; | |
| 341 | 358 | while( rid ){ |
| 342 | 359 | int nChildUsed = 0; |
| 343 | 360 | int i; |
| 361 | + | |
| 362 | + /* Parse the object rid itself */ | |
| 344 | 363 | if( linkFlag ){ |
| 345 | - Blob content; | |
| 346 | 364 | content_get(rid, &content); |
| 347 | 365 | manifest_crosslink(rid, &content); |
| 348 | 366 | blob_reset(&content); |
| 349 | 367 | } |
| 368 | + | |
| 369 | + /* Parse all delta-manifests that depend on baseline-manifest rid */ | |
| 370 | + db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); | |
| 371 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 372 | + int child = db_column_int(&q, 0); | |
| 373 | + if( nChildUsed>=nChildAlloc ){ | |
| 374 | + nChildAlloc = nChildAlloc*2 + 10; | |
| 375 | + aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); | |
| 376 | + } | |
| 377 | + aChild[nChildUsed++] = child; | |
| 378 | + } | |
| 379 | + db_finalize(&q); | |
| 380 | + for(i=0; i<nChildUsed; i++){ | |
| 381 | + content_get(aChild[i], &content); | |
| 382 | + manifest_crosslink(aChild[i], &content); | |
| 383 | + blob_reset(&content); | |
| 384 | + } | |
| 385 | + if( nChildUsed ){ | |
| 386 | + db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); | |
| 387 | + } | |
| 388 | + | |
| 389 | + /* Recursively dephantomize all artifacts that are derived by | |
| 390 | + ** delta from artifact rid */ | |
| 391 | + nChildUsed = 0; | |
| 350 | 392 | db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
| 351 | 393 | while( db_step(&q)==SQLITE_ROW ){ |
| 352 | 394 | int child = db_column_int(&q, 0); |
| 353 | 395 | if( nChildUsed>=nChildAlloc ){ |
| 354 | 396 | nChildAlloc = nChildAlloc*2 + 10; |
| 355 | - aChild = realloc(aChild, nChildAlloc*sizeof(aChild)); | |
| 356 | - if( aChild==0 ) fossil_panic("out of memory"); | |
| 397 | + aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); | |
| 357 | 398 | } |
| 358 | 399 | aChild[nChildUsed++] = child; |
| 359 | 400 | } |
| 360 | 401 | db_finalize(&q); |
| 361 | 402 | for(i=1; i<nChildUsed; i++){ |
| 362 | 403 | after_dephantomize(aChild[i], 1); |
| 363 | 404 | } |
| 405 | + | |
| 406 | + /* Tail recursion for the common case where only a single artifact | |
| 407 | + ** is derived by delta from rid... */ | |
| 364 | 408 | rid = nChildUsed>0 ? aChild[0] : 0; |
| 365 | 409 | linkFlag = 1; |
| 366 | 410 | } |
| 367 | 411 | free(aChild); |
| 368 | 412 | } |
| 413 | + | |
| 414 | +/* | |
| 415 | +** Turn dephantomization processing on or off. | |
| 416 | +*/ | |
| 417 | +void content_enable_dephantomize(int onoff){ | |
| 418 | + ignoreDephantomizations = !onoff; | |
| 419 | +} | |
| 369 | 420 | |
| 370 | 421 | /* |
| 371 | 422 | ** Write content into the database. Return the record ID. If the |
| 372 | 423 | ** content is already in the database, just return the record ID. |
| 373 | 424 | ** |
| 374 | 425 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -80,13 +80,12 @@ | |
| 80 | if( contentCache.n>500 || contentCache.szTotal>50000000 ){ |
| 81 | content_cache_expire_oldest(); |
| 82 | } |
| 83 | if( contentCache.n>=contentCache.nAlloc ){ |
| 84 | contentCache.nAlloc = contentCache.nAlloc*2 + 10; |
| 85 | contentCache.a = realloc(contentCache.a, |
| 86 | contentCache.nAlloc*sizeof(contentCache.a[0])); |
| 87 | if( contentCache.a==0 ) fossil_panic("out of memory"); |
| 88 | } |
| 89 | p = &contentCache.a[contentCache.n++]; |
| 90 | p->rid = rid; |
| 91 | p->age = contentCache.nextAge++; |
| 92 | contentCache.szTotal += blob_size(pBlob); |
| @@ -240,22 +239,20 @@ | |
| 240 | int nAlloc = 10; |
| 241 | int *a = 0; |
| 242 | int mx; |
| 243 | Blob delta, next; |
| 244 | |
| 245 | a = malloc( sizeof(a[0])*nAlloc ); |
| 246 | if( a==0 ) fossil_panic("out of memory"); |
| 247 | a[0] = rid; |
| 248 | a[1] = nextRid; |
| 249 | n = 1; |
| 250 | while( !bag_find(&contentCache.inCache, nextRid) |
| 251 | && (nextRid = findSrcid(nextRid))>0 ){ |
| 252 | n++; |
| 253 | if( n>=nAlloc ){ |
| 254 | nAlloc = nAlloc*2 + 10; |
| 255 | a = realloc(a, nAlloc*sizeof(a[0])); |
| 256 | if( a==0 ) fossil_panic("out of memory"); |
| 257 | } |
| 258 | a[n] = nextRid; |
| 259 | } |
| 260 | mx = n; |
| 261 | rc = content_get(a[n], pBlob); |
| @@ -284,25 +281,29 @@ | |
| 284 | } |
| 285 | return rc; |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | ** COMMAND: artifact |
| 290 | ** |
| 291 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? |
| 292 | ** |
| 293 | ** Extract an artifact by its SHA1 hash and write the results on |
| 294 | ** standard output, or if the optional 4th argument is given, in |
| 295 | ** the named output file. |
| 296 | */ |
| 297 | void artifact_cmd(void){ |
| 298 | int rid; |
| 299 | Blob content; |
| 300 | const char *zFile; |
| 301 | if( g.argc!=4 && g.argc!=3 ) usage("RECORDID ?FILENAME?"); |
| 302 | zFile = g.argc==4 ? g.argv[3] : "-"; |
| 303 | db_must_be_within_tree(); |
| 304 | rid = name_to_rid(g.argv[2]); |
| 305 | content_get(rid, &content); |
| 306 | blob_write_to_file(&content, zFile); |
| 307 | } |
| 308 | |
| @@ -324,50 +325,100 @@ | |
| 324 | db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); |
| 325 | blob_uncompress(&content, &content); |
| 326 | blob_write_to_file(&content, zFile); |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | ** When a record is converted from a phantom to a real record, |
| 331 | ** if that record has other records that are derived by delta, |
| 332 | ** then call manifest_crosslink() on those other records. |
| 333 | ** |
| 334 | ** Tail recursion is used to minimize stack depth. |
| 335 | */ |
| 336 | void after_dephantomize(int rid, int linkFlag){ |
| 337 | Stmt q; |
| 338 | int nChildAlloc = 0; |
| 339 | int *aChild = 0; |
| 340 | |
| 341 | while( rid ){ |
| 342 | int nChildUsed = 0; |
| 343 | int i; |
| 344 | if( linkFlag ){ |
| 345 | Blob content; |
| 346 | content_get(rid, &content); |
| 347 | manifest_crosslink(rid, &content); |
| 348 | blob_reset(&content); |
| 349 | } |
| 350 | db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
| 351 | while( db_step(&q)==SQLITE_ROW ){ |
| 352 | int child = db_column_int(&q, 0); |
| 353 | if( nChildUsed>=nChildAlloc ){ |
| 354 | nChildAlloc = nChildAlloc*2 + 10; |
| 355 | aChild = realloc(aChild, nChildAlloc*sizeof(aChild)); |
| 356 | if( aChild==0 ) fossil_panic("out of memory"); |
| 357 | } |
| 358 | aChild[nChildUsed++] = child; |
| 359 | } |
| 360 | db_finalize(&q); |
| 361 | for(i=1; i<nChildUsed; i++){ |
| 362 | after_dephantomize(aChild[i], 1); |
| 363 | } |
| 364 | rid = nChildUsed>0 ? aChild[0] : 0; |
| 365 | linkFlag = 1; |
| 366 | } |
| 367 | free(aChild); |
| 368 | } |
| 369 | |
| 370 | /* |
| 371 | ** Write content into the database. Return the record ID. If the |
| 372 | ** content is already in the database, just return the record ID. |
| 373 | ** |
| 374 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -80,13 +80,12 @@ | |
| 80 | if( contentCache.n>500 || contentCache.szTotal>50000000 ){ |
| 81 | content_cache_expire_oldest(); |
| 82 | } |
| 83 | if( contentCache.n>=contentCache.nAlloc ){ |
| 84 | contentCache.nAlloc = contentCache.nAlloc*2 + 10; |
| 85 | contentCache.a = fossil_realloc(contentCache.a, |
| 86 | contentCache.nAlloc*sizeof(contentCache.a[0])); |
| 87 | } |
| 88 | p = &contentCache.a[contentCache.n++]; |
| 89 | p->rid = rid; |
| 90 | p->age = contentCache.nextAge++; |
| 91 | contentCache.szTotal += blob_size(pBlob); |
| @@ -240,22 +239,20 @@ | |
| 239 | int nAlloc = 10; |
| 240 | int *a = 0; |
| 241 | int mx; |
| 242 | Blob delta, next; |
| 243 | |
| 244 | a = fossil_malloc( sizeof(a[0])*nAlloc ); |
| 245 | a[0] = rid; |
| 246 | a[1] = nextRid; |
| 247 | n = 1; |
| 248 | while( !bag_find(&contentCache.inCache, nextRid) |
| 249 | && (nextRid = findSrcid(nextRid))>0 ){ |
| 250 | n++; |
| 251 | if( n>=nAlloc ){ |
| 252 | nAlloc = nAlloc*2 + 10; |
| 253 | a = fossil_realloc(a, nAlloc*sizeof(a[0])); |
| 254 | } |
| 255 | a[n] = nextRid; |
| 256 | } |
| 257 | mx = n; |
| 258 | rc = content_get(a[n], pBlob); |
| @@ -284,25 +281,29 @@ | |
| 281 | } |
| 282 | return rc; |
| 283 | } |
| 284 | |
| 285 | /* |
| 286 | ** COMMAND: artifact |
| 287 | ** |
| 288 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? |
| 289 | ** |
| 290 | ** Extract an artifact by its SHA1 hash and write the results on |
| 291 | ** standard output, or if the optional 4th argument is given, in |
| 292 | ** the named output file. |
| 293 | ** |
| 294 | ** Options: |
| 295 | ** |
| 296 | ** -R|--repository FILE Extract artifacts from repository FILE |
| 297 | */ |
| 298 | void artifact_cmd(void){ |
| 299 | int rid; |
| 300 | Blob content; |
| 301 | const char *zFile; |
| 302 | db_find_and_open_repository(1); |
| 303 | if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); |
| 304 | zFile = g.argc==4 ? g.argv[3] : "-"; |
| 305 | rid = name_to_rid(g.argv[2]); |
| 306 | content_get(rid, &content); |
| 307 | blob_write_to_file(&content, zFile); |
| 308 | } |
| 309 | |
| @@ -324,50 +325,100 @@ | |
| 325 | db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid); |
| 326 | blob_uncompress(&content, &content); |
| 327 | blob_write_to_file(&content, zFile); |
| 328 | } |
| 329 | |
| 330 | /* |
| 331 | ** The following flag is set to disable the automatic calls to |
| 332 | ** manifest_crosslink() when a record is dephantomized. This |
| 333 | ** flag can be set (for example) when doing a clone when we know |
| 334 | ** that rebuild will be run over all records at the conclusion |
| 335 | ** of the operation. |
| 336 | */ |
| 337 | static int ignoreDephantomizations = 0; |
| 338 | |
| 339 | /* |
| 340 | ** When a record is converted from a phantom to a real record, |
| 341 | ** if that record has other records that are derived by delta, |
| 342 | ** then call manifest_crosslink() on those other records. |
| 343 | ** |
| 344 | ** If the formerly phantom record or any of the other records |
| 345 | ** derived by delta from the former phantom are a baseline manifest, |
| 346 | ** then also invoke manifest_crosslink() on the delta-manifests |
| 347 | ** associated with that baseline. |
| 348 | ** |
| 349 | ** Tail recursion is used to minimize stack depth. |
| 350 | */ |
| 351 | void after_dephantomize(int rid, int linkFlag){ |
| 352 | Stmt q; |
| 353 | int nChildAlloc = 0; |
| 354 | int *aChild = 0; |
| 355 | Blob content; |
| 356 | |
| 357 | if( ignoreDephantomizations ) return; |
| 358 | while( rid ){ |
| 359 | int nChildUsed = 0; |
| 360 | int i; |
| 361 | |
| 362 | /* Parse the object rid itself */ |
| 363 | if( linkFlag ){ |
| 364 | content_get(rid, &content); |
| 365 | manifest_crosslink(rid, &content); |
| 366 | blob_reset(&content); |
| 367 | } |
| 368 | |
| 369 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 370 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| 371 | while( db_step(&q)==SQLITE_ROW ){ |
| 372 | int child = db_column_int(&q, 0); |
| 373 | if( nChildUsed>=nChildAlloc ){ |
| 374 | nChildAlloc = nChildAlloc*2 + 10; |
| 375 | aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); |
| 376 | } |
| 377 | aChild[nChildUsed++] = child; |
| 378 | } |
| 379 | db_finalize(&q); |
| 380 | for(i=0; i<nChildUsed; i++){ |
| 381 | content_get(aChild[i], &content); |
| 382 | manifest_crosslink(aChild[i], &content); |
| 383 | blob_reset(&content); |
| 384 | } |
| 385 | if( nChildUsed ){ |
| 386 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 387 | } |
| 388 | |
| 389 | /* Recursively dephantomize all artifacts that are derived by |
| 390 | ** delta from artifact rid */ |
| 391 | nChildUsed = 0; |
| 392 | db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid); |
| 393 | while( db_step(&q)==SQLITE_ROW ){ |
| 394 | int child = db_column_int(&q, 0); |
| 395 | if( nChildUsed>=nChildAlloc ){ |
| 396 | nChildAlloc = nChildAlloc*2 + 10; |
| 397 | aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); |
| 398 | } |
| 399 | aChild[nChildUsed++] = child; |
| 400 | } |
| 401 | db_finalize(&q); |
| 402 | for(i=1; i<nChildUsed; i++){ |
| 403 | after_dephantomize(aChild[i], 1); |
| 404 | } |
| 405 | |
| 406 | /* Tail recursion for the common case where only a single artifact |
| 407 | ** is derived by delta from rid... */ |
| 408 | rid = nChildUsed>0 ? aChild[0] : 0; |
| 409 | linkFlag = 1; |
| 410 | } |
| 411 | free(aChild); |
| 412 | } |
| 413 | |
| 414 | /* |
| 415 | ** Turn dephantomization processing on or off. |
| 416 | */ |
| 417 | void content_enable_dephantomize(int onoff){ |
| 418 | ignoreDephantomizations = !onoff; |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | ** Write content into the database. Return the record ID. If the |
| 423 | ** content is already in the database, just return the record ID. |
| 424 | ** |
| 425 |
M
src/db.c
+32
-10
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1510,25 +1510,27 @@ | ||
| 1510 | 1510 | int width; /* Width of display. 0 for boolean values */ |
| 1511 | 1511 | char const *def; /* Default value */ |
| 1512 | 1512 | }; |
| 1513 | 1513 | #endif /* INTERFACE */ |
| 1514 | 1514 | struct stControlSettings const ctrlSettings[] = { |
| 1515 | - { "auto-captcha", "autocaptcha", 0, "0" }, | |
| 1516 | - { "auto-shun", 0, 0, "1" }, | |
| 1517 | - { "autosync", 0, 0, "0" }, | |
| 1518 | - { "binary-glob", 0, 0, "1" }, | |
| 1519 | - { "clearsign", 0, 0, "0" }, | |
| 1520 | - { "diff-command", 0, 16, "diff" }, | |
| 1521 | - { "dont-push", 0, 0, "0" }, | |
| 1515 | + { "auto-captcha", "autocaptcha", 0, "on" }, | |
| 1516 | + { "auto-shun", 0, 0, "on" }, | |
| 1517 | + { "autosync", 0, 0, "on" }, | |
| 1518 | + { "binary-glob", 0, 32, "" }, | |
| 1519 | + { "clearsign", 0, 0, "off" }, | |
| 1520 | + { "diff-command", 0, 16, "" }, | |
| 1521 | + { "dont-push", 0, 0, "off" }, | |
| 1522 | 1522 | { "editor", 0, 16, "" }, |
| 1523 | 1523 | { "gdiff-command", 0, 16, "gdiff" }, |
| 1524 | 1524 | { "ignore-glob", 0, 40, "" }, |
| 1525 | 1525 | { "http-port", 0, 16, "8080" }, |
| 1526 | - { "localauth", 0, 0, "0" }, | |
| 1527 | - { "mtime-changes", 0, 0, "0" }, | |
| 1526 | + { "localauth", 0, 0, "off" }, | |
| 1527 | + { "manifest", 0, 0, "off" }, | |
| 1528 | + { "mtime-changes", 0, 0, "on" }, | |
| 1528 | 1529 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1529 | 1530 | { "proxy", 0, 32, "off" }, |
| 1531 | + { "repo-cksum", 0, 0, "on" }, | |
| 1530 | 1532 | { "ssh-command", 0, 32, "" }, |
| 1531 | 1533 | { "web-browser", 0, 32, "" }, |
| 1532 | 1534 | { 0,0,0,0 } |
| 1533 | 1535 | }; |
| 1534 | 1536 | |
| @@ -1548,23 +1550,25 @@ | ||
| 1548 | 1550 | ** auto-captcha If enabled, the Login page provides a button to |
| 1549 | 1551 | ** fill in the captcha password. Default: on |
| 1550 | 1552 | ** |
| 1551 | 1553 | ** auto-shun If enabled, automatically pull the shunning list |
| 1552 | 1554 | ** from a server to which the client autosyncs. |
| 1555 | +** Default: on | |
| 1553 | 1556 | ** |
| 1554 | 1557 | ** autosync If enabled, automatically pull prior to commit |
| 1555 | 1558 | ** or update and automatically push after commit or |
| 1556 | 1559 | ** tag or branch creation. If the value is "pullonly" |
| 1557 | 1560 | ** then only pull operations occur automatically. |
| 1561 | +** Default: on | |
| 1558 | 1562 | ** |
| 1559 | 1563 | ** binary-glob The VALUE is a comma-separated list of GLOB patterns |
| 1560 | 1564 | ** that should be treated as binary files for merging |
| 1561 | 1565 | ** purposes. Example: *.xml |
| 1562 | 1566 | ** |
| 1563 | 1567 | ** clearsign When enabled, fossil will attempt to sign all commits |
| 1564 | 1568 | ** with gpg. When disabled (the default), commits will |
| 1565 | -** be unsigned. | |
| 1569 | +** be unsigned. Default: off | |
| 1566 | 1570 | ** |
| 1567 | 1571 | ** diff-command External command to run when performing a diff. |
| 1568 | 1572 | ** If undefined, the internal text diff will be used. |
| 1569 | 1573 | ** |
| 1570 | 1574 | ** dont-push Prevent this repository from pushing from client to |
| @@ -1585,10 +1589,14 @@ | ||
| 1585 | 1589 | ** localauth If enabled, require that HTTP connections from |
| 1586 | 1590 | ** 127.0.0.1 be authenticated by password. If |
| 1587 | 1591 | ** false, all HTTP requests from localhost have |
| 1588 | 1592 | ** unrestricted access to the repository. |
| 1589 | 1593 | ** |
| 1594 | +** manifest If enabled, automatically create files "manifest" and | |
| 1595 | +** "manifest.uuid" in every checkout. The SQLite and | |
| 1596 | +** Fossil repositories both require this. Default: off. | |
| 1597 | +** | |
| 1590 | 1598 | ** mtime-changes Use file modification times (mtimes) to detect when |
| 1591 | 1599 | ** files have been modified. (Default "on".) |
| 1592 | 1600 | ** |
| 1593 | 1601 | ** pgp-command Command used to clear-sign manifests at check-in. |
| 1594 | 1602 | ** The default is "gpg --clearsign -o ". |
| @@ -1595,10 +1603,15 @@ | ||
| 1595 | 1603 | ** |
| 1596 | 1604 | ** proxy URL of the HTTP proxy. If undefined or "off" then |
| 1597 | 1605 | ** the "http_proxy" environment variable is consulted. |
| 1598 | 1606 | ** If the http_proxy environment variable is undefined |
| 1599 | 1607 | ** then a direct HTTP connection is used. |
| 1608 | +** | |
| 1609 | +** repo-cksum Compute checksums over all files in each checkout | |
| 1610 | +** as a double-check of correctness. Defaults to "on". | |
| 1611 | +** Disable on large repositories for a performance | |
| 1612 | +** improvement. | |
| 1600 | 1613 | ** |
| 1601 | 1614 | ** ssh-command Command used to talk to a remote machine with |
| 1602 | 1615 | ** the "ssh://" protocol. |
| 1603 | 1616 | ** |
| 1604 | 1617 | ** web-browser A shell command used to launch your preferred |
| @@ -1622,24 +1635,33 @@ | ||
| 1622 | 1635 | for(i=0; ctrlSettings[i].name; i++){ |
| 1623 | 1636 | print_setting(ctrlSettings[i].name); |
| 1624 | 1637 | } |
| 1625 | 1638 | }else if( g.argc==3 || g.argc==4 ){ |
| 1626 | 1639 | const char *zName = g.argv[2]; |
| 1640 | + int isManifest; | |
| 1627 | 1641 | int n = strlen(zName); |
| 1628 | 1642 | for(i=0; ctrlSettings[i].name; i++){ |
| 1629 | 1643 | if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break; |
| 1630 | 1644 | } |
| 1631 | 1645 | if( !ctrlSettings[i].name ){ |
| 1632 | 1646 | fossil_fatal("no such setting: %s", zName); |
| 1633 | 1647 | } |
| 1648 | + isManifest = strcmp(ctrlSettings[i].name, "manifest")==0; | |
| 1649 | + if( isManifest && globalFlag ){ | |
| 1650 | + fossil_fatal("cannot set 'manifest' globally"); | |
| 1651 | + } | |
| 1634 | 1652 | if( unsetFlag ){ |
| 1635 | 1653 | db_unset(ctrlSettings[i].name, globalFlag); |
| 1636 | 1654 | }else if( g.argc==4 ){ |
| 1637 | 1655 | db_set(ctrlSettings[i].name, g.argv[3], globalFlag); |
| 1638 | 1656 | }else{ |
| 1657 | + isManifest = 0; | |
| 1639 | 1658 | print_setting(ctrlSettings[i].name); |
| 1640 | 1659 | } |
| 1660 | + if( isManifest ){ | |
| 1661 | + manifest_to_disk(db_lget_int("checkout", 0)); | |
| 1662 | + } | |
| 1641 | 1663 | }else{ |
| 1642 | 1664 | usage("?PROPERTY? ?VALUE?"); |
| 1643 | 1665 | } |
| 1644 | 1666 | } |
| 1645 | 1667 | |
| 1646 | 1668 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1510,25 +1510,27 @@ | |
| 1510 | int width; /* Width of display. 0 for boolean values */ |
| 1511 | char const *def; /* Default value */ |
| 1512 | }; |
| 1513 | #endif /* INTERFACE */ |
| 1514 | struct stControlSettings const ctrlSettings[] = { |
| 1515 | { "auto-captcha", "autocaptcha", 0, "0" }, |
| 1516 | { "auto-shun", 0, 0, "1" }, |
| 1517 | { "autosync", 0, 0, "0" }, |
| 1518 | { "binary-glob", 0, 0, "1" }, |
| 1519 | { "clearsign", 0, 0, "0" }, |
| 1520 | { "diff-command", 0, 16, "diff" }, |
| 1521 | { "dont-push", 0, 0, "0" }, |
| 1522 | { "editor", 0, 16, "" }, |
| 1523 | { "gdiff-command", 0, 16, "gdiff" }, |
| 1524 | { "ignore-glob", 0, 40, "" }, |
| 1525 | { "http-port", 0, 16, "8080" }, |
| 1526 | { "localauth", 0, 0, "0" }, |
| 1527 | { "mtime-changes", 0, 0, "0" }, |
| 1528 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1529 | { "proxy", 0, 32, "off" }, |
| 1530 | { "ssh-command", 0, 32, "" }, |
| 1531 | { "web-browser", 0, 32, "" }, |
| 1532 | { 0,0,0,0 } |
| 1533 | }; |
| 1534 | |
| @@ -1548,23 +1550,25 @@ | |
| 1548 | ** auto-captcha If enabled, the Login page provides a button to |
| 1549 | ** fill in the captcha password. Default: on |
| 1550 | ** |
| 1551 | ** auto-shun If enabled, automatically pull the shunning list |
| 1552 | ** from a server to which the client autosyncs. |
| 1553 | ** |
| 1554 | ** autosync If enabled, automatically pull prior to commit |
| 1555 | ** or update and automatically push after commit or |
| 1556 | ** tag or branch creation. If the value is "pullonly" |
| 1557 | ** then only pull operations occur automatically. |
| 1558 | ** |
| 1559 | ** binary-glob The VALUE is a comma-separated list of GLOB patterns |
| 1560 | ** that should be treated as binary files for merging |
| 1561 | ** purposes. Example: *.xml |
| 1562 | ** |
| 1563 | ** clearsign When enabled, fossil will attempt to sign all commits |
| 1564 | ** with gpg. When disabled (the default), commits will |
| 1565 | ** be unsigned. |
| 1566 | ** |
| 1567 | ** diff-command External command to run when performing a diff. |
| 1568 | ** If undefined, the internal text diff will be used. |
| 1569 | ** |
| 1570 | ** dont-push Prevent this repository from pushing from client to |
| @@ -1585,10 +1589,14 @@ | |
| 1585 | ** localauth If enabled, require that HTTP connections from |
| 1586 | ** 127.0.0.1 be authenticated by password. If |
| 1587 | ** false, all HTTP requests from localhost have |
| 1588 | ** unrestricted access to the repository. |
| 1589 | ** |
| 1590 | ** mtime-changes Use file modification times (mtimes) to detect when |
| 1591 | ** files have been modified. (Default "on".) |
| 1592 | ** |
| 1593 | ** pgp-command Command used to clear-sign manifests at check-in. |
| 1594 | ** The default is "gpg --clearsign -o ". |
| @@ -1595,10 +1603,15 @@ | |
| 1595 | ** |
| 1596 | ** proxy URL of the HTTP proxy. If undefined or "off" then |
| 1597 | ** the "http_proxy" environment variable is consulted. |
| 1598 | ** If the http_proxy environment variable is undefined |
| 1599 | ** then a direct HTTP connection is used. |
| 1600 | ** |
| 1601 | ** ssh-command Command used to talk to a remote machine with |
| 1602 | ** the "ssh://" protocol. |
| 1603 | ** |
| 1604 | ** web-browser A shell command used to launch your preferred |
| @@ -1622,24 +1635,33 @@ | |
| 1622 | for(i=0; ctrlSettings[i].name; i++){ |
| 1623 | print_setting(ctrlSettings[i].name); |
| 1624 | } |
| 1625 | }else if( g.argc==3 || g.argc==4 ){ |
| 1626 | const char *zName = g.argv[2]; |
| 1627 | int n = strlen(zName); |
| 1628 | for(i=0; ctrlSettings[i].name; i++){ |
| 1629 | if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break; |
| 1630 | } |
| 1631 | if( !ctrlSettings[i].name ){ |
| 1632 | fossil_fatal("no such setting: %s", zName); |
| 1633 | } |
| 1634 | if( unsetFlag ){ |
| 1635 | db_unset(ctrlSettings[i].name, globalFlag); |
| 1636 | }else if( g.argc==4 ){ |
| 1637 | db_set(ctrlSettings[i].name, g.argv[3], globalFlag); |
| 1638 | }else{ |
| 1639 | print_setting(ctrlSettings[i].name); |
| 1640 | } |
| 1641 | }else{ |
| 1642 | usage("?PROPERTY? ?VALUE?"); |
| 1643 | } |
| 1644 | } |
| 1645 | |
| 1646 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1510,25 +1510,27 @@ | |
| 1510 | int width; /* Width of display. 0 for boolean values */ |
| 1511 | char const *def; /* Default value */ |
| 1512 | }; |
| 1513 | #endif /* INTERFACE */ |
| 1514 | struct stControlSettings const ctrlSettings[] = { |
| 1515 | { "auto-captcha", "autocaptcha", 0, "on" }, |
| 1516 | { "auto-shun", 0, 0, "on" }, |
| 1517 | { "autosync", 0, 0, "on" }, |
| 1518 | { "binary-glob", 0, 32, "" }, |
| 1519 | { "clearsign", 0, 0, "off" }, |
| 1520 | { "diff-command", 0, 16, "" }, |
| 1521 | { "dont-push", 0, 0, "off" }, |
| 1522 | { "editor", 0, 16, "" }, |
| 1523 | { "gdiff-command", 0, 16, "gdiff" }, |
| 1524 | { "ignore-glob", 0, 40, "" }, |
| 1525 | { "http-port", 0, 16, "8080" }, |
| 1526 | { "localauth", 0, 0, "off" }, |
| 1527 | { "manifest", 0, 0, "off" }, |
| 1528 | { "mtime-changes", 0, 0, "on" }, |
| 1529 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1530 | { "proxy", 0, 32, "off" }, |
| 1531 | { "repo-cksum", 0, 0, "on" }, |
| 1532 | { "ssh-command", 0, 32, "" }, |
| 1533 | { "web-browser", 0, 32, "" }, |
| 1534 | { 0,0,0,0 } |
| 1535 | }; |
| 1536 | |
| @@ -1548,23 +1550,25 @@ | |
| 1550 | ** auto-captcha If enabled, the Login page provides a button to |
| 1551 | ** fill in the captcha password. Default: on |
| 1552 | ** |
| 1553 | ** auto-shun If enabled, automatically pull the shunning list |
| 1554 | ** from a server to which the client autosyncs. |
| 1555 | ** Default: on |
| 1556 | ** |
| 1557 | ** autosync If enabled, automatically pull prior to commit |
| 1558 | ** or update and automatically push after commit or |
| 1559 | ** tag or branch creation. If the value is "pullonly" |
| 1560 | ** then only pull operations occur automatically. |
| 1561 | ** Default: on |
| 1562 | ** |
| 1563 | ** binary-glob The VALUE is a comma-separated list of GLOB patterns |
| 1564 | ** that should be treated as binary files for merging |
| 1565 | ** purposes. Example: *.xml |
| 1566 | ** |
| 1567 | ** clearsign When enabled, fossil will attempt to sign all commits |
| 1568 | ** with gpg. When disabled (the default), commits will |
| 1569 | ** be unsigned. Default: off |
| 1570 | ** |
| 1571 | ** diff-command External command to run when performing a diff. |
| 1572 | ** If undefined, the internal text diff will be used. |
| 1573 | ** |
| 1574 | ** dont-push Prevent this repository from pushing from client to |
| @@ -1585,10 +1589,14 @@ | |
| 1589 | ** localauth If enabled, require that HTTP connections from |
| 1590 | ** 127.0.0.1 be authenticated by password. If |
| 1591 | ** false, all HTTP requests from localhost have |
| 1592 | ** unrestricted access to the repository. |
| 1593 | ** |
| 1594 | ** manifest If enabled, automatically create files "manifest" and |
| 1595 | ** "manifest.uuid" in every checkout. The SQLite and |
| 1596 | ** Fossil repositories both require this. Default: off. |
| 1597 | ** |
| 1598 | ** mtime-changes Use file modification times (mtimes) to detect when |
| 1599 | ** files have been modified. (Default "on".) |
| 1600 | ** |
| 1601 | ** pgp-command Command used to clear-sign manifests at check-in. |
| 1602 | ** The default is "gpg --clearsign -o ". |
| @@ -1595,10 +1603,15 @@ | |
| 1603 | ** |
| 1604 | ** proxy URL of the HTTP proxy. If undefined or "off" then |
| 1605 | ** the "http_proxy" environment variable is consulted. |
| 1606 | ** If the http_proxy environment variable is undefined |
| 1607 | ** then a direct HTTP connection is used. |
| 1608 | ** |
| 1609 | ** repo-cksum Compute checksums over all files in each checkout |
| 1610 | ** as a double-check of correctness. Defaults to "on". |
| 1611 | ** Disable on large repositories for a performance |
| 1612 | ** improvement. |
| 1613 | ** |
| 1614 | ** ssh-command Command used to talk to a remote machine with |
| 1615 | ** the "ssh://" protocol. |
| 1616 | ** |
| 1617 | ** web-browser A shell command used to launch your preferred |
| @@ -1622,24 +1635,33 @@ | |
| 1635 | for(i=0; ctrlSettings[i].name; i++){ |
| 1636 | print_setting(ctrlSettings[i].name); |
| 1637 | } |
| 1638 | }else if( g.argc==3 || g.argc==4 ){ |
| 1639 | const char *zName = g.argv[2]; |
| 1640 | int isManifest; |
| 1641 | int n = strlen(zName); |
| 1642 | for(i=0; ctrlSettings[i].name; i++){ |
| 1643 | if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break; |
| 1644 | } |
| 1645 | if( !ctrlSettings[i].name ){ |
| 1646 | fossil_fatal("no such setting: %s", zName); |
| 1647 | } |
| 1648 | isManifest = strcmp(ctrlSettings[i].name, "manifest")==0; |
| 1649 | if( isManifest && globalFlag ){ |
| 1650 | fossil_fatal("cannot set 'manifest' globally"); |
| 1651 | } |
| 1652 | if( unsetFlag ){ |
| 1653 | db_unset(ctrlSettings[i].name, globalFlag); |
| 1654 | }else if( g.argc==4 ){ |
| 1655 | db_set(ctrlSettings[i].name, g.argv[3], globalFlag); |
| 1656 | }else{ |
| 1657 | isManifest = 0; |
| 1658 | print_setting(ctrlSettings[i].name); |
| 1659 | } |
| 1660 | if( isManifest ){ |
| 1661 | manifest_to_disk(db_lget_int("checkout", 0)); |
| 1662 | } |
| 1663 | }else{ |
| 1664 | usage("?PROPERTY? ?VALUE?"); |
| 1665 | } |
| 1666 | } |
| 1667 | |
| 1668 |
+22
-12
| --- src/delta.c | ||
| +++ src/delta.c | ||
| @@ -195,31 +195,38 @@ | ||
| 195 | 195 | /* |
| 196 | 196 | ** Compute a 32-bit checksum on the N-byte buffer. Return the result. |
| 197 | 197 | */ |
| 198 | 198 | static unsigned int checksum(const char *zIn, size_t N){ |
| 199 | 199 | const unsigned char *z = (const unsigned char *)zIn; |
| 200 | - unsigned sum = 0; | |
| 200 | + unsigned sum0 = 0; | |
| 201 | + unsigned sum1 = 0; | |
| 202 | + unsigned sum2 = 0; | |
| 203 | + unsigned sum3 = 0; | |
| 201 | 204 | while(N >= 16){ |
| 202 | - sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24; | |
| 203 | - sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16; | |
| 204 | - sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8; | |
| 205 | - sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); | |
| 205 | + sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); | |
| 206 | + sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); | |
| 207 | + sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); | |
| 208 | + sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]); | |
| 206 | 209 | z += 16; |
| 207 | 210 | N -= 16; |
| 208 | 211 | } |
| 209 | 212 | while(N >= 4){ |
| 210 | - sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3]; | |
| 213 | + sum0 += z[0]; | |
| 214 | + sum1 += z[1]; | |
| 215 | + sum2 += z[2]; | |
| 216 | + sum3 += z[3]; | |
| 211 | 217 | z += 4; |
| 212 | 218 | N -= 4; |
| 213 | 219 | } |
| 220 | + sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); | |
| 214 | 221 | switch(N){ |
| 215 | - case 3: sum += (z[2] << 8); | |
| 216 | - case 2: sum += (z[1] << 16); | |
| 217 | - case 1: sum += (z[0] << 24); | |
| 222 | + case 3: sum3 += (z[2] << 8); | |
| 223 | + case 2: sum3 += (z[1] << 16); | |
| 224 | + case 1: sum3 += (z[0] << 24); | |
| 218 | 225 | default: ; |
| 219 | 226 | } |
| 220 | - return sum; | |
| 227 | + return sum3; | |
| 221 | 228 | } |
| 222 | 229 | |
| 223 | 230 | /* |
| 224 | 231 | ** Create a new delta. |
| 225 | 232 | ** |
| @@ -317,12 +324,11 @@ | ||
| 317 | 324 | |
| 318 | 325 | /* Compute the hash table used to locate matching sections in the |
| 319 | 326 | ** source file. |
| 320 | 327 | */ |
| 321 | 328 | nHash = lenSrc/NHASH; |
| 322 | - collide = malloc( nHash*2*sizeof(int) ); | |
| 323 | - if( collide==0 ) return -1; | |
| 329 | + collide = fossil_malloc( nHash*2*sizeof(int) ); | |
| 324 | 330 | landmark = &collide[nHash]; |
| 325 | 331 | memset(landmark, -1, nHash*sizeof(int)); |
| 326 | 332 | memset(collide, -1, nHash*sizeof(int)); |
| 327 | 333 | for(i=0; i<lenSrc-NHASH; i+=NHASH){ |
| 328 | 334 | int hv; |
| @@ -513,11 +519,13 @@ | ||
| 513 | 519 | int lenDelta, /* Length of the delta */ |
| 514 | 520 | char *zOut /* Write the output into this preallocated buffer */ |
| 515 | 521 | ){ |
| 516 | 522 | unsigned int limit; |
| 517 | 523 | unsigned int total = 0; |
| 524 | +#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST | |
| 518 | 525 | char *zOrigOut = zOut; |
| 526 | +#endif | |
| 519 | 527 | |
| 520 | 528 | limit = getInt(&zDelta, &lenDelta); |
| 521 | 529 | if( *zDelta!='\n' ){ |
| 522 | 530 | /* ERROR: size integer not terminated by "\n" */ |
| 523 | 531 | return -1; |
| @@ -568,14 +576,16 @@ | ||
| 568 | 576 | break; |
| 569 | 577 | } |
| 570 | 578 | case ';': { |
| 571 | 579 | zDelta++; lenDelta--; |
| 572 | 580 | zOut[0] = 0; |
| 581 | +#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST | |
| 573 | 582 | if( cnt!=checksum(zOrigOut, total) ){ |
| 574 | 583 | /* ERROR: bad checksum */ |
| 575 | 584 | return -1; |
| 576 | 585 | } |
| 586 | +#endif | |
| 577 | 587 | if( total!=limit ){ |
| 578 | 588 | /* ERROR: generated size does not match predicted size */ |
| 579 | 589 | return -1; |
| 580 | 590 | } |
| 581 | 591 | return total; |
| 582 | 592 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -195,31 +195,38 @@ | |
| 195 | /* |
| 196 | ** Compute a 32-bit checksum on the N-byte buffer. Return the result. |
| 197 | */ |
| 198 | static unsigned int checksum(const char *zIn, size_t N){ |
| 199 | const unsigned char *z = (const unsigned char *)zIn; |
| 200 | unsigned sum = 0; |
| 201 | while(N >= 16){ |
| 202 | sum += ((unsigned)z[0] + z[4] + z[8] + z[12]) << 24; |
| 203 | sum += ((unsigned)z[1] + z[5] + z[9] + z[13]) << 16; |
| 204 | sum += ((unsigned)z[2] + z[6] + z[10]+ z[14]) << 8; |
| 205 | sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]); |
| 206 | z += 16; |
| 207 | N -= 16; |
| 208 | } |
| 209 | while(N >= 4){ |
| 210 | sum += (z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3]; |
| 211 | z += 4; |
| 212 | N -= 4; |
| 213 | } |
| 214 | switch(N){ |
| 215 | case 3: sum += (z[2] << 8); |
| 216 | case 2: sum += (z[1] << 16); |
| 217 | case 1: sum += (z[0] << 24); |
| 218 | default: ; |
| 219 | } |
| 220 | return sum; |
| 221 | } |
| 222 | |
| 223 | /* |
| 224 | ** Create a new delta. |
| 225 | ** |
| @@ -317,12 +324,11 @@ | |
| 317 | |
| 318 | /* Compute the hash table used to locate matching sections in the |
| 319 | ** source file. |
| 320 | */ |
| 321 | nHash = lenSrc/NHASH; |
| 322 | collide = malloc( nHash*2*sizeof(int) ); |
| 323 | if( collide==0 ) return -1; |
| 324 | landmark = &collide[nHash]; |
| 325 | memset(landmark, -1, nHash*sizeof(int)); |
| 326 | memset(collide, -1, nHash*sizeof(int)); |
| 327 | for(i=0; i<lenSrc-NHASH; i+=NHASH){ |
| 328 | int hv; |
| @@ -513,11 +519,13 @@ | |
| 513 | int lenDelta, /* Length of the delta */ |
| 514 | char *zOut /* Write the output into this preallocated buffer */ |
| 515 | ){ |
| 516 | unsigned int limit; |
| 517 | unsigned int total = 0; |
| 518 | char *zOrigOut = zOut; |
| 519 | |
| 520 | limit = getInt(&zDelta, &lenDelta); |
| 521 | if( *zDelta!='\n' ){ |
| 522 | /* ERROR: size integer not terminated by "\n" */ |
| 523 | return -1; |
| @@ -568,14 +576,16 @@ | |
| 568 | break; |
| 569 | } |
| 570 | case ';': { |
| 571 | zDelta++; lenDelta--; |
| 572 | zOut[0] = 0; |
| 573 | if( cnt!=checksum(zOrigOut, total) ){ |
| 574 | /* ERROR: bad checksum */ |
| 575 | return -1; |
| 576 | } |
| 577 | if( total!=limit ){ |
| 578 | /* ERROR: generated size does not match predicted size */ |
| 579 | return -1; |
| 580 | } |
| 581 | return total; |
| 582 |
| --- src/delta.c | |
| +++ src/delta.c | |
| @@ -195,31 +195,38 @@ | |
| 195 | /* |
| 196 | ** Compute a 32-bit checksum on the N-byte buffer. Return the result. |
| 197 | */ |
| 198 | static unsigned int checksum(const char *zIn, size_t N){ |
| 199 | const unsigned char *z = (const unsigned char *)zIn; |
| 200 | unsigned sum0 = 0; |
| 201 | unsigned sum1 = 0; |
| 202 | unsigned sum2 = 0; |
| 203 | unsigned sum3 = 0; |
| 204 | while(N >= 16){ |
| 205 | sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); |
| 206 | sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); |
| 207 | sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); |
| 208 | sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]); |
| 209 | z += 16; |
| 210 | N -= 16; |
| 211 | } |
| 212 | while(N >= 4){ |
| 213 | sum0 += z[0]; |
| 214 | sum1 += z[1]; |
| 215 | sum2 += z[2]; |
| 216 | sum3 += z[3]; |
| 217 | z += 4; |
| 218 | N -= 4; |
| 219 | } |
| 220 | sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); |
| 221 | switch(N){ |
| 222 | case 3: sum3 += (z[2] << 8); |
| 223 | case 2: sum3 += (z[1] << 16); |
| 224 | case 1: sum3 += (z[0] << 24); |
| 225 | default: ; |
| 226 | } |
| 227 | return sum3; |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** Create a new delta. |
| 232 | ** |
| @@ -317,12 +324,11 @@ | |
| 324 | |
| 325 | /* Compute the hash table used to locate matching sections in the |
| 326 | ** source file. |
| 327 | */ |
| 328 | nHash = lenSrc/NHASH; |
| 329 | collide = fossil_malloc( nHash*2*sizeof(int) ); |
| 330 | landmark = &collide[nHash]; |
| 331 | memset(landmark, -1, nHash*sizeof(int)); |
| 332 | memset(collide, -1, nHash*sizeof(int)); |
| 333 | for(i=0; i<lenSrc-NHASH; i+=NHASH){ |
| 334 | int hv; |
| @@ -513,11 +519,13 @@ | |
| 519 | int lenDelta, /* Length of the delta */ |
| 520 | char *zOut /* Write the output into this preallocated buffer */ |
| 521 | ){ |
| 522 | unsigned int limit; |
| 523 | unsigned int total = 0; |
| 524 | #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST |
| 525 | char *zOrigOut = zOut; |
| 526 | #endif |
| 527 | |
| 528 | limit = getInt(&zDelta, &lenDelta); |
| 529 | if( *zDelta!='\n' ){ |
| 530 | /* ERROR: size integer not terminated by "\n" */ |
| 531 | return -1; |
| @@ -568,14 +576,16 @@ | |
| 576 | break; |
| 577 | } |
| 578 | case ';': { |
| 579 | zDelta++; lenDelta--; |
| 580 | zOut[0] = 0; |
| 581 | #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST |
| 582 | if( cnt!=checksum(zOrigOut, total) ){ |
| 583 | /* ERROR: bad checksum */ |
| 584 | return -1; |
| 585 | } |
| 586 | #endif |
| 587 | if( total!=limit ){ |
| 588 | /* ERROR: generated size does not match predicted size */ |
| 589 | return -1; |
| 590 | } |
| 591 | return total; |
| 592 |
+8
-13
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -96,20 +96,23 @@ | ||
| 96 | 96 | } |
| 97 | 97 | } |
| 98 | 98 | if( j>LENGTH_MASK ){ |
| 99 | 99 | return 0; |
| 100 | 100 | } |
| 101 | - a = malloc( nLine*sizeof(a[0]) ); | |
| 102 | - if( a==0 ) fossil_panic("out of memory"); | |
| 101 | + a = fossil_malloc( nLine*sizeof(a[0]) ); | |
| 103 | 102 | memset(a, 0, nLine*sizeof(a[0]) ); |
| 103 | + if( n==0 ){ | |
| 104 | + *pnLine = 0; | |
| 105 | + return a; | |
| 106 | + } | |
| 104 | 107 | |
| 105 | 108 | /* Fill in the array */ |
| 106 | 109 | for(i=0; i<nLine; i++){ |
| 107 | 110 | a[i].z = z; |
| 108 | 111 | for(j=0; z[j] && z[j]!='\n'; j++){} |
| 109 | 112 | k = j; |
| 110 | - while( ignoreWS && k>0 && isspace(z[k-1]) ){ k--; } | |
| 113 | + while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; } | |
| 111 | 114 | for(h=0, x=0; x<k; x++){ |
| 112 | 115 | h = h ^ (h<<2) ^ z[x]; |
| 113 | 116 | } |
| 114 | 117 | a[i].h = h = (h<<LENGTH_MASK_SZ) | k;; |
| 115 | 118 | h2 = h % nLine; |
| @@ -141,18 +144,11 @@ | ||
| 141 | 144 | |
| 142 | 145 | /* |
| 143 | 146 | ** Expand the size of aEdit[] array to hold nEdit elements. |
| 144 | 147 | */ |
| 145 | 148 | static void expandEdit(DContext *p, int nEdit){ |
| 146 | - int *a; | |
| 147 | - a = realloc(p->aEdit, nEdit*sizeof(int)); | |
| 148 | - if( a==0 ){ | |
| 149 | - free( p->aEdit ); | |
| 150 | - p->nEdit = 0; | |
| 151 | - nEdit = 0; | |
| 152 | - } | |
| 153 | - p->aEdit = a; | |
| 149 | + p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); | |
| 154 | 150 | p->nEditAlloc = nEdit; |
| 155 | 151 | } |
| 156 | 152 | |
| 157 | 153 | /* |
| 158 | 154 | ** Append a new COPY/DELETE/INSERT triple. |
| @@ -645,12 +641,11 @@ | ||
| 645 | 641 | memset(p, 0, sizeof(*p)); |
| 646 | 642 | p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1); |
| 647 | 643 | if( p->c.aTo==0 ){ |
| 648 | 644 | return 1; |
| 649 | 645 | } |
| 650 | - p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo ); | |
| 651 | - if( p->aOrig==0 ) fossil_panic("out of memory"); | |
| 646 | + p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); | |
| 652 | 647 | for(i=0; i<p->c.nTo; i++){ |
| 653 | 648 | p->aOrig[i].z = p->c.aTo[i].z; |
| 654 | 649 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 655 | 650 | p->aOrig[i].zSrc = 0; |
| 656 | 651 | } |
| 657 | 652 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -96,20 +96,23 @@ | |
| 96 | } |
| 97 | } |
| 98 | if( j>LENGTH_MASK ){ |
| 99 | return 0; |
| 100 | } |
| 101 | a = malloc( nLine*sizeof(a[0]) ); |
| 102 | if( a==0 ) fossil_panic("out of memory"); |
| 103 | memset(a, 0, nLine*sizeof(a[0]) ); |
| 104 | |
| 105 | /* Fill in the array */ |
| 106 | for(i=0; i<nLine; i++){ |
| 107 | a[i].z = z; |
| 108 | for(j=0; z[j] && z[j]!='\n'; j++){} |
| 109 | k = j; |
| 110 | while( ignoreWS && k>0 && isspace(z[k-1]) ){ k--; } |
| 111 | for(h=0, x=0; x<k; x++){ |
| 112 | h = h ^ (h<<2) ^ z[x]; |
| 113 | } |
| 114 | a[i].h = h = (h<<LENGTH_MASK_SZ) | k;; |
| 115 | h2 = h % nLine; |
| @@ -141,18 +144,11 @@ | |
| 141 | |
| 142 | /* |
| 143 | ** Expand the size of aEdit[] array to hold nEdit elements. |
| 144 | */ |
| 145 | static void expandEdit(DContext *p, int nEdit){ |
| 146 | int *a; |
| 147 | a = realloc(p->aEdit, nEdit*sizeof(int)); |
| 148 | if( a==0 ){ |
| 149 | free( p->aEdit ); |
| 150 | p->nEdit = 0; |
| 151 | nEdit = 0; |
| 152 | } |
| 153 | p->aEdit = a; |
| 154 | p->nEditAlloc = nEdit; |
| 155 | } |
| 156 | |
| 157 | /* |
| 158 | ** Append a new COPY/DELETE/INSERT triple. |
| @@ -645,12 +641,11 @@ | |
| 645 | memset(p, 0, sizeof(*p)); |
| 646 | p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1); |
| 647 | if( p->c.aTo==0 ){ |
| 648 | return 1; |
| 649 | } |
| 650 | p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo ); |
| 651 | if( p->aOrig==0 ) fossil_panic("out of memory"); |
| 652 | for(i=0; i<p->c.nTo; i++){ |
| 653 | p->aOrig[i].z = p->c.aTo[i].z; |
| 654 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 655 | p->aOrig[i].zSrc = 0; |
| 656 | } |
| 657 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -96,20 +96,23 @@ | |
| 96 | } |
| 97 | } |
| 98 | if( j>LENGTH_MASK ){ |
| 99 | return 0; |
| 100 | } |
| 101 | a = fossil_malloc( nLine*sizeof(a[0]) ); |
| 102 | memset(a, 0, nLine*sizeof(a[0]) ); |
| 103 | if( n==0 ){ |
| 104 | *pnLine = 0; |
| 105 | return a; |
| 106 | } |
| 107 | |
| 108 | /* Fill in the array */ |
| 109 | for(i=0; i<nLine; i++){ |
| 110 | a[i].z = z; |
| 111 | for(j=0; z[j] && z[j]!='\n'; j++){} |
| 112 | k = j; |
| 113 | while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; } |
| 114 | for(h=0, x=0; x<k; x++){ |
| 115 | h = h ^ (h<<2) ^ z[x]; |
| 116 | } |
| 117 | a[i].h = h = (h<<LENGTH_MASK_SZ) | k;; |
| 118 | h2 = h % nLine; |
| @@ -141,18 +144,11 @@ | |
| 144 | |
| 145 | /* |
| 146 | ** Expand the size of aEdit[] array to hold nEdit elements. |
| 147 | */ |
| 148 | static void expandEdit(DContext *p, int nEdit){ |
| 149 | p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); |
| 150 | p->nEditAlloc = nEdit; |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | ** Append a new COPY/DELETE/INSERT triple. |
| @@ -645,12 +641,11 @@ | |
| 641 | memset(p, 0, sizeof(*p)); |
| 642 | p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1); |
| 643 | if( p->c.aTo==0 ){ |
| 644 | return 1; |
| 645 | } |
| 646 | p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); |
| 647 | for(i=0; i<p->c.nTo; i++){ |
| 648 | p->aOrig[i].z = p->c.aTo[i].z; |
| 649 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 650 | p->aOrig[i].zSrc = 0; |
| 651 | } |
| 652 |
+121
-72
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -20,28 +20,14 @@ | ||
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "diffcmd.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | 24 | /* |
| 25 | -** This function implements a cross-platform "system()" interface. | |
| 26 | -*/ | |
| 27 | -int portable_system(const char *zOrigCmd){ | |
| 28 | - int rc; | |
| 29 | -#if defined(_WIN32) | |
| 30 | - /* On windows, we have to put double-quotes around the entire command. | |
| 31 | - ** Who knows why - this is just the way windows works. | |
| 32 | - */ | |
| 33 | - char *zNewCmd = mprintf("\"%s\"", zOrigCmd); | |
| 34 | - rc = system(zNewCmd); | |
| 35 | - free(zNewCmd); | |
| 36 | -#else | |
| 37 | - /* On unix, evaluate the command directly. | |
| 38 | - */ | |
| 39 | - rc = system(zOrigCmd); | |
| 40 | -#endif | |
| 41 | - return rc; | |
| 42 | -} | |
| 25 | +** Diff option flags | |
| 26 | +*/ | |
| 27 | +#define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */ | |
| 28 | +#define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */ | |
| 43 | 29 | |
| 44 | 30 | /* |
| 45 | 31 | ** Show the difference between two files, one in memory and one on disk. |
| 46 | 32 | ** |
| 47 | 33 | ** The difference is the set of edits needed to transform pFile1 into |
| @@ -53,24 +39,30 @@ | ||
| 53 | 39 | static void diff_file( |
| 54 | 40 | Blob *pFile1, /* In memory content to compare from */ |
| 55 | 41 | const char *zFile2, /* On disk content to compare to */ |
| 56 | 42 | const char *zName, /* Display name of the file */ |
| 57 | 43 | const char *zDiffCmd, /* Command for comparison */ |
| 58 | - int ignoreEolWs /* Ignore whitespace at end of lines */ | |
| 44 | + int ignoreEolWs /* Ignore whitespace at end of line */ | |
| 59 | 45 | ){ |
| 60 | 46 | if( zDiffCmd==0 ){ |
| 61 | - Blob out; /* Diff output text */ | |
| 62 | - Blob file2; /* Content of zFile2 */ | |
| 47 | + Blob out; /* Diff output text */ | |
| 48 | + Blob file2; /* Content of zFile2 */ | |
| 49 | + const char *zName2; /* Name of zFile2 for display */ | |
| 63 | 50 | |
| 64 | 51 | /* Read content of zFile2 into memory */ |
| 65 | 52 | blob_zero(&file2); |
| 66 | - blob_read_from_file(&file2, zFile2); | |
| 53 | + if( file_size(zFile2)<0 ){ | |
| 54 | + zName2 = "/dev/null"; | |
| 55 | + }else{ | |
| 56 | + blob_read_from_file(&file2, zFile2); | |
| 57 | + zName2 = zName; | |
| 58 | + } | |
| 67 | 59 | |
| 68 | 60 | /* Compute and output the differences */ |
| 69 | 61 | blob_zero(&out); |
| 70 | 62 | text_diff(pFile1, &file2, &out, 5, ignoreEolWs); |
| 71 | - printf("--- %s\n+++ %s\n", zName, zName); | |
| 63 | + printf("--- %s\n+++ %s\n", zName, zName2); | |
| 72 | 64 | printf("%s\n", blob_str(&out)); |
| 73 | 65 | |
| 74 | 66 | /* Release memory resources */ |
| 75 | 67 | blob_reset(&file2); |
| 76 | 68 | blob_reset(&out); |
| @@ -94,11 +86,11 @@ | ||
| 94 | 86 | shell_escape(&cmd, blob_str(&nameFile1)); |
| 95 | 87 | blob_append(&cmd, " ", 1); |
| 96 | 88 | shell_escape(&cmd, zFile2); |
| 97 | 89 | |
| 98 | 90 | /* Run the external diff command */ |
| 99 | - portable_system(blob_str(&cmd)); | |
| 91 | + fossil_system(blob_str(&cmd)); | |
| 100 | 92 | |
| 101 | 93 | /* Delete the temporary file and clean up memory used */ |
| 102 | 94 | unlink(blob_str(&nameFile1)); |
| 103 | 95 | blob_reset(&nameFile1); |
| 104 | 96 | blob_reset(&cmd); |
| @@ -148,11 +140,11 @@ | ||
| 148 | 140 | shell_escape(&cmd, zTemp1); |
| 149 | 141 | blob_append(&cmd, " ", 1); |
| 150 | 142 | shell_escape(&cmd, zTemp2); |
| 151 | 143 | |
| 152 | 144 | /* Run the external diff command */ |
| 153 | - portable_system(blob_str(&cmd)); | |
| 145 | + fossil_system(blob_str(&cmd)); | |
| 154 | 146 | |
| 155 | 147 | /* Delete the temporary file and clean up memory used */ |
| 156 | 148 | unlink(zTemp1); |
| 157 | 149 | unlink(zTemp2); |
| 158 | 150 | blob_reset(&cmd); |
| @@ -183,16 +175,20 @@ | ||
| 183 | 175 | ** files on disk and the check-out on which they are based. |
| 184 | 176 | */ |
| 185 | 177 | static void diff_all_against_disk( |
| 186 | 178 | const char *zFrom, /* Version to difference from */ |
| 187 | 179 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 188 | - int ignoreEolWs /* Ignore end-of-line whitespace */ | |
| 180 | + int diffFlags /* Flags controlling diff output */ | |
| 189 | 181 | ){ |
| 190 | 182 | int vid; |
| 191 | 183 | Blob sql; |
| 192 | 184 | Stmt q; |
| 185 | + int ignoreEolWs; /* Ignore end-of-line whitespace */ | |
| 186 | + int asNewFile; /* Treat non-existant files as empty files */ | |
| 193 | 187 | |
| 188 | + ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0; | |
| 189 | + asNewFile = (diffFlags & DIFF_NEWFILE)!=0; | |
| 194 | 190 | vid = db_lget_int("checkout", 0); |
| 195 | 191 | vfile_check_signature(vid, 1); |
| 196 | 192 | blob_zero(&sql); |
| 197 | 193 | db_begin_transaction(); |
| 198 | 194 | if( zFrom ){ |
| @@ -235,31 +231,44 @@ | ||
| 235 | 231 | while( db_step(&q)==SQLITE_ROW ){ |
| 236 | 232 | const char *zPathname = db_column_text(&q,0); |
| 237 | 233 | int isDeleted = db_column_int(&q, 1); |
| 238 | 234 | int isChnged = db_column_int(&q,2); |
| 239 | 235 | int isNew = db_column_int(&q,3); |
| 236 | + int srcid = db_column_int(&q, 4); | |
| 240 | 237 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 238 | + char *zToFree = zFullName; | |
| 239 | + int showDiff = 1; | |
| 241 | 240 | if( isDeleted ){ |
| 242 | 241 | printf("DELETED %s\n", zPathname); |
| 242 | + if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } | |
| 243 | 243 | }else if( access(zFullName, 0) ){ |
| 244 | 244 | printf("MISSING %s\n", zPathname); |
| 245 | + if( !asNewFile ){ showDiff = 0; } | |
| 245 | 246 | }else if( isNew ){ |
| 246 | 247 | printf("ADDED %s\n", zPathname); |
| 248 | + srcid = 0; | |
| 249 | + if( !asNewFile ){ showDiff = 0; } | |
| 247 | 250 | }else if( isChnged==3 ){ |
| 248 | 251 | printf("ADDED_BY_MERGE %s\n", zPathname); |
| 249 | - }else{ | |
| 250 | - int srcid = db_column_int(&q, 4); | |
| 252 | + srcid = 0; | |
| 253 | + if( !asNewFile ){ showDiff = 0; } | |
| 254 | + } | |
| 255 | + if( showDiff ){ | |
| 251 | 256 | Blob content; |
| 252 | - content_get(srcid, &content); | |
| 257 | + if( srcid>0 ){ | |
| 258 | + content_get(srcid, &content); | |
| 259 | + }else{ | |
| 260 | + blob_zero(&content); | |
| 261 | + } | |
| 253 | 262 | printf("Index: %s\n=======================================" |
| 254 | 263 | "============================\n", |
| 255 | 264 | zPathname |
| 256 | 265 | ); |
| 257 | 266 | diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs); |
| 258 | 267 | blob_reset(&content); |
| 259 | 268 | } |
| 260 | - free(zFullName); | |
| 269 | + free(zToFree); | |
| 261 | 270 | } |
| 262 | 271 | db_finalize(&q); |
| 263 | 272 | db_end_transaction(1); /* ROLLBACK */ |
| 264 | 273 | } |
| 265 | 274 | |
| @@ -284,66 +293,98 @@ | ||
| 284 | 293 | diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs); |
| 285 | 294 | blob_reset(&v1); |
| 286 | 295 | blob_reset(&v2); |
| 287 | 296 | blob_reset(&fname); |
| 288 | 297 | } |
| 298 | + | |
| 299 | +/* | |
| 300 | +** Show the difference between two files identified by ManifestFile | |
| 301 | +** entries. | |
| 302 | +*/ | |
| 303 | +static void diff_manifest_entry( | |
| 304 | + struct ManifestFile *pFrom, | |
| 305 | + struct ManifestFile *pTo, | |
| 306 | + const char *zDiffCmd, | |
| 307 | + int ignoreEolWs | |
| 308 | +){ | |
| 309 | + Blob f1, f2; | |
| 310 | + int rid; | |
| 311 | + const char *zName = pFrom ? pFrom->zName : pTo->zName; | |
| 312 | + printf("Index: %s\n=======================================" | |
| 313 | + "============================\n", zName); | |
| 314 | + if( pFrom ){ | |
| 315 | + rid = uuid_to_rid(pFrom->zUuid, 0); | |
| 316 | + content_get(rid, &f1); | |
| 317 | + }else{ | |
| 318 | + blob_zero(&f1); | |
| 319 | + } | |
| 320 | + if( pTo ){ | |
| 321 | + rid = uuid_to_rid(pTo->zUuid, 0); | |
| 322 | + content_get(rid, &f2); | |
| 323 | + }else{ | |
| 324 | + blob_zero(&f2); | |
| 325 | + } | |
| 326 | + diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs); | |
| 327 | + blob_reset(&f1); | |
| 328 | + blob_reset(&f2); | |
| 329 | +} | |
| 289 | 330 | |
| 290 | 331 | /* |
| 291 | 332 | ** Output the differences between two check-ins. |
| 292 | 333 | */ |
| 293 | 334 | static void diff_all_two_versions( |
| 294 | 335 | const char *zFrom, |
| 295 | 336 | const char *zTo, |
| 296 | 337 | const char *zDiffCmd, |
| 297 | - int ignoreEolWs | |
| 338 | + int diffFlags | |
| 298 | 339 | ){ |
| 299 | - Manifest mFrom, mTo; | |
| 300 | - int iFrom, iTo; | |
| 340 | + Manifest *pFrom, *pTo; | |
| 341 | + ManifestFile *pFromFile, *pToFile; | |
| 342 | + int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0; | |
| 343 | + int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0; | |
| 344 | + | |
| 345 | + pFrom = manifest_get_by_name(zFrom, 0); | |
| 346 | + manifest_file_rewind(pFrom); | |
| 347 | + pFromFile = manifest_file_next(pFrom,0); | |
| 348 | + pTo = manifest_get_by_name(zTo, 0); | |
| 349 | + manifest_file_rewind(pTo); | |
| 350 | + pToFile = manifest_file_next(pTo,0); | |
| 301 | 351 | |
| 302 | - manifest_from_name(zFrom, &mFrom); | |
| 303 | - manifest_from_name(zTo, &mTo); | |
| 304 | - iFrom = iTo = 0; | |
| 305 | - while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ | |
| 352 | + while( pFromFile || pToFile ){ | |
| 306 | 353 | int cmp; |
| 307 | - if( iFrom>=mFrom.nFile ){ | |
| 354 | + if( pFromFile==0 ){ | |
| 308 | 355 | cmp = +1; |
| 309 | - }else if( iTo>=mTo.nFile ){ | |
| 356 | + }else if( pToFile==0 ){ | |
| 310 | 357 | cmp = -1; |
| 311 | 358 | }else{ |
| 312 | - cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); | |
| 359 | + cmp = strcmp(pFromFile->zName, pToFile->zName); | |
| 313 | 360 | } |
| 314 | 361 | if( cmp<0 ){ |
| 315 | - printf("DELETED %s\n", mFrom.aFile[iFrom].zName); | |
| 316 | - iFrom++; | |
| 317 | - }else if( cmp>0 ){ | |
| 318 | - printf("ADDED %s\n", mTo.aFile[iTo].zName); | |
| 319 | - iTo++; | |
| 320 | - }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ | |
| 321 | - /* No changes */ | |
| 322 | - iFrom++; | |
| 323 | - iTo++; | |
| 324 | - }else{ | |
| 325 | - Blob f1, f2; | |
| 326 | - int rid; | |
| 327 | - printf("CHANGED %s\n", mFrom.aFile[iFrom].zName); | |
| 328 | - printf("Index: %s\n=======================================" | |
| 329 | - "============================\n", | |
| 330 | - mFrom.aFile[iFrom].zName | |
| 331 | - ); | |
| 332 | - rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0); | |
| 333 | - content_get(rid, &f1); | |
| 334 | - rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0); | |
| 335 | - content_get(rid, &f2); | |
| 336 | - diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd, ignoreEolWs); | |
| 337 | - blob_reset(&f1); | |
| 338 | - blob_reset(&f2); | |
| 339 | - iFrom++; | |
| 340 | - iTo++; | |
| 341 | - } | |
| 342 | - } | |
| 343 | - manifest_clear(&mFrom); | |
| 344 | - manifest_clear(&mTo); | |
| 362 | + printf("DELETED %s\n", pFromFile->zName); | |
| 363 | + if( asNewFlag ){ | |
| 364 | + diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs); | |
| 365 | + } | |
| 366 | + pFromFile = manifest_file_next(pFrom,0); | |
| 367 | + }else if( cmp>0 ){ | |
| 368 | + printf("ADDED %s\n", pToFile->zName); | |
| 369 | + if( asNewFlag ){ | |
| 370 | + diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs); | |
| 371 | + } | |
| 372 | + pToFile = manifest_file_next(pTo,0); | |
| 373 | + }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ | |
| 374 | + /* No changes */ | |
| 375 | + pFromFile = manifest_file_next(pFrom,0); | |
| 376 | + pToFile = manifest_file_next(pTo,0); | |
| 377 | + }else{ | |
| 378 | + printf("CHANGED %s\n", pFromFile->zName); | |
| 379 | + diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs); | |
| 380 | + pFromFile = manifest_file_next(pFrom,0); | |
| 381 | + pToFile = manifest_file_next(pTo,0); | |
| 382 | + } | |
| 383 | + } | |
| 384 | + manifest_destroy(pFrom); | |
| 385 | + manifest_destroy(pTo); | |
| 345 | 386 | } |
| 346 | 387 | |
| 347 | 388 | /* |
| 348 | 389 | ** COMMAND: diff |
| 349 | 390 | ** COMMAND: gdiff |
| @@ -366,33 +407,41 @@ | ||
| 366 | 407 | ** |
| 367 | 408 | ** The "-i" command-line option forces the use of the internal diff logic |
| 368 | 409 | ** rather than any external diff program that might be configured using |
| 369 | 410 | ** the "setting" command. If no external diff program is configured, then |
| 370 | 411 | ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". |
| 412 | +** | |
| 413 | +** The "-N" or "--new-file" option causes the complete text of added or | |
| 414 | +** deleted files to be displayed. | |
| 371 | 415 | */ |
| 372 | 416 | void diff_cmd(void){ |
| 373 | 417 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 374 | 418 | int isInternDiff; /* True for internal diff */ |
| 419 | + int hasNFlag; /* True if -N or --new-file flag is used */ | |
| 375 | 420 | const char *zFrom; /* Source version number */ |
| 376 | 421 | const char *zTo; /* Target version number */ |
| 377 | 422 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 423 | + int diffFlags = 0; /* Flags to control the DIFF */ | |
| 378 | 424 | |
| 379 | 425 | isGDiff = g.argv[1][0]=='g'; |
| 380 | 426 | isInternDiff = find_option("internal","i",0)!=0; |
| 381 | 427 | zFrom = find_option("from", "r", 1); |
| 382 | 428 | zTo = find_option("to", 0, 1); |
| 429 | + hasNFlag = find_option("new-file","N",0)!=0; | |
| 430 | + | |
| 383 | 431 | |
| 432 | + if( hasNFlag ) diffFlags |= DIFF_NEWFILE; | |
| 384 | 433 | if( zTo==0 ){ |
| 385 | 434 | db_must_be_within_tree(); |
| 386 | 435 | verify_all_options(); |
| 387 | 436 | if( !isInternDiff ){ |
| 388 | 437 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 389 | 438 | } |
| 390 | 439 | if( g.argc==3 ){ |
| 391 | 440 | diff_one_against_disk(zFrom, zDiffCmd, 0); |
| 392 | 441 | }else{ |
| 393 | - diff_all_against_disk(zFrom, zDiffCmd, 0); | |
| 442 | + diff_all_against_disk(zFrom, zDiffCmd, diffFlags); | |
| 394 | 443 | } |
| 395 | 444 | }else if( zFrom==0 ){ |
| 396 | 445 | fossil_fatal("must use --from if --to is present"); |
| 397 | 446 | }else{ |
| 398 | 447 | db_find_and_open_repository(1); |
| @@ -401,9 +450,9 @@ | ||
| 401 | 450 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 402 | 451 | } |
| 403 | 452 | if( g.argc==3 ){ |
| 404 | 453 | diff_one_two_versions(zFrom, zTo, zDiffCmd, 0); |
| 405 | 454 | }else{ |
| 406 | - diff_all_two_versions(zFrom, zTo, zDiffCmd, 0); | |
| 455 | + diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags); | |
| 407 | 456 | } |
| 408 | 457 | } |
| 409 | 458 | } |
| 410 | 459 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -20,28 +20,14 @@ | |
| 20 | #include "config.h" |
| 21 | #include "diffcmd.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** This function implements a cross-platform "system()" interface. |
| 26 | */ |
| 27 | int portable_system(const char *zOrigCmd){ |
| 28 | int rc; |
| 29 | #if defined(_WIN32) |
| 30 | /* On windows, we have to put double-quotes around the entire command. |
| 31 | ** Who knows why - this is just the way windows works. |
| 32 | */ |
| 33 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 34 | rc = system(zNewCmd); |
| 35 | free(zNewCmd); |
| 36 | #else |
| 37 | /* On unix, evaluate the command directly. |
| 38 | */ |
| 39 | rc = system(zOrigCmd); |
| 40 | #endif |
| 41 | return rc; |
| 42 | } |
| 43 | |
| 44 | /* |
| 45 | ** Show the difference between two files, one in memory and one on disk. |
| 46 | ** |
| 47 | ** The difference is the set of edits needed to transform pFile1 into |
| @@ -53,24 +39,30 @@ | |
| 53 | static void diff_file( |
| 54 | Blob *pFile1, /* In memory content to compare from */ |
| 55 | const char *zFile2, /* On disk content to compare to */ |
| 56 | const char *zName, /* Display name of the file */ |
| 57 | const char *zDiffCmd, /* Command for comparison */ |
| 58 | int ignoreEolWs /* Ignore whitespace at end of lines */ |
| 59 | ){ |
| 60 | if( zDiffCmd==0 ){ |
| 61 | Blob out; /* Diff output text */ |
| 62 | Blob file2; /* Content of zFile2 */ |
| 63 | |
| 64 | /* Read content of zFile2 into memory */ |
| 65 | blob_zero(&file2); |
| 66 | blob_read_from_file(&file2, zFile2); |
| 67 | |
| 68 | /* Compute and output the differences */ |
| 69 | blob_zero(&out); |
| 70 | text_diff(pFile1, &file2, &out, 5, ignoreEolWs); |
| 71 | printf("--- %s\n+++ %s\n", zName, zName); |
| 72 | printf("%s\n", blob_str(&out)); |
| 73 | |
| 74 | /* Release memory resources */ |
| 75 | blob_reset(&file2); |
| 76 | blob_reset(&out); |
| @@ -94,11 +86,11 @@ | |
| 94 | shell_escape(&cmd, blob_str(&nameFile1)); |
| 95 | blob_append(&cmd, " ", 1); |
| 96 | shell_escape(&cmd, zFile2); |
| 97 | |
| 98 | /* Run the external diff command */ |
| 99 | portable_system(blob_str(&cmd)); |
| 100 | |
| 101 | /* Delete the temporary file and clean up memory used */ |
| 102 | unlink(blob_str(&nameFile1)); |
| 103 | blob_reset(&nameFile1); |
| 104 | blob_reset(&cmd); |
| @@ -148,11 +140,11 @@ | |
| 148 | shell_escape(&cmd, zTemp1); |
| 149 | blob_append(&cmd, " ", 1); |
| 150 | shell_escape(&cmd, zTemp2); |
| 151 | |
| 152 | /* Run the external diff command */ |
| 153 | portable_system(blob_str(&cmd)); |
| 154 | |
| 155 | /* Delete the temporary file and clean up memory used */ |
| 156 | unlink(zTemp1); |
| 157 | unlink(zTemp2); |
| 158 | blob_reset(&cmd); |
| @@ -183,16 +175,20 @@ | |
| 183 | ** files on disk and the check-out on which they are based. |
| 184 | */ |
| 185 | static void diff_all_against_disk( |
| 186 | const char *zFrom, /* Version to difference from */ |
| 187 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 188 | int ignoreEolWs /* Ignore end-of-line whitespace */ |
| 189 | ){ |
| 190 | int vid; |
| 191 | Blob sql; |
| 192 | Stmt q; |
| 193 | |
| 194 | vid = db_lget_int("checkout", 0); |
| 195 | vfile_check_signature(vid, 1); |
| 196 | blob_zero(&sql); |
| 197 | db_begin_transaction(); |
| 198 | if( zFrom ){ |
| @@ -235,31 +231,44 @@ | |
| 235 | while( db_step(&q)==SQLITE_ROW ){ |
| 236 | const char *zPathname = db_column_text(&q,0); |
| 237 | int isDeleted = db_column_int(&q, 1); |
| 238 | int isChnged = db_column_int(&q,2); |
| 239 | int isNew = db_column_int(&q,3); |
| 240 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 241 | if( isDeleted ){ |
| 242 | printf("DELETED %s\n", zPathname); |
| 243 | }else if( access(zFullName, 0) ){ |
| 244 | printf("MISSING %s\n", zPathname); |
| 245 | }else if( isNew ){ |
| 246 | printf("ADDED %s\n", zPathname); |
| 247 | }else if( isChnged==3 ){ |
| 248 | printf("ADDED_BY_MERGE %s\n", zPathname); |
| 249 | }else{ |
| 250 | int srcid = db_column_int(&q, 4); |
| 251 | Blob content; |
| 252 | content_get(srcid, &content); |
| 253 | printf("Index: %s\n=======================================" |
| 254 | "============================\n", |
| 255 | zPathname |
| 256 | ); |
| 257 | diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs); |
| 258 | blob_reset(&content); |
| 259 | } |
| 260 | free(zFullName); |
| 261 | } |
| 262 | db_finalize(&q); |
| 263 | db_end_transaction(1); /* ROLLBACK */ |
| 264 | } |
| 265 | |
| @@ -284,66 +293,98 @@ | |
| 284 | diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs); |
| 285 | blob_reset(&v1); |
| 286 | blob_reset(&v2); |
| 287 | blob_reset(&fname); |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | ** Output the differences between two check-ins. |
| 292 | */ |
| 293 | static void diff_all_two_versions( |
| 294 | const char *zFrom, |
| 295 | const char *zTo, |
| 296 | const char *zDiffCmd, |
| 297 | int ignoreEolWs |
| 298 | ){ |
| 299 | Manifest mFrom, mTo; |
| 300 | int iFrom, iTo; |
| 301 | |
| 302 | manifest_from_name(zFrom, &mFrom); |
| 303 | manifest_from_name(zTo, &mTo); |
| 304 | iFrom = iTo = 0; |
| 305 | while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ |
| 306 | int cmp; |
| 307 | if( iFrom>=mFrom.nFile ){ |
| 308 | cmp = +1; |
| 309 | }else if( iTo>=mTo.nFile ){ |
| 310 | cmp = -1; |
| 311 | }else{ |
| 312 | cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); |
| 313 | } |
| 314 | if( cmp<0 ){ |
| 315 | printf("DELETED %s\n", mFrom.aFile[iFrom].zName); |
| 316 | iFrom++; |
| 317 | }else if( cmp>0 ){ |
| 318 | printf("ADDED %s\n", mTo.aFile[iTo].zName); |
| 319 | iTo++; |
| 320 | }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ |
| 321 | /* No changes */ |
| 322 | iFrom++; |
| 323 | iTo++; |
| 324 | }else{ |
| 325 | Blob f1, f2; |
| 326 | int rid; |
| 327 | printf("CHANGED %s\n", mFrom.aFile[iFrom].zName); |
| 328 | printf("Index: %s\n=======================================" |
| 329 | "============================\n", |
| 330 | mFrom.aFile[iFrom].zName |
| 331 | ); |
| 332 | rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0); |
| 333 | content_get(rid, &f1); |
| 334 | rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0); |
| 335 | content_get(rid, &f2); |
| 336 | diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd, ignoreEolWs); |
| 337 | blob_reset(&f1); |
| 338 | blob_reset(&f2); |
| 339 | iFrom++; |
| 340 | iTo++; |
| 341 | } |
| 342 | } |
| 343 | manifest_clear(&mFrom); |
| 344 | manifest_clear(&mTo); |
| 345 | } |
| 346 | |
| 347 | /* |
| 348 | ** COMMAND: diff |
| 349 | ** COMMAND: gdiff |
| @@ -366,33 +407,41 @@ | |
| 366 | ** |
| 367 | ** The "-i" command-line option forces the use of the internal diff logic |
| 368 | ** rather than any external diff program that might be configured using |
| 369 | ** the "setting" command. If no external diff program is configured, then |
| 370 | ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". |
| 371 | */ |
| 372 | void diff_cmd(void){ |
| 373 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 374 | int isInternDiff; /* True for internal diff */ |
| 375 | const char *zFrom; /* Source version number */ |
| 376 | const char *zTo; /* Target version number */ |
| 377 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 378 | |
| 379 | isGDiff = g.argv[1][0]=='g'; |
| 380 | isInternDiff = find_option("internal","i",0)!=0; |
| 381 | zFrom = find_option("from", "r", 1); |
| 382 | zTo = find_option("to", 0, 1); |
| 383 | |
| 384 | if( zTo==0 ){ |
| 385 | db_must_be_within_tree(); |
| 386 | verify_all_options(); |
| 387 | if( !isInternDiff ){ |
| 388 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 389 | } |
| 390 | if( g.argc==3 ){ |
| 391 | diff_one_against_disk(zFrom, zDiffCmd, 0); |
| 392 | }else{ |
| 393 | diff_all_against_disk(zFrom, zDiffCmd, 0); |
| 394 | } |
| 395 | }else if( zFrom==0 ){ |
| 396 | fossil_fatal("must use --from if --to is present"); |
| 397 | }else{ |
| 398 | db_find_and_open_repository(1); |
| @@ -401,9 +450,9 @@ | |
| 401 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 402 | } |
| 403 | if( g.argc==3 ){ |
| 404 | diff_one_two_versions(zFrom, zTo, zDiffCmd, 0); |
| 405 | }else{ |
| 406 | diff_all_two_versions(zFrom, zTo, zDiffCmd, 0); |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -20,28 +20,14 @@ | |
| 20 | #include "config.h" |
| 21 | #include "diffcmd.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Diff option flags |
| 26 | */ |
| 27 | #define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */ |
| 28 | #define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */ |
| 29 | |
| 30 | /* |
| 31 | ** Show the difference between two files, one in memory and one on disk. |
| 32 | ** |
| 33 | ** The difference is the set of edits needed to transform pFile1 into |
| @@ -53,24 +39,30 @@ | |
| 39 | static void diff_file( |
| 40 | Blob *pFile1, /* In memory content to compare from */ |
| 41 | const char *zFile2, /* On disk content to compare to */ |
| 42 | const char *zName, /* Display name of the file */ |
| 43 | const char *zDiffCmd, /* Command for comparison */ |
| 44 | int ignoreEolWs /* Ignore whitespace at end of line */ |
| 45 | ){ |
| 46 | if( zDiffCmd==0 ){ |
| 47 | Blob out; /* Diff output text */ |
| 48 | Blob file2; /* Content of zFile2 */ |
| 49 | const char *zName2; /* Name of zFile2 for display */ |
| 50 | |
| 51 | /* Read content of zFile2 into memory */ |
| 52 | blob_zero(&file2); |
| 53 | if( file_size(zFile2)<0 ){ |
| 54 | zName2 = "/dev/null"; |
| 55 | }else{ |
| 56 | blob_read_from_file(&file2, zFile2); |
| 57 | zName2 = zName; |
| 58 | } |
| 59 | |
| 60 | /* Compute and output the differences */ |
| 61 | blob_zero(&out); |
| 62 | text_diff(pFile1, &file2, &out, 5, ignoreEolWs); |
| 63 | printf("--- %s\n+++ %s\n", zName, zName2); |
| 64 | printf("%s\n", blob_str(&out)); |
| 65 | |
| 66 | /* Release memory resources */ |
| 67 | blob_reset(&file2); |
| 68 | blob_reset(&out); |
| @@ -94,11 +86,11 @@ | |
| 86 | shell_escape(&cmd, blob_str(&nameFile1)); |
| 87 | blob_append(&cmd, " ", 1); |
| 88 | shell_escape(&cmd, zFile2); |
| 89 | |
| 90 | /* Run the external diff command */ |
| 91 | fossil_system(blob_str(&cmd)); |
| 92 | |
| 93 | /* Delete the temporary file and clean up memory used */ |
| 94 | unlink(blob_str(&nameFile1)); |
| 95 | blob_reset(&nameFile1); |
| 96 | blob_reset(&cmd); |
| @@ -148,11 +140,11 @@ | |
| 140 | shell_escape(&cmd, zTemp1); |
| 141 | blob_append(&cmd, " ", 1); |
| 142 | shell_escape(&cmd, zTemp2); |
| 143 | |
| 144 | /* Run the external diff command */ |
| 145 | fossil_system(blob_str(&cmd)); |
| 146 | |
| 147 | /* Delete the temporary file and clean up memory used */ |
| 148 | unlink(zTemp1); |
| 149 | unlink(zTemp2); |
| 150 | blob_reset(&cmd); |
| @@ -183,16 +175,20 @@ | |
| 175 | ** files on disk and the check-out on which they are based. |
| 176 | */ |
| 177 | static void diff_all_against_disk( |
| 178 | const char *zFrom, /* Version to difference from */ |
| 179 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 180 | int diffFlags /* Flags controlling diff output */ |
| 181 | ){ |
| 182 | int vid; |
| 183 | Blob sql; |
| 184 | Stmt q; |
| 185 | int ignoreEolWs; /* Ignore end-of-line whitespace */ |
| 186 | int asNewFile; /* Treat non-existant files as empty files */ |
| 187 | |
| 188 | ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0; |
| 189 | asNewFile = (diffFlags & DIFF_NEWFILE)!=0; |
| 190 | vid = db_lget_int("checkout", 0); |
| 191 | vfile_check_signature(vid, 1); |
| 192 | blob_zero(&sql); |
| 193 | db_begin_transaction(); |
| 194 | if( zFrom ){ |
| @@ -235,31 +231,44 @@ | |
| 231 | while( db_step(&q)==SQLITE_ROW ){ |
| 232 | const char *zPathname = db_column_text(&q,0); |
| 233 | int isDeleted = db_column_int(&q, 1); |
| 234 | int isChnged = db_column_int(&q,2); |
| 235 | int isNew = db_column_int(&q,3); |
| 236 | int srcid = db_column_int(&q, 4); |
| 237 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 238 | char *zToFree = zFullName; |
| 239 | int showDiff = 1; |
| 240 | if( isDeleted ){ |
| 241 | printf("DELETED %s\n", zPathname); |
| 242 | if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } |
| 243 | }else if( access(zFullName, 0) ){ |
| 244 | printf("MISSING %s\n", zPathname); |
| 245 | if( !asNewFile ){ showDiff = 0; } |
| 246 | }else if( isNew ){ |
| 247 | printf("ADDED %s\n", zPathname); |
| 248 | srcid = 0; |
| 249 | if( !asNewFile ){ showDiff = 0; } |
| 250 | }else if( isChnged==3 ){ |
| 251 | printf("ADDED_BY_MERGE %s\n", zPathname); |
| 252 | srcid = 0; |
| 253 | if( !asNewFile ){ showDiff = 0; } |
| 254 | } |
| 255 | if( showDiff ){ |
| 256 | Blob content; |
| 257 | if( srcid>0 ){ |
| 258 | content_get(srcid, &content); |
| 259 | }else{ |
| 260 | blob_zero(&content); |
| 261 | } |
| 262 | printf("Index: %s\n=======================================" |
| 263 | "============================\n", |
| 264 | zPathname |
| 265 | ); |
| 266 | diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs); |
| 267 | blob_reset(&content); |
| 268 | } |
| 269 | free(zToFree); |
| 270 | } |
| 271 | db_finalize(&q); |
| 272 | db_end_transaction(1); /* ROLLBACK */ |
| 273 | } |
| 274 | |
| @@ -284,66 +293,98 @@ | |
| 293 | diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs); |
| 294 | blob_reset(&v1); |
| 295 | blob_reset(&v2); |
| 296 | blob_reset(&fname); |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | ** Show the difference between two files identified by ManifestFile |
| 301 | ** entries. |
| 302 | */ |
| 303 | static void diff_manifest_entry( |
| 304 | struct ManifestFile *pFrom, |
| 305 | struct ManifestFile *pTo, |
| 306 | const char *zDiffCmd, |
| 307 | int ignoreEolWs |
| 308 | ){ |
| 309 | Blob f1, f2; |
| 310 | int rid; |
| 311 | const char *zName = pFrom ? pFrom->zName : pTo->zName; |
| 312 | printf("Index: %s\n=======================================" |
| 313 | "============================\n", zName); |
| 314 | if( pFrom ){ |
| 315 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 316 | content_get(rid, &f1); |
| 317 | }else{ |
| 318 | blob_zero(&f1); |
| 319 | } |
| 320 | if( pTo ){ |
| 321 | rid = uuid_to_rid(pTo->zUuid, 0); |
| 322 | content_get(rid, &f2); |
| 323 | }else{ |
| 324 | blob_zero(&f2); |
| 325 | } |
| 326 | diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs); |
| 327 | blob_reset(&f1); |
| 328 | blob_reset(&f2); |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | ** Output the differences between two check-ins. |
| 333 | */ |
| 334 | static void diff_all_two_versions( |
| 335 | const char *zFrom, |
| 336 | const char *zTo, |
| 337 | const char *zDiffCmd, |
| 338 | int diffFlags |
| 339 | ){ |
| 340 | Manifest *pFrom, *pTo; |
| 341 | ManifestFile *pFromFile, *pToFile; |
| 342 | int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0; |
| 343 | int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0; |
| 344 | |
| 345 | pFrom = manifest_get_by_name(zFrom, 0); |
| 346 | manifest_file_rewind(pFrom); |
| 347 | pFromFile = manifest_file_next(pFrom,0); |
| 348 | pTo = manifest_get_by_name(zTo, 0); |
| 349 | manifest_file_rewind(pTo); |
| 350 | pToFile = manifest_file_next(pTo,0); |
| 351 | |
| 352 | while( pFromFile || pToFile ){ |
| 353 | int cmp; |
| 354 | if( pFromFile==0 ){ |
| 355 | cmp = +1; |
| 356 | }else if( pToFile==0 ){ |
| 357 | cmp = -1; |
| 358 | }else{ |
| 359 | cmp = strcmp(pFromFile->zName, pToFile->zName); |
| 360 | } |
| 361 | if( cmp<0 ){ |
| 362 | printf("DELETED %s\n", pFromFile->zName); |
| 363 | if( asNewFlag ){ |
| 364 | diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs); |
| 365 | } |
| 366 | pFromFile = manifest_file_next(pFrom,0); |
| 367 | }else if( cmp>0 ){ |
| 368 | printf("ADDED %s\n", pToFile->zName); |
| 369 | if( asNewFlag ){ |
| 370 | diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs); |
| 371 | } |
| 372 | pToFile = manifest_file_next(pTo,0); |
| 373 | }else if( strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ |
| 374 | /* No changes */ |
| 375 | pFromFile = manifest_file_next(pFrom,0); |
| 376 | pToFile = manifest_file_next(pTo,0); |
| 377 | }else{ |
| 378 | printf("CHANGED %s\n", pFromFile->zName); |
| 379 | diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs); |
| 380 | pFromFile = manifest_file_next(pFrom,0); |
| 381 | pToFile = manifest_file_next(pTo,0); |
| 382 | } |
| 383 | } |
| 384 | manifest_destroy(pFrom); |
| 385 | manifest_destroy(pTo); |
| 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** COMMAND: diff |
| 390 | ** COMMAND: gdiff |
| @@ -366,33 +407,41 @@ | |
| 407 | ** |
| 408 | ** The "-i" command-line option forces the use of the internal diff logic |
| 409 | ** rather than any external diff program that might be configured using |
| 410 | ** the "setting" command. If no external diff program is configured, then |
| 411 | ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". |
| 412 | ** |
| 413 | ** The "-N" or "--new-file" option causes the complete text of added or |
| 414 | ** deleted files to be displayed. |
| 415 | */ |
| 416 | void diff_cmd(void){ |
| 417 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 418 | int isInternDiff; /* True for internal diff */ |
| 419 | int hasNFlag; /* True if -N or --new-file flag is used */ |
| 420 | const char *zFrom; /* Source version number */ |
| 421 | const char *zTo; /* Target version number */ |
| 422 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 423 | int diffFlags = 0; /* Flags to control the DIFF */ |
| 424 | |
| 425 | isGDiff = g.argv[1][0]=='g'; |
| 426 | isInternDiff = find_option("internal","i",0)!=0; |
| 427 | zFrom = find_option("from", "r", 1); |
| 428 | zTo = find_option("to", 0, 1); |
| 429 | hasNFlag = find_option("new-file","N",0)!=0; |
| 430 | |
| 431 | |
| 432 | if( hasNFlag ) diffFlags |= DIFF_NEWFILE; |
| 433 | if( zTo==0 ){ |
| 434 | db_must_be_within_tree(); |
| 435 | verify_all_options(); |
| 436 | if( !isInternDiff ){ |
| 437 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 438 | } |
| 439 | if( g.argc==3 ){ |
| 440 | diff_one_against_disk(zFrom, zDiffCmd, 0); |
| 441 | }else{ |
| 442 | diff_all_against_disk(zFrom, zDiffCmd, diffFlags); |
| 443 | } |
| 444 | }else if( zFrom==0 ){ |
| 445 | fossil_fatal("must use --from if --to is present"); |
| 446 | }else{ |
| 447 | db_find_and_open_repository(1); |
| @@ -401,9 +450,9 @@ | |
| 450 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 451 | } |
| 452 | if( g.argc==3 ){ |
| 453 | diff_one_two_versions(zFrom, zTo, zDiffCmd, 0); |
| 454 | }else{ |
| 455 | diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags); |
| 456 | } |
| 457 | } |
| 458 | } |
| 459 |
+10
-11
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -290,11 +290,11 @@ | ||
| 290 | 290 | if( zName[i]=='.' ) z = &zName[i+1]; |
| 291 | 291 | } |
| 292 | 292 | len = strlen(z); |
| 293 | 293 | if( len<sizeof(zSuffix)-1 ){ |
| 294 | 294 | strcpy(zSuffix, z); |
| 295 | - for(i=0; zSuffix[i]; i++) zSuffix[i] = tolower(zSuffix[i]); | |
| 295 | + for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); | |
| 296 | 296 | first = 0; |
| 297 | 297 | last = sizeof(aMime)/sizeof(aMime[0]); |
| 298 | 298 | while( first<=last ){ |
| 299 | 299 | int c; |
| 300 | 300 | i = (first+last)/2; |
| @@ -388,37 +388,36 @@ | ||
| 388 | 388 | goto doc_not_found; |
| 389 | 389 | } |
| 390 | 390 | |
| 391 | 391 | if( rid==0 ){ |
| 392 | 392 | Stmt s; |
| 393 | - Blob baseline; | |
| 394 | - Manifest m; | |
| 393 | + Manifest *pM; | |
| 394 | + ManifestFile *pFile; | |
| 395 | 395 | |
| 396 | 396 | /* Add the vid baseline to the cache */ |
| 397 | 397 | if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ |
| 398 | 398 | db_multi_exec("DELETE FROM vcache"); |
| 399 | 399 | } |
| 400 | - if( content_get(vid, &baseline)==0 ){ | |
| 401 | - goto doc_not_found; | |
| 402 | - } | |
| 403 | - if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){ | |
| 400 | + pM = manifest_get(vid, CFTYPE_MANIFEST); | |
| 401 | + if( pM==0 ){ | |
| 404 | 402 | goto doc_not_found; |
| 405 | 403 | } |
| 406 | 404 | db_prepare(&s, |
| 407 | 405 | "INSERT INTO vcache(vid,fname,rid)" |
| 408 | 406 | " SELECT %d, :fname, rid FROM blob" |
| 409 | 407 | " WHERE uuid=:uuid", |
| 410 | 408 | vid |
| 411 | 409 | ); |
| 412 | - for(i=0; i<m.nFile; i++){ | |
| 413 | - db_bind_text(&s, ":fname", m.aFile[i].zName); | |
| 414 | - db_bind_text(&s, ":uuid", m.aFile[i].zUuid); | |
| 410 | + manifest_file_rewind(pM); | |
| 411 | + while( (pFile = manifest_file_next(pM,0))!=0 ){ | |
| 412 | + db_bind_text(&s, ":fname", pFile->zName); | |
| 413 | + db_bind_text(&s, ":uuid", pFile->zUuid); | |
| 415 | 414 | db_step(&s); |
| 416 | 415 | db_reset(&s); |
| 417 | 416 | } |
| 418 | 417 | db_finalize(&s); |
| 419 | - manifest_clear(&m); | |
| 418 | + manifest_destroy(pM); | |
| 420 | 419 | |
| 421 | 420 | /* Try again to find the file */ |
| 422 | 421 | rid = db_int(0, "SELECT rid FROM vcache" |
| 423 | 422 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 424 | 423 | } |
| 425 | 424 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -290,11 +290,11 @@ | |
| 290 | if( zName[i]=='.' ) z = &zName[i+1]; |
| 291 | } |
| 292 | len = strlen(z); |
| 293 | if( len<sizeof(zSuffix)-1 ){ |
| 294 | strcpy(zSuffix, z); |
| 295 | for(i=0; zSuffix[i]; i++) zSuffix[i] = tolower(zSuffix[i]); |
| 296 | first = 0; |
| 297 | last = sizeof(aMime)/sizeof(aMime[0]); |
| 298 | while( first<=last ){ |
| 299 | int c; |
| 300 | i = (first+last)/2; |
| @@ -388,37 +388,36 @@ | |
| 388 | goto doc_not_found; |
| 389 | } |
| 390 | |
| 391 | if( rid==0 ){ |
| 392 | Stmt s; |
| 393 | Blob baseline; |
| 394 | Manifest m; |
| 395 | |
| 396 | /* Add the vid baseline to the cache */ |
| 397 | if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ |
| 398 | db_multi_exec("DELETE FROM vcache"); |
| 399 | } |
| 400 | if( content_get(vid, &baseline)==0 ){ |
| 401 | goto doc_not_found; |
| 402 | } |
| 403 | if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){ |
| 404 | goto doc_not_found; |
| 405 | } |
| 406 | db_prepare(&s, |
| 407 | "INSERT INTO vcache(vid,fname,rid)" |
| 408 | " SELECT %d, :fname, rid FROM blob" |
| 409 | " WHERE uuid=:uuid", |
| 410 | vid |
| 411 | ); |
| 412 | for(i=0; i<m.nFile; i++){ |
| 413 | db_bind_text(&s, ":fname", m.aFile[i].zName); |
| 414 | db_bind_text(&s, ":uuid", m.aFile[i].zUuid); |
| 415 | db_step(&s); |
| 416 | db_reset(&s); |
| 417 | } |
| 418 | db_finalize(&s); |
| 419 | manifest_clear(&m); |
| 420 | |
| 421 | /* Try again to find the file */ |
| 422 | rid = db_int(0, "SELECT rid FROM vcache" |
| 423 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 424 | } |
| 425 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -290,11 +290,11 @@ | |
| 290 | if( zName[i]=='.' ) z = &zName[i+1]; |
| 291 | } |
| 292 | len = strlen(z); |
| 293 | if( len<sizeof(zSuffix)-1 ){ |
| 294 | strcpy(zSuffix, z); |
| 295 | for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); |
| 296 | first = 0; |
| 297 | last = sizeof(aMime)/sizeof(aMime[0]); |
| 298 | while( first<=last ){ |
| 299 | int c; |
| 300 | i = (first+last)/2; |
| @@ -388,37 +388,36 @@ | |
| 388 | goto doc_not_found; |
| 389 | } |
| 390 | |
| 391 | if( rid==0 ){ |
| 392 | Stmt s; |
| 393 | Manifest *pM; |
| 394 | ManifestFile *pFile; |
| 395 | |
| 396 | /* Add the vid baseline to the cache */ |
| 397 | if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ |
| 398 | db_multi_exec("DELETE FROM vcache"); |
| 399 | } |
| 400 | pM = manifest_get(vid, CFTYPE_MANIFEST); |
| 401 | if( pM==0 ){ |
| 402 | goto doc_not_found; |
| 403 | } |
| 404 | db_prepare(&s, |
| 405 | "INSERT INTO vcache(vid,fname,rid)" |
| 406 | " SELECT %d, :fname, rid FROM blob" |
| 407 | " WHERE uuid=:uuid", |
| 408 | vid |
| 409 | ); |
| 410 | manifest_file_rewind(pM); |
| 411 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 412 | db_bind_text(&s, ":fname", pFile->zName); |
| 413 | db_bind_text(&s, ":uuid", pFile->zUuid); |
| 414 | db_step(&s); |
| 415 | db_reset(&s); |
| 416 | } |
| 417 | db_finalize(&s); |
| 418 | manifest_destroy(pM); |
| 419 | |
| 420 | /* Try again to find the file */ |
| 421 | rid = db_int(0, "SELECT rid FROM vcache" |
| 422 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 423 | } |
| 424 |
+92
-11
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -44,12 +44,11 @@ | ||
| 44 | 44 | default: count++; break; |
| 45 | 45 | } |
| 46 | 46 | i++; |
| 47 | 47 | } |
| 48 | 48 | i = 0; |
| 49 | - zOut = malloc( count+1 ); | |
| 50 | - if( zOut==0 ) return 0; | |
| 49 | + zOut = fossil_malloc( count+1 ); | |
| 51 | 50 | while( n-->0 && (c = *zIn)!=0 ){ |
| 52 | 51 | switch( c ){ |
| 53 | 52 | case '<': |
| 54 | 53 | zOut[i++] = '&'; |
| 55 | 54 | zOut[i++] = 'l'; |
| @@ -100,11 +99,11 @@ | ||
| 100 | 99 | int i = 0; |
| 101 | 100 | int count = 0; |
| 102 | 101 | char *zOut; |
| 103 | 102 | int other; |
| 104 | 103 | # define IsSafeChar(X) \ |
| 105 | - (isalnum(X) || (X)=='.' || (X)=='$' \ | |
| 104 | + (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ | |
| 106 | 105 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 107 | 106 | |
| 108 | 107 | if( zIn==0 ) return 0; |
| 109 | 108 | if( n<0 ) n = strlen(zIn); |
| 110 | 109 | other = encodeSlash ? 'a' : '/'; |
| @@ -115,12 +114,11 @@ | ||
| 115 | 114 | count += 3; |
| 116 | 115 | } |
| 117 | 116 | i++; |
| 118 | 117 | } |
| 119 | 118 | i = 0; |
| 120 | - zOut = malloc( count+1 ); | |
| 121 | - if( zOut==0 ) return 0; | |
| 119 | + zOut = fossil_malloc( count+1 ); | |
| 122 | 120 | while( n-->0 && (c = *zIn)!=0 ){ |
| 123 | 121 | if( IsSafeChar(c) ){ |
| 124 | 122 | zOut[i++] = c; |
| 125 | 123 | }else if( c==' ' ){ |
| 126 | 124 | zOut[i++] = '+'; |
| @@ -234,21 +232,21 @@ | ||
| 234 | 232 | c = zIn[i]; |
| 235 | 233 | if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' |
| 236 | 234 | || c=='\\' ) n++; |
| 237 | 235 | } |
| 238 | 236 | n += nIn; |
| 239 | - zOut = malloc( n+1 ); | |
| 237 | + zOut = fossil_malloc( n+1 ); | |
| 240 | 238 | if( zOut ){ |
| 241 | 239 | for(i=j=0; i<nIn; i++){ |
| 242 | 240 | int c = zIn[i]; |
| 243 | 241 | if( c==0 ){ |
| 244 | 242 | zOut[j++] = '\\'; |
| 245 | 243 | zOut[j++] = '0'; |
| 246 | 244 | }else if( c=='\\' ){ |
| 247 | 245 | zOut[j++] = '\\'; |
| 248 | 246 | zOut[j++] = '\\'; |
| 249 | - }else if( isspace(c) ){ | |
| 247 | + }else if( fossil_isspace(c) ){ | |
| 250 | 248 | zOut[j++] = '\\'; |
| 251 | 249 | switch( c ){ |
| 252 | 250 | case '\n': c = 'n'; break; |
| 253 | 251 | case ' ': c = 's'; break; |
| 254 | 252 | case '\t': c = 't'; break; |
| @@ -269,12 +267,13 @@ | ||
| 269 | 267 | /* |
| 270 | 268 | ** Decode a fossilized string in-place. |
| 271 | 269 | */ |
| 272 | 270 | void defossilize(char *z){ |
| 273 | 271 | int i, j, c; |
| 274 | - for(i=j=0; z[i]; i++){ | |
| 275 | - c = z[i]; | |
| 272 | + for(i=0; (c=z[i])!=0 && c!='\\'; i++){} | |
| 273 | + if( c==0 ) return; | |
| 274 | + for(j=i; (c=z[i])!=0; i++){ | |
| 276 | 275 | if( c=='\\' && z[i+1] ){ |
| 277 | 276 | i++; |
| 278 | 277 | switch( z[i] ){ |
| 279 | 278 | case 'n': c = '\n'; break; |
| 280 | 279 | case 's': c = ' '; break; |
| @@ -310,11 +309,11 @@ | ||
| 310 | 309 | int i, n; |
| 311 | 310 | |
| 312 | 311 | if( nData<=0 ){ |
| 313 | 312 | nData = strlen(zData); |
| 314 | 313 | } |
| 315 | - z64 = malloc( (nData*4)/3 + 8 ); | |
| 314 | + z64 = fossil_malloc( (nData*4)/3 + 8 ); | |
| 316 | 315 | for(i=n=0; i+2<nData; i+=3){ |
| 317 | 316 | z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; |
| 318 | 317 | z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; |
| 319 | 318 | z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; |
| 320 | 319 | z64[n++] = zBase[ zData[i+2] & 0x3f ]; |
| @@ -371,11 +370,11 @@ | ||
| 371 | 370 | for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } |
| 372 | 371 | isInit = 1; |
| 373 | 372 | } |
| 374 | 373 | n64 = strlen(z64); |
| 375 | 374 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 376 | - zData = malloc( (n64*3)/4 + 4 ); | |
| 375 | + zData = fossil_malloc( (n64*3)/4 + 4 ); | |
| 377 | 376 | for(i=j=0; i+3<n64; i+=4){ |
| 378 | 377 | a = trans[z64[i] & 0x7f]; |
| 379 | 378 | b = trans[z64[i+1] & 0x7f]; |
| 380 | 379 | c = trans[z64[i+2] & 0x7f]; |
| 381 | 380 | d = trans[z64[i+3] & 0x7f]; |
| @@ -506,5 +505,87 @@ | ||
| 506 | 505 | while( *z && n-- ){ |
| 507 | 506 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 508 | 507 | z++; |
| 509 | 508 | } |
| 510 | 509 | } |
| 510 | + | |
| 511 | +/* Randomness used for XOR-ing by the obscure() and unobscure() routines */ | |
| 512 | +static const unsigned char aObscurer[16] = { | |
| 513 | + 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, | |
| 514 | + 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 | |
| 515 | +}; | |
| 516 | + | |
| 517 | + | |
| 518 | +/* | |
| 519 | +** Obscure plain text so that it is not easily readable. | |
| 520 | +** | |
| 521 | +** This is used for storing sensitive information (such as passwords) in a | |
| 522 | +** way that prevents their exposure through idle browsing. This is not | |
| 523 | +** encryption. Anybody who really wants the password can still get it. | |
| 524 | +** | |
| 525 | +** The text is XOR-ed with a repeating pattern then converted to hex. | |
| 526 | +** Space to hold the returned string is obtained from malloc and should | |
| 527 | +** be freed by the caller. | |
| 528 | +*/ | |
| 529 | +char *obscure(const char *zIn){ | |
| 530 | + int n, i; | |
| 531 | + unsigned char salt; | |
| 532 | + char *zOut; | |
| 533 | + | |
| 534 | + if( zIn==0 ) return 0; | |
| 535 | + n = strlen(zIn); | |
| 536 | + zOut = fossil_malloc( n*2+3 ); | |
| 537 | + sqlite3_randomness(1, &salt); | |
| 538 | + zOut[n+1] = (char)salt; | |
| 539 | + for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt; | |
| 540 | + encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1); | |
| 541 | + return zOut; | |
| 542 | +} | |
| 543 | + | |
| 544 | +/* | |
| 545 | +** Undo the obscuring of text performed by obscure(). Or, if the input is | |
| 546 | +** not hexadecimal (meaning the input is not the output of obscure()) then | |
| 547 | +** do the equivalent of strdup(). | |
| 548 | +** | |
| 549 | +** The result is memory obtained from malloc that should be freed by the caller. | |
| 550 | +*/ | |
| 551 | +char *unobscure(const char *zIn){ | |
| 552 | + int n, i; | |
| 553 | + unsigned char salt; | |
| 554 | + char *zOut; | |
| 555 | + | |
| 556 | + if( zIn==0 ) return 0; | |
| 557 | + n = strlen(zIn); | |
| 558 | + zOut = fossil_malloc( n + 1 ); | |
| 559 | + if( n<2 | |
| 560 | + || decode16((unsigned char*)zIn, &salt, 2) | |
| 561 | + || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2) | |
| 562 | + ){ | |
| 563 | + memcpy(zOut, zIn, n+1); | |
| 564 | + }else{ | |
| 565 | + n = n/2 - 1; | |
| 566 | + for(i=0; i<n; i++) zOut[i] = zOut[i]^aObscurer[i&0x0f]^salt; | |
| 567 | + zOut[n] = 0; | |
| 568 | + } | |
| 569 | + return zOut; | |
| 570 | +} | |
| 571 | + | |
| 572 | +/* | |
| 573 | +** Command to test obscure() and unobscure(). These commands are also useful | |
| 574 | +** utilities for decoding passwords found in the database. | |
| 575 | +** | |
| 576 | +** COMMAND: test-obscure | |
| 577 | +*/ | |
| 578 | +void test_obscure_cmd(void){ | |
| 579 | + int i; | |
| 580 | + char *z, *z2; | |
| 581 | + for(i=2; i<g.argc; i++){ | |
| 582 | + z = obscure(g.argv[i]); | |
| 583 | + z2 = unobscure(z); | |
| 584 | + printf("OBSCURE: %s -> %s (%s)\n", g.argv[i], z, z2); | |
| 585 | + free(z); | |
| 586 | + free(z2); | |
| 587 | + z = unobscure(g.argv[i]); | |
| 588 | + printf("UNOBSCURE: %s -> %s\n", g.argv[i], z); | |
| 589 | + free(z); | |
| 590 | + } | |
| 591 | +} | |
| 511 | 592 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -44,12 +44,11 @@ | |
| 44 | default: count++; break; |
| 45 | } |
| 46 | i++; |
| 47 | } |
| 48 | i = 0; |
| 49 | zOut = malloc( count+1 ); |
| 50 | if( zOut==0 ) return 0; |
| 51 | while( n-->0 && (c = *zIn)!=0 ){ |
| 52 | switch( c ){ |
| 53 | case '<': |
| 54 | zOut[i++] = '&'; |
| 55 | zOut[i++] = 'l'; |
| @@ -100,11 +99,11 @@ | |
| 100 | int i = 0; |
| 101 | int count = 0; |
| 102 | char *zOut; |
| 103 | int other; |
| 104 | # define IsSafeChar(X) \ |
| 105 | (isalnum(X) || (X)=='.' || (X)=='$' \ |
| 106 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 107 | |
| 108 | if( zIn==0 ) return 0; |
| 109 | if( n<0 ) n = strlen(zIn); |
| 110 | other = encodeSlash ? 'a' : '/'; |
| @@ -115,12 +114,11 @@ | |
| 115 | count += 3; |
| 116 | } |
| 117 | i++; |
| 118 | } |
| 119 | i = 0; |
| 120 | zOut = malloc( count+1 ); |
| 121 | if( zOut==0 ) return 0; |
| 122 | while( n-->0 && (c = *zIn)!=0 ){ |
| 123 | if( IsSafeChar(c) ){ |
| 124 | zOut[i++] = c; |
| 125 | }else if( c==' ' ){ |
| 126 | zOut[i++] = '+'; |
| @@ -234,21 +232,21 @@ | |
| 234 | c = zIn[i]; |
| 235 | if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' |
| 236 | || c=='\\' ) n++; |
| 237 | } |
| 238 | n += nIn; |
| 239 | zOut = malloc( n+1 ); |
| 240 | if( zOut ){ |
| 241 | for(i=j=0; i<nIn; i++){ |
| 242 | int c = zIn[i]; |
| 243 | if( c==0 ){ |
| 244 | zOut[j++] = '\\'; |
| 245 | zOut[j++] = '0'; |
| 246 | }else if( c=='\\' ){ |
| 247 | zOut[j++] = '\\'; |
| 248 | zOut[j++] = '\\'; |
| 249 | }else if( isspace(c) ){ |
| 250 | zOut[j++] = '\\'; |
| 251 | switch( c ){ |
| 252 | case '\n': c = 'n'; break; |
| 253 | case ' ': c = 's'; break; |
| 254 | case '\t': c = 't'; break; |
| @@ -269,12 +267,13 @@ | |
| 269 | /* |
| 270 | ** Decode a fossilized string in-place. |
| 271 | */ |
| 272 | void defossilize(char *z){ |
| 273 | int i, j, c; |
| 274 | for(i=j=0; z[i]; i++){ |
| 275 | c = z[i]; |
| 276 | if( c=='\\' && z[i+1] ){ |
| 277 | i++; |
| 278 | switch( z[i] ){ |
| 279 | case 'n': c = '\n'; break; |
| 280 | case 's': c = ' '; break; |
| @@ -310,11 +309,11 @@ | |
| 310 | int i, n; |
| 311 | |
| 312 | if( nData<=0 ){ |
| 313 | nData = strlen(zData); |
| 314 | } |
| 315 | z64 = malloc( (nData*4)/3 + 8 ); |
| 316 | for(i=n=0; i+2<nData; i+=3){ |
| 317 | z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; |
| 318 | z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; |
| 319 | z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; |
| 320 | z64[n++] = zBase[ zData[i+2] & 0x3f ]; |
| @@ -371,11 +370,11 @@ | |
| 371 | for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } |
| 372 | isInit = 1; |
| 373 | } |
| 374 | n64 = strlen(z64); |
| 375 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 376 | zData = malloc( (n64*3)/4 + 4 ); |
| 377 | for(i=j=0; i+3<n64; i+=4){ |
| 378 | a = trans[z64[i] & 0x7f]; |
| 379 | b = trans[z64[i+1] & 0x7f]; |
| 380 | c = trans[z64[i+2] & 0x7f]; |
| 381 | d = trans[z64[i+3] & 0x7f]; |
| @@ -506,5 +505,87 @@ | |
| 506 | while( *z && n-- ){ |
| 507 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 508 | z++; |
| 509 | } |
| 510 | } |
| 511 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -44,12 +44,11 @@ | |
| 44 | default: count++; break; |
| 45 | } |
| 46 | i++; |
| 47 | } |
| 48 | i = 0; |
| 49 | zOut = fossil_malloc( count+1 ); |
| 50 | while( n-->0 && (c = *zIn)!=0 ){ |
| 51 | switch( c ){ |
| 52 | case '<': |
| 53 | zOut[i++] = '&'; |
| 54 | zOut[i++] = 'l'; |
| @@ -100,11 +99,11 @@ | |
| 99 | int i = 0; |
| 100 | int count = 0; |
| 101 | char *zOut; |
| 102 | int other; |
| 103 | # define IsSafeChar(X) \ |
| 104 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 105 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 106 | |
| 107 | if( zIn==0 ) return 0; |
| 108 | if( n<0 ) n = strlen(zIn); |
| 109 | other = encodeSlash ? 'a' : '/'; |
| @@ -115,12 +114,11 @@ | |
| 114 | count += 3; |
| 115 | } |
| 116 | i++; |
| 117 | } |
| 118 | i = 0; |
| 119 | zOut = fossil_malloc( count+1 ); |
| 120 | while( n-->0 && (c = *zIn)!=0 ){ |
| 121 | if( IsSafeChar(c) ){ |
| 122 | zOut[i++] = c; |
| 123 | }else if( c==' ' ){ |
| 124 | zOut[i++] = '+'; |
| @@ -234,21 +232,21 @@ | |
| 232 | c = zIn[i]; |
| 233 | if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v' |
| 234 | || c=='\\' ) n++; |
| 235 | } |
| 236 | n += nIn; |
| 237 | zOut = fossil_malloc( n+1 ); |
| 238 | if( zOut ){ |
| 239 | for(i=j=0; i<nIn; i++){ |
| 240 | int c = zIn[i]; |
| 241 | if( c==0 ){ |
| 242 | zOut[j++] = '\\'; |
| 243 | zOut[j++] = '0'; |
| 244 | }else if( c=='\\' ){ |
| 245 | zOut[j++] = '\\'; |
| 246 | zOut[j++] = '\\'; |
| 247 | }else if( fossil_isspace(c) ){ |
| 248 | zOut[j++] = '\\'; |
| 249 | switch( c ){ |
| 250 | case '\n': c = 'n'; break; |
| 251 | case ' ': c = 's'; break; |
| 252 | case '\t': c = 't'; break; |
| @@ -269,12 +267,13 @@ | |
| 267 | /* |
| 268 | ** Decode a fossilized string in-place. |
| 269 | */ |
| 270 | void defossilize(char *z){ |
| 271 | int i, j, c; |
| 272 | for(i=0; (c=z[i])!=0 && c!='\\'; i++){} |
| 273 | if( c==0 ) return; |
| 274 | for(j=i; (c=z[i])!=0; i++){ |
| 275 | if( c=='\\' && z[i+1] ){ |
| 276 | i++; |
| 277 | switch( z[i] ){ |
| 278 | case 'n': c = '\n'; break; |
| 279 | case 's': c = ' '; break; |
| @@ -310,11 +309,11 @@ | |
| 309 | int i, n; |
| 310 | |
| 311 | if( nData<=0 ){ |
| 312 | nData = strlen(zData); |
| 313 | } |
| 314 | z64 = fossil_malloc( (nData*4)/3 + 8 ); |
| 315 | for(i=n=0; i+2<nData; i+=3){ |
| 316 | z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; |
| 317 | z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; |
| 318 | z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; |
| 319 | z64[n++] = zBase[ zData[i+2] & 0x3f ]; |
| @@ -371,11 +370,11 @@ | |
| 370 | for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } |
| 371 | isInit = 1; |
| 372 | } |
| 373 | n64 = strlen(z64); |
| 374 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 375 | zData = fossil_malloc( (n64*3)/4 + 4 ); |
| 376 | for(i=j=0; i+3<n64; i+=4){ |
| 377 | a = trans[z64[i] & 0x7f]; |
| 378 | b = trans[z64[i+1] & 0x7f]; |
| 379 | c = trans[z64[i+2] & 0x7f]; |
| 380 | d = trans[z64[i+3] & 0x7f]; |
| @@ -506,5 +505,87 @@ | |
| 505 | while( *z && n-- ){ |
| 506 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 507 | z++; |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ |
| 512 | static const unsigned char aObscurer[16] = { |
| 513 | 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, |
| 514 | 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 |
| 515 | }; |
| 516 | |
| 517 | |
| 518 | /* |
| 519 | ** Obscure plain text so that it is not easily readable. |
| 520 | ** |
| 521 | ** This is used for storing sensitive information (such as passwords) in a |
| 522 | ** way that prevents their exposure through idle browsing. This is not |
| 523 | ** encryption. Anybody who really wants the password can still get it. |
| 524 | ** |
| 525 | ** The text is XOR-ed with a repeating pattern then converted to hex. |
| 526 | ** Space to hold the returned string is obtained from malloc and should |
| 527 | ** be freed by the caller. |
| 528 | */ |
| 529 | char *obscure(const char *zIn){ |
| 530 | int n, i; |
| 531 | unsigned char salt; |
| 532 | char *zOut; |
| 533 | |
| 534 | if( zIn==0 ) return 0; |
| 535 | n = strlen(zIn); |
| 536 | zOut = fossil_malloc( n*2+3 ); |
| 537 | sqlite3_randomness(1, &salt); |
| 538 | zOut[n+1] = (char)salt; |
| 539 | for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt; |
| 540 | encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1); |
| 541 | return zOut; |
| 542 | } |
| 543 | |
| 544 | /* |
| 545 | ** Undo the obscuring of text performed by obscure(). Or, if the input is |
| 546 | ** not hexadecimal (meaning the input is not the output of obscure()) then |
| 547 | ** do the equivalent of strdup(). |
| 548 | ** |
| 549 | ** The result is memory obtained from malloc that should be freed by the caller. |
| 550 | */ |
| 551 | char *unobscure(const char *zIn){ |
| 552 | int n, i; |
| 553 | unsigned char salt; |
| 554 | char *zOut; |
| 555 | |
| 556 | if( zIn==0 ) return 0; |
| 557 | n = strlen(zIn); |
| 558 | zOut = fossil_malloc( n + 1 ); |
| 559 | if( n<2 |
| 560 | || decode16((unsigned char*)zIn, &salt, 2) |
| 561 | || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2) |
| 562 | ){ |
| 563 | memcpy(zOut, zIn, n+1); |
| 564 | }else{ |
| 565 | n = n/2 - 1; |
| 566 | for(i=0; i<n; i++) zOut[i] = zOut[i]^aObscurer[i&0x0f]^salt; |
| 567 | zOut[n] = 0; |
| 568 | } |
| 569 | return zOut; |
| 570 | } |
| 571 | |
| 572 | /* |
| 573 | ** Command to test obscure() and unobscure(). These commands are also useful |
| 574 | ** utilities for decoding passwords found in the database. |
| 575 | ** |
| 576 | ** COMMAND: test-obscure |
| 577 | */ |
| 578 | void test_obscure_cmd(void){ |
| 579 | int i; |
| 580 | char *z, *z2; |
| 581 | for(i=2; i<g.argc; i++){ |
| 582 | z = obscure(g.argv[i]); |
| 583 | z2 = unobscure(z); |
| 584 | printf("OBSCURE: %s -> %s (%s)\n", g.argv[i], z, z2); |
| 585 | free(z); |
| 586 | free(z2); |
| 587 | z = unobscure(g.argv[i]); |
| 588 | printf("UNOBSCURE: %s -> %s\n", g.argv[i], z); |
| 589 | free(z); |
| 590 | } |
| 591 | } |
| 592 |
+27
-30
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -46,11 +46,16 @@ | ||
| 46 | 46 | free(zEventId); |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | /* |
| 50 | 50 | ** WEBPAGE: event |
| 51 | -** URL: /event?name=EVENTID&detail=BOOLEAN&aid=ARTIFACTID | |
| 51 | +** URL: /event | |
| 52 | +** PARAMETERS: | |
| 53 | +** | |
| 54 | +** name=EVENTID // Identify the event to display EVENTID must be complete | |
| 55 | +** detail=BOOLEAN // Show details if TRUE. Default is FALSE. Optional. | |
| 56 | +** aid=ARTIFACTID // Which specific version of the event. Optional. | |
| 52 | 57 | ** |
| 53 | 58 | ** Display an existing event identified by EVENTID |
| 54 | 59 | */ |
| 55 | 60 | void event_page(void){ |
| 56 | 61 | int rid = 0; /* rid of the event artifact */ |
| @@ -58,12 +63,11 @@ | ||
| 58 | 63 | const char *zEventId; /* Event identifier */ |
| 59 | 64 | char *zETime; /* Time of the event */ |
| 60 | 65 | char *zATime; /* Time the artifact was created */ |
| 61 | 66 | int specRid; /* rid specified by aid= parameter */ |
| 62 | 67 | int prevRid, nextRid; /* Previous or next edits of this event */ |
| 63 | - Manifest m; /* Parsed event artifact */ | |
| 64 | - Blob content; /* Original event artifact content */ | |
| 68 | + Manifest *pEvent; /* Parsed event artifact */ | |
| 65 | 69 | Blob fullbody; /* Complete content of the event body */ |
| 66 | 70 | Blob title; /* Title extracted from the event body */ |
| 67 | 71 | Blob tail; /* Event body that comes after the title */ |
| 68 | 72 | Stmt q1; /* Query to search for the event */ |
| 69 | 73 | int showDetail; /* True to show details */ |
| @@ -82,11 +86,11 @@ | ||
| 82 | 86 | zUuid = (char*)P("aid"); |
| 83 | 87 | specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0; |
| 84 | 88 | rid = nextRid = prevRid = 0; |
| 85 | 89 | db_prepare(&q1, |
| 86 | 90 | "SELECT rid FROM tagxref" |
| 87 | - " WHERE tagid=(SELECT tagid FROM tag WHERE tagname='event-%q')" | |
| 91 | + " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB 'event-%q*')" | |
| 88 | 92 | " ORDER BY mtime DESC", |
| 89 | 93 | zEventId |
| 90 | 94 | ); |
| 91 | 95 | while( db_step(&q1)==SQLITE_ROW ){ |
| 92 | 96 | nextRid = rid; |
| @@ -108,18 +112,15 @@ | ||
| 108 | 112 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 109 | 113 | showDetail = atoi(PD("detail","0")); |
| 110 | 114 | |
| 111 | 115 | /* Extract the event content. |
| 112 | 116 | */ |
| 113 | - memset(&m, 0, sizeof(m)); | |
| 114 | - blob_zero(&m.content); | |
| 115 | - content_get(rid, &content); | |
| 116 | - manifest_parse(&m, &content); | |
| 117 | - if( m.type!=CFTYPE_EVENT ){ | |
| 117 | + pEvent = manifest_get(rid, CFTYPE_EVENT); | |
| 118 | + if( pEvent==0 ){ | |
| 118 | 119 | fossil_panic("Object #%d is not an event", rid); |
| 119 | 120 | } |
| 120 | - blob_init(&fullbody, m.zWiki, -1); | |
| 121 | + blob_init(&fullbody, pEvent->zWiki, -1); | |
| 121 | 122 | if( wiki_find_title(&fullbody, &title, &tail) ){ |
| 122 | 123 | style_header(blob_str(&title)); |
| 123 | 124 | }else{ |
| 124 | 125 | style_header("Event %S", zEventId); |
| 125 | 126 | tail = fullbody; |
| @@ -126,11 +127,11 @@ | ||
| 126 | 127 | } |
| 127 | 128 | if( g.okWrWiki && g.okWrite && nextRid==0 ){ |
| 128 | 129 | style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s", |
| 129 | 130 | g.zTop, zEventId); |
| 130 | 131 | } |
| 131 | - zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate); | |
| 132 | + zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); | |
| 132 | 133 | style_submenu_element("Context", "Context", "%s/timeline?c=%T", |
| 133 | 134 | g.zTop, zETime); |
| 134 | 135 | if( g.okHistory ){ |
| 135 | 136 | if( showDetail ){ |
| 136 | 137 | style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s", |
| @@ -161,37 +162,37 @@ | ||
| 161 | 162 | if( showDetail && g.okHistory ){ |
| 162 | 163 | int i; |
| 163 | 164 | const char *zClr = 0; |
| 164 | 165 | Blob comment; |
| 165 | 166 | |
| 166 | - zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate); | |
| 167 | + zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate); | |
| 167 | 168 | @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at |
| 168 | 169 | @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>] |
| 169 | - @ entered by user <b>%h(m.zUser)</b> on | |
| 170 | + @ entered by user <b>%h(pEvent->zUser)</b> on | |
| 170 | 171 | @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p> |
| 171 | 172 | @ <blockquote> |
| 172 | - for(i=0; i<m.nTag; i++){ | |
| 173 | - if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){ | |
| 174 | - zClr = m.aTag[i].zValue; | |
| 173 | + for(i=0; i<pEvent->nTag; i++){ | |
| 174 | + if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){ | |
| 175 | + zClr = pEvent->aTag[i].zValue; | |
| 175 | 176 | } |
| 176 | 177 | } |
| 177 | 178 | if( zClr && zClr[0]==0 ) zClr = 0; |
| 178 | 179 | if( zClr ){ |
| 179 | 180 | @ <div style="background-color: %h(zClr);"> |
| 180 | 181 | }else{ |
| 181 | 182 | @ <div> |
| 182 | 183 | } |
| 183 | - blob_init(&comment, m.zComment, -1); | |
| 184 | + blob_init(&comment, pEvent->zComment, -1); | |
| 184 | 185 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 185 | 186 | blob_reset(&comment); |
| 186 | 187 | @ </div> |
| 187 | 188 | @ </blockquote><hr /> |
| 188 | 189 | } |
| 189 | 190 | |
| 190 | 191 | wiki_convert(&tail, 0, 0); |
| 191 | 192 | style_footer(); |
| 192 | - manifest_clear(&m); | |
| 193 | + manifest_destroy(pEvent); | |
| 193 | 194 | } |
| 194 | 195 | |
| 195 | 196 | /* |
| 196 | 197 | ** WEBPAGE: eventedit |
| 197 | 198 | ** URL: /eventedit?name=EVENTID |
| @@ -254,22 +255,18 @@ | ||
| 254 | 255 | |
| 255 | 256 | /* If editing an existing event, extract the key fields to use as |
| 256 | 257 | ** a starting point for the edit. |
| 257 | 258 | */ |
| 258 | 259 | if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){ |
| 259 | - Manifest m; | |
| 260 | - Blob content; | |
| 261 | - memset(&m, 0, sizeof(m)); | |
| 262 | - blob_zero(&m.content); | |
| 263 | - content_get(rid, &content); | |
| 264 | - manifest_parse(&m, &content); | |
| 265 | - if( m.type==CFTYPE_EVENT ){ | |
| 266 | - if( zBody==0 ) zBody = m.zWiki; | |
| 260 | + Manifest *pEvent; | |
| 261 | + pEvent = manifest_get(rid, CFTYPE_EVENT); | |
| 262 | + if( pEvent && pEvent->type==CFTYPE_EVENT ){ | |
| 263 | + if( zBody==0 ) zBody = pEvent->zWiki; | |
| 267 | 264 | if( zETime==0 ){ |
| 268 | - zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate); | |
| 265 | + zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); | |
| 269 | 266 | } |
| 270 | - if( zComment==0 ) zComment = m.zComment; | |
| 267 | + if( zComment==0 ) zComment = pEvent->zComment; | |
| 271 | 268 | } |
| 272 | 269 | if( zTags==0 ){ |
| 273 | 270 | zTags = db_text(0, |
| 274 | 271 | "SELECT group_concat(substr(tagname,5),', ')" |
| 275 | 272 | " FROM tagxref, tag" |
| @@ -316,12 +313,12 @@ | ||
| 316 | 313 | |
| 317 | 314 | /* Collapse all sequences of whitespace and "," characters into |
| 318 | 315 | ** a single space character */ |
| 319 | 316 | zBlob = blob_str(&tags); |
| 320 | 317 | for(i=j=0; zBlob[i]; i++, j++){ |
| 321 | - if( blob_isspace(zBlob[i]) || zBlob[i]==',' ){ | |
| 322 | - while( blob_isspace(zBlob[i+1]) ){ i++; } | |
| 318 | + if( fossil_isspace(zBlob[i]) || zBlob[i]==',' ){ | |
| 319 | + while( fossil_isspace(zBlob[i+1]) ){ i++; } | |
| 323 | 320 | zBlob[j] = ' '; |
| 324 | 321 | }else{ |
| 325 | 322 | zBlob[j] = zBlob[i]; |
| 326 | 323 | } |
| 327 | 324 | } |
| 328 | 325 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -46,11 +46,16 @@ | |
| 46 | free(zEventId); |
| 47 | } |
| 48 | |
| 49 | /* |
| 50 | ** WEBPAGE: event |
| 51 | ** URL: /event?name=EVENTID&detail=BOOLEAN&aid=ARTIFACTID |
| 52 | ** |
| 53 | ** Display an existing event identified by EVENTID |
| 54 | */ |
| 55 | void event_page(void){ |
| 56 | int rid = 0; /* rid of the event artifact */ |
| @@ -58,12 +63,11 @@ | |
| 58 | const char *zEventId; /* Event identifier */ |
| 59 | char *zETime; /* Time of the event */ |
| 60 | char *zATime; /* Time the artifact was created */ |
| 61 | int specRid; /* rid specified by aid= parameter */ |
| 62 | int prevRid, nextRid; /* Previous or next edits of this event */ |
| 63 | Manifest m; /* Parsed event artifact */ |
| 64 | Blob content; /* Original event artifact content */ |
| 65 | Blob fullbody; /* Complete content of the event body */ |
| 66 | Blob title; /* Title extracted from the event body */ |
| 67 | Blob tail; /* Event body that comes after the title */ |
| 68 | Stmt q1; /* Query to search for the event */ |
| 69 | int showDetail; /* True to show details */ |
| @@ -82,11 +86,11 @@ | |
| 82 | zUuid = (char*)P("aid"); |
| 83 | specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0; |
| 84 | rid = nextRid = prevRid = 0; |
| 85 | db_prepare(&q1, |
| 86 | "SELECT rid FROM tagxref" |
| 87 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname='event-%q')" |
| 88 | " ORDER BY mtime DESC", |
| 89 | zEventId |
| 90 | ); |
| 91 | while( db_step(&q1)==SQLITE_ROW ){ |
| 92 | nextRid = rid; |
| @@ -108,18 +112,15 @@ | |
| 108 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 109 | showDetail = atoi(PD("detail","0")); |
| 110 | |
| 111 | /* Extract the event content. |
| 112 | */ |
| 113 | memset(&m, 0, sizeof(m)); |
| 114 | blob_zero(&m.content); |
| 115 | content_get(rid, &content); |
| 116 | manifest_parse(&m, &content); |
| 117 | if( m.type!=CFTYPE_EVENT ){ |
| 118 | fossil_panic("Object #%d is not an event", rid); |
| 119 | } |
| 120 | blob_init(&fullbody, m.zWiki, -1); |
| 121 | if( wiki_find_title(&fullbody, &title, &tail) ){ |
| 122 | style_header(blob_str(&title)); |
| 123 | }else{ |
| 124 | style_header("Event %S", zEventId); |
| 125 | tail = fullbody; |
| @@ -126,11 +127,11 @@ | |
| 126 | } |
| 127 | if( g.okWrWiki && g.okWrite && nextRid==0 ){ |
| 128 | style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s", |
| 129 | g.zTop, zEventId); |
| 130 | } |
| 131 | zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate); |
| 132 | style_submenu_element("Context", "Context", "%s/timeline?c=%T", |
| 133 | g.zTop, zETime); |
| 134 | if( g.okHistory ){ |
| 135 | if( showDetail ){ |
| 136 | style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s", |
| @@ -161,37 +162,37 @@ | |
| 161 | if( showDetail && g.okHistory ){ |
| 162 | int i; |
| 163 | const char *zClr = 0; |
| 164 | Blob comment; |
| 165 | |
| 166 | zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate); |
| 167 | @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at |
| 168 | @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>] |
| 169 | @ entered by user <b>%h(m.zUser)</b> on |
| 170 | @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p> |
| 171 | @ <blockquote> |
| 172 | for(i=0; i<m.nTag; i++){ |
| 173 | if( strcmp(m.aTag[i].zName,"+bgcolor")==0 ){ |
| 174 | zClr = m.aTag[i].zValue; |
| 175 | } |
| 176 | } |
| 177 | if( zClr && zClr[0]==0 ) zClr = 0; |
| 178 | if( zClr ){ |
| 179 | @ <div style="background-color: %h(zClr);"> |
| 180 | }else{ |
| 181 | @ <div> |
| 182 | } |
| 183 | blob_init(&comment, m.zComment, -1); |
| 184 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 185 | blob_reset(&comment); |
| 186 | @ </div> |
| 187 | @ </blockquote><hr /> |
| 188 | } |
| 189 | |
| 190 | wiki_convert(&tail, 0, 0); |
| 191 | style_footer(); |
| 192 | manifest_clear(&m); |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | ** WEBPAGE: eventedit |
| 197 | ** URL: /eventedit?name=EVENTID |
| @@ -254,22 +255,18 @@ | |
| 254 | |
| 255 | /* If editing an existing event, extract the key fields to use as |
| 256 | ** a starting point for the edit. |
| 257 | */ |
| 258 | if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){ |
| 259 | Manifest m; |
| 260 | Blob content; |
| 261 | memset(&m, 0, sizeof(m)); |
| 262 | blob_zero(&m.content); |
| 263 | content_get(rid, &content); |
| 264 | manifest_parse(&m, &content); |
| 265 | if( m.type==CFTYPE_EVENT ){ |
| 266 | if( zBody==0 ) zBody = m.zWiki; |
| 267 | if( zETime==0 ){ |
| 268 | zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate); |
| 269 | } |
| 270 | if( zComment==0 ) zComment = m.zComment; |
| 271 | } |
| 272 | if( zTags==0 ){ |
| 273 | zTags = db_text(0, |
| 274 | "SELECT group_concat(substr(tagname,5),', ')" |
| 275 | " FROM tagxref, tag" |
| @@ -316,12 +313,12 @@ | |
| 316 | |
| 317 | /* Collapse all sequences of whitespace and "," characters into |
| 318 | ** a single space character */ |
| 319 | zBlob = blob_str(&tags); |
| 320 | for(i=j=0; zBlob[i]; i++, j++){ |
| 321 | if( blob_isspace(zBlob[i]) || zBlob[i]==',' ){ |
| 322 | while( blob_isspace(zBlob[i+1]) ){ i++; } |
| 323 | zBlob[j] = ' '; |
| 324 | }else{ |
| 325 | zBlob[j] = zBlob[i]; |
| 326 | } |
| 327 | } |
| 328 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -46,11 +46,16 @@ | |
| 46 | free(zEventId); |
| 47 | } |
| 48 | |
| 49 | /* |
| 50 | ** WEBPAGE: event |
| 51 | ** URL: /event |
| 52 | ** PARAMETERS: |
| 53 | ** |
| 54 | ** name=EVENTID // Identify the event to display EVENTID must be complete |
| 55 | ** detail=BOOLEAN // Show details if TRUE. Default is FALSE. Optional. |
| 56 | ** aid=ARTIFACTID // Which specific version of the event. Optional. |
| 57 | ** |
| 58 | ** Display an existing event identified by EVENTID |
| 59 | */ |
| 60 | void event_page(void){ |
| 61 | int rid = 0; /* rid of the event artifact */ |
| @@ -58,12 +63,11 @@ | |
| 63 | const char *zEventId; /* Event identifier */ |
| 64 | char *zETime; /* Time of the event */ |
| 65 | char *zATime; /* Time the artifact was created */ |
| 66 | int specRid; /* rid specified by aid= parameter */ |
| 67 | int prevRid, nextRid; /* Previous or next edits of this event */ |
| 68 | Manifest *pEvent; /* Parsed event artifact */ |
| 69 | Blob fullbody; /* Complete content of the event body */ |
| 70 | Blob title; /* Title extracted from the event body */ |
| 71 | Blob tail; /* Event body that comes after the title */ |
| 72 | Stmt q1; /* Query to search for the event */ |
| 73 | int showDetail; /* True to show details */ |
| @@ -82,11 +86,11 @@ | |
| 86 | zUuid = (char*)P("aid"); |
| 87 | specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0; |
| 88 | rid = nextRid = prevRid = 0; |
| 89 | db_prepare(&q1, |
| 90 | "SELECT rid FROM tagxref" |
| 91 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB 'event-%q*')" |
| 92 | " ORDER BY mtime DESC", |
| 93 | zEventId |
| 94 | ); |
| 95 | while( db_step(&q1)==SQLITE_ROW ){ |
| 96 | nextRid = rid; |
| @@ -108,18 +112,15 @@ | |
| 112 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 113 | showDetail = atoi(PD("detail","0")); |
| 114 | |
| 115 | /* Extract the event content. |
| 116 | */ |
| 117 | pEvent = manifest_get(rid, CFTYPE_EVENT); |
| 118 | if( pEvent==0 ){ |
| 119 | fossil_panic("Object #%d is not an event", rid); |
| 120 | } |
| 121 | blob_init(&fullbody, pEvent->zWiki, -1); |
| 122 | if( wiki_find_title(&fullbody, &title, &tail) ){ |
| 123 | style_header(blob_str(&title)); |
| 124 | }else{ |
| 125 | style_header("Event %S", zEventId); |
| 126 | tail = fullbody; |
| @@ -126,11 +127,11 @@ | |
| 127 | } |
| 128 | if( g.okWrWiki && g.okWrite && nextRid==0 ){ |
| 129 | style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s", |
| 130 | g.zTop, zEventId); |
| 131 | } |
| 132 | zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); |
| 133 | style_submenu_element("Context", "Context", "%s/timeline?c=%T", |
| 134 | g.zTop, zETime); |
| 135 | if( g.okHistory ){ |
| 136 | if( showDetail ){ |
| 137 | style_submenu_element("Plain", "Plain", "%s/event?name=%s&aid=%s", |
| @@ -161,37 +162,37 @@ | |
| 162 | if( showDetail && g.okHistory ){ |
| 163 | int i; |
| 164 | const char *zClr = 0; |
| 165 | Blob comment; |
| 166 | |
| 167 | zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate); |
| 168 | @ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at |
| 169 | @ [<a href="%s(g.zTop)/timeline?c=%T(zETime)">%s(zETime)</a>] |
| 170 | @ entered by user <b>%h(pEvent->zUser)</b> on |
| 171 | @ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p> |
| 172 | @ <blockquote> |
| 173 | for(i=0; i<pEvent->nTag; i++){ |
| 174 | if( strcmp(pEvent->aTag[i].zName,"+bgcolor")==0 ){ |
| 175 | zClr = pEvent->aTag[i].zValue; |
| 176 | } |
| 177 | } |
| 178 | if( zClr && zClr[0]==0 ) zClr = 0; |
| 179 | if( zClr ){ |
| 180 | @ <div style="background-color: %h(zClr);"> |
| 181 | }else{ |
| 182 | @ <div> |
| 183 | } |
| 184 | blob_init(&comment, pEvent->zComment, -1); |
| 185 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 186 | blob_reset(&comment); |
| 187 | @ </div> |
| 188 | @ </blockquote><hr /> |
| 189 | } |
| 190 | |
| 191 | wiki_convert(&tail, 0, 0); |
| 192 | style_footer(); |
| 193 | manifest_destroy(pEvent); |
| 194 | } |
| 195 | |
| 196 | /* |
| 197 | ** WEBPAGE: eventedit |
| 198 | ** URL: /eventedit?name=EVENTID |
| @@ -254,22 +255,18 @@ | |
| 255 | |
| 256 | /* If editing an existing event, extract the key fields to use as |
| 257 | ** a starting point for the edit. |
| 258 | */ |
| 259 | if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0) ){ |
| 260 | Manifest *pEvent; |
| 261 | pEvent = manifest_get(rid, CFTYPE_EVENT); |
| 262 | if( pEvent && pEvent->type==CFTYPE_EVENT ){ |
| 263 | if( zBody==0 ) zBody = pEvent->zWiki; |
| 264 | if( zETime==0 ){ |
| 265 | zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate); |
| 266 | } |
| 267 | if( zComment==0 ) zComment = pEvent->zComment; |
| 268 | } |
| 269 | if( zTags==0 ){ |
| 270 | zTags = db_text(0, |
| 271 | "SELECT group_concat(substr(tagname,5),', ')" |
| 272 | " FROM tagxref, tag" |
| @@ -316,12 +313,12 @@ | |
| 313 | |
| 314 | /* Collapse all sequences of whitespace and "," characters into |
| 315 | ** a single space character */ |
| 316 | zBlob = blob_str(&tags); |
| 317 | for(i=j=0; zBlob[i]; i++, j++){ |
| 318 | if( fossil_isspace(zBlob[i]) || zBlob[i]==',' ){ |
| 319 | while( fossil_isspace(zBlob[i+1]) ){ i++; } |
| 320 | zBlob[j] = ' '; |
| 321 | }else{ |
| 322 | zBlob[j] = zBlob[i]; |
| 323 | } |
| 324 | } |
| 325 |
+1
-1
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -131,11 +131,11 @@ | ||
| 131 | 131 | TAG_BRANCH, |
| 132 | 132 | zFilename |
| 133 | 133 | ); |
| 134 | 134 | blob_zero(&title); |
| 135 | 135 | blob_appendf(&title, "History of "); |
| 136 | - hyperlinked_path(zFilename, &title); | |
| 136 | + hyperlinked_path(zFilename, &title, 0); | |
| 137 | 137 | @ <h2>%b(&title)</h2> |
| 138 | 138 | blob_reset(&title); |
| 139 | 139 | pGraph = graph_init(); |
| 140 | 140 | @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> |
| 141 | 141 | @ <table class="timelineTable"> |
| 142 | 142 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -131,11 +131,11 @@ | |
| 131 | TAG_BRANCH, |
| 132 | zFilename |
| 133 | ); |
| 134 | blob_zero(&title); |
| 135 | blob_appendf(&title, "History of "); |
| 136 | hyperlinked_path(zFilename, &title); |
| 137 | @ <h2>%b(&title)</h2> |
| 138 | blob_reset(&title); |
| 139 | pGraph = graph_init(); |
| 140 | @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> |
| 141 | @ <table class="timelineTable"> |
| 142 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -131,11 +131,11 @@ | |
| 131 | TAG_BRANCH, |
| 132 | zFilename |
| 133 | ); |
| 134 | blob_zero(&title); |
| 135 | blob_appendf(&title, "History of "); |
| 136 | hyperlinked_path(zFilename, &title, 0); |
| 137 | @ <h2>%b(&title)</h2> |
| 138 | blob_reset(&title); |
| 139 | pGraph = graph_init(); |
| 140 | @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> |
| 141 | @ <table class="timelineTable"> |
| 142 |
+2
-4
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -72,12 +72,11 @@ | ||
| 72 | 72 | /* |
| 73 | 73 | ** Malloc for zeroed space. Panic if unable to provide the |
| 74 | 74 | ** requested space. |
| 75 | 75 | */ |
| 76 | 76 | void *safeMalloc(int nByte){ |
| 77 | - void *p = malloc(nByte); | |
| 78 | - if( p==0 ) fossil_panic("out of memory"); | |
| 77 | + void *p = fossil_malloc(nByte); | |
| 79 | 78 | memset(p, 0, nByte); |
| 80 | 79 | return p; |
| 81 | 80 | } |
| 82 | 81 | |
| 83 | 82 | /* |
| @@ -148,12 +147,11 @@ | ||
| 148 | 147 | int i; |
| 149 | 148 | for(i=0; i<p->nBranch; i++){ |
| 150 | 149 | if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; |
| 151 | 150 | } |
| 152 | 151 | p->nBranch++; |
| 153 | - p->azBranch = realloc(p->azBranch, sizeof(char*)*p->nBranch); | |
| 154 | - if( p->azBranch==0 ) fossil_panic("out of memory"); | |
| 152 | + p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch); | |
| 155 | 153 | p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); |
| 156 | 154 | return p->azBranch[p->nBranch-1]; |
| 157 | 155 | } |
| 158 | 156 | |
| 159 | 157 | /* |
| 160 | 158 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -72,12 +72,11 @@ | |
| 72 | /* |
| 73 | ** Malloc for zeroed space. Panic if unable to provide the |
| 74 | ** requested space. |
| 75 | */ |
| 76 | void *safeMalloc(int nByte){ |
| 77 | void *p = malloc(nByte); |
| 78 | if( p==0 ) fossil_panic("out of memory"); |
| 79 | memset(p, 0, nByte); |
| 80 | return p; |
| 81 | } |
| 82 | |
| 83 | /* |
| @@ -148,12 +147,11 @@ | |
| 148 | int i; |
| 149 | for(i=0; i<p->nBranch; i++){ |
| 150 | if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; |
| 151 | } |
| 152 | p->nBranch++; |
| 153 | p->azBranch = realloc(p->azBranch, sizeof(char*)*p->nBranch); |
| 154 | if( p->azBranch==0 ) fossil_panic("out of memory"); |
| 155 | p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); |
| 156 | return p->azBranch[p->nBranch-1]; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -72,12 +72,11 @@ | |
| 72 | /* |
| 73 | ** Malloc for zeroed space. Panic if unable to provide the |
| 74 | ** requested space. |
| 75 | */ |
| 76 | void *safeMalloc(int nByte){ |
| 77 | void *p = fossil_malloc(nByte); |
| 78 | memset(p, 0, nByte); |
| 79 | return p; |
| 80 | } |
| 81 | |
| 82 | /* |
| @@ -148,12 +147,11 @@ | |
| 147 | int i; |
| 148 | for(i=0; i<p->nBranch; i++){ |
| 149 | if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; |
| 150 | } |
| 151 | p->nBranch++; |
| 152 | p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch); |
| 153 | p->azBranch[p->nBranch-1] = mprintf("%s", zBranch); |
| 154 | return p->azBranch[p->nBranch-1]; |
| 155 | } |
| 156 | |
| 157 | /* |
| 158 |
+9
-6
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -60,11 +60,11 @@ | ||
| 60 | 60 | zPw = 0; |
| 61 | 61 | }else{ |
| 62 | 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | 63 | url_prompt_for_password(); |
| 64 | 64 | zPw = g.urlPasswd; |
| 65 | - if( !g.dontKeepUrl ) db_set("last-sync-pw", zPw, 0); | |
| 65 | + if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); | |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | /* The login card wants the SHA1 hash of the password, so convert the |
| 69 | 69 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 70 | 70 | ** |
| @@ -103,11 +103,11 @@ | ||
| 103 | 103 | if( i>0 && g.urlPath[i-1]=='/' ){ |
| 104 | 104 | zSep = ""; |
| 105 | 105 | }else{ |
| 106 | 106 | zSep = "/"; |
| 107 | 107 | } |
| 108 | - blob_appendf(pHdr, "POST %s%sxfer HTTP/1.0\r\n", g.urlPath, zSep); | |
| 108 | + blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); | |
| 109 | 109 | if( g.urlProxyAuth ){ |
| 110 | 110 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 111 | 111 | } |
| 112 | 112 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 113 | 113 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| @@ -210,15 +210,15 @@ | ||
| 210 | 210 | closeConnection = 1; |
| 211 | 211 | }else{ |
| 212 | 212 | closeConnection = 0; |
| 213 | 213 | } |
| 214 | 214 | }else if( strncasecmp(zLine, "content-length:", 15)==0 ){ |
| 215 | - for(i=15; isspace(zLine[i]); i++){} | |
| 215 | + for(i=15; fossil_isspace(zLine[i]); i++){} | |
| 216 | 216 | iLength = atoi(&zLine[i]); |
| 217 | 217 | }else if( strncasecmp(zLine, "connection:", 11)==0 ){ |
| 218 | 218 | char c; |
| 219 | - for(i=11; isspace(zLine[i]); i++){} | |
| 219 | + for(i=11; fossil_isspace(zLine[i]); i++){} | |
| 220 | 220 | c = zLine[i]; |
| 221 | 221 | if( c=='c' || c=='C' ){ |
| 222 | 222 | closeConnection = 1; |
| 223 | 223 | }else if( c=='k' || c=='K' ){ |
| 224 | 224 | closeConnection = 0; |
| @@ -226,11 +226,14 @@ | ||
| 226 | 226 | }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ |
| 227 | 227 | int i, j; |
| 228 | 228 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 229 | 229 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 230 | 230 | j = strlen(zLine) - 1; |
| 231 | - if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0; | |
| 231 | + while( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ){ | |
| 232 | + j -= 4; | |
| 233 | + zLine[j] = 0; | |
| 234 | + } | |
| 232 | 235 | fossil_print("redirect to %s\n", &zLine[i]); |
| 233 | 236 | url_parse(&zLine[i]); |
| 234 | 237 | transport_close(); |
| 235 | 238 | http_exchange(pSend, pReply, useLogin); |
| 236 | 239 | return; |
| @@ -267,11 +270,11 @@ | ||
| 267 | 270 | } |
| 268 | 271 | z[j] = 0; |
| 269 | 272 | fossil_fatal("server sends error: %s", z); |
| 270 | 273 | } |
| 271 | 274 | if( g.fHttpTrace ){ |
| 272 | - printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply)); | |
| 275 | + /*printf("HTTP RECEIVE:\n%s\n=======================\n",blob_str(pReply));*/ | |
| 273 | 276 | }else{ |
| 274 | 277 | blob_uncompress(pReply, pReply); |
| 275 | 278 | } |
| 276 | 279 | |
| 277 | 280 | /* |
| 278 | 281 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -60,11 +60,11 @@ | |
| 60 | zPw = 0; |
| 61 | }else{ |
| 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | url_prompt_for_password(); |
| 64 | zPw = g.urlPasswd; |
| 65 | if( !g.dontKeepUrl ) db_set("last-sync-pw", zPw, 0); |
| 66 | } |
| 67 | |
| 68 | /* The login card wants the SHA1 hash of the password, so convert the |
| 69 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 70 | ** |
| @@ -103,11 +103,11 @@ | |
| 103 | if( i>0 && g.urlPath[i-1]=='/' ){ |
| 104 | zSep = ""; |
| 105 | }else{ |
| 106 | zSep = "/"; |
| 107 | } |
| 108 | blob_appendf(pHdr, "POST %s%sxfer HTTP/1.0\r\n", g.urlPath, zSep); |
| 109 | if( g.urlProxyAuth ){ |
| 110 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 111 | } |
| 112 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 113 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| @@ -210,15 +210,15 @@ | |
| 210 | closeConnection = 1; |
| 211 | }else{ |
| 212 | closeConnection = 0; |
| 213 | } |
| 214 | }else if( strncasecmp(zLine, "content-length:", 15)==0 ){ |
| 215 | for(i=15; isspace(zLine[i]); i++){} |
| 216 | iLength = atoi(&zLine[i]); |
| 217 | }else if( strncasecmp(zLine, "connection:", 11)==0 ){ |
| 218 | char c; |
| 219 | for(i=11; isspace(zLine[i]); i++){} |
| 220 | c = zLine[i]; |
| 221 | if( c=='c' || c=='C' ){ |
| 222 | closeConnection = 1; |
| 223 | }else if( c=='k' || c=='K' ){ |
| 224 | closeConnection = 0; |
| @@ -226,11 +226,14 @@ | |
| 226 | }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ |
| 227 | int i, j; |
| 228 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 229 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 230 | j = strlen(zLine) - 1; |
| 231 | if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0; |
| 232 | fossil_print("redirect to %s\n", &zLine[i]); |
| 233 | url_parse(&zLine[i]); |
| 234 | transport_close(); |
| 235 | http_exchange(pSend, pReply, useLogin); |
| 236 | return; |
| @@ -267,11 +270,11 @@ | |
| 267 | } |
| 268 | z[j] = 0; |
| 269 | fossil_fatal("server sends error: %s", z); |
| 270 | } |
| 271 | if( g.fHttpTrace ){ |
| 272 | printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply)); |
| 273 | }else{ |
| 274 | blob_uncompress(pReply, pReply); |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -60,11 +60,11 @@ | |
| 60 | zPw = 0; |
| 61 | }else{ |
| 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | url_prompt_for_password(); |
| 64 | zPw = g.urlPasswd; |
| 65 | if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); |
| 66 | } |
| 67 | |
| 68 | /* The login card wants the SHA1 hash of the password, so convert the |
| 69 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 70 | ** |
| @@ -103,11 +103,11 @@ | |
| 103 | if( i>0 && g.urlPath[i-1]=='/' ){ |
| 104 | zSep = ""; |
| 105 | }else{ |
| 106 | zSep = "/"; |
| 107 | } |
| 108 | blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); |
| 109 | if( g.urlProxyAuth ){ |
| 110 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 111 | } |
| 112 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 113 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| @@ -210,15 +210,15 @@ | |
| 210 | closeConnection = 1; |
| 211 | }else{ |
| 212 | closeConnection = 0; |
| 213 | } |
| 214 | }else if( strncasecmp(zLine, "content-length:", 15)==0 ){ |
| 215 | for(i=15; fossil_isspace(zLine[i]); i++){} |
| 216 | iLength = atoi(&zLine[i]); |
| 217 | }else if( strncasecmp(zLine, "connection:", 11)==0 ){ |
| 218 | char c; |
| 219 | for(i=11; fossil_isspace(zLine[i]); i++){} |
| 220 | c = zLine[i]; |
| 221 | if( c=='c' || c=='C' ){ |
| 222 | closeConnection = 1; |
| 223 | }else if( c=='k' || c=='K' ){ |
| 224 | closeConnection = 0; |
| @@ -226,11 +226,14 @@ | |
| 226 | }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ |
| 227 | int i, j; |
| 228 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 229 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 230 | j = strlen(zLine) - 1; |
| 231 | while( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 232 | j -= 4; |
| 233 | zLine[j] = 0; |
| 234 | } |
| 235 | fossil_print("redirect to %s\n", &zLine[i]); |
| 236 | url_parse(&zLine[i]); |
| 237 | transport_close(); |
| 238 | http_exchange(pSend, pReply, useLogin); |
| 239 | return; |
| @@ -267,11 +270,11 @@ | |
| 270 | } |
| 271 | z[j] = 0; |
| 272 | fossil_fatal("server sends error: %s", z); |
| 273 | } |
| 274 | if( g.fHttpTrace ){ |
| 275 | /*printf("HTTP RECEIVE:\n%s\n=======================\n",blob_str(pReply));*/ |
| 276 | }else{ |
| 277 | blob_uncompress(pReply, pReply); |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 |
+1
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -90,10 +90,11 @@ | ||
| 90 | 90 | SSL_library_init(); |
| 91 | 91 | SSL_load_error_strings(); |
| 92 | 92 | ERR_load_BIO_strings(); |
| 93 | 93 | OpenSSL_add_all_algorithms(); |
| 94 | 94 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 95 | + X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); | |
| 95 | 96 | sslIsInit = 1; |
| 96 | 97 | } |
| 97 | 98 | } |
| 98 | 99 | |
| 99 | 100 | /* |
| 100 | 101 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -90,10 +90,11 @@ | |
| 90 | SSL_library_init(); |
| 91 | SSL_load_error_strings(); |
| 92 | ERR_load_BIO_strings(); |
| 93 | OpenSSL_add_all_algorithms(); |
| 94 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 95 | sslIsInit = 1; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -90,10 +90,11 @@ | |
| 90 | SSL_library_init(); |
| 91 | SSL_load_error_strings(); |
| 92 | ERR_load_BIO_strings(); |
| 93 | OpenSSL_add_all_algorithms(); |
| 94 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 95 | X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); |
| 96 | sslIsInit = 1; |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 |
+5
-7
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -83,11 +83,11 @@ | ||
| 83 | 83 | int got; |
| 84 | 84 | zBuf[0] = 0; |
| 85 | 85 | got = read(sshIn, zBuf, szBuf-1); |
| 86 | 86 | while( got>=0 ){ |
| 87 | 87 | zBuf[got] = 0; |
| 88 | - if( got==0 || !isspace(zBuf[got-1]) ) break; | |
| 88 | + if( got==0 || !fossil_isspace(zBuf[got-1]) ) break; | |
| 89 | 89 | got--; |
| 90 | 90 | } |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | /* |
| @@ -296,11 +296,11 @@ | ||
| 296 | 296 | char *zCmd; |
| 297 | 297 | fclose(transport.pFile); |
| 298 | 298 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 299 | 299 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 300 | 300 | ); |
| 301 | - portable_system(zCmd); | |
| 301 | + fossil_system(zCmd); | |
| 302 | 302 | free(zCmd); |
| 303 | 303 | transport.pFile = fopen(transport.zInFile, "rb"); |
| 304 | 304 | } |
| 305 | 305 | } |
| 306 | 306 | |
| @@ -386,12 +386,11 @@ | ||
| 386 | 386 | */ |
| 387 | 387 | static void transport_load_buffer(int N){ |
| 388 | 388 | int i, j; |
| 389 | 389 | if( transport.nAlloc==0 ){ |
| 390 | 390 | transport.nAlloc = N; |
| 391 | - transport.pBuf = malloc( N ); | |
| 392 | - if( transport.pBuf==0 ) fossil_panic("out of memory"); | |
| 391 | + transport.pBuf = fossil_malloc( N ); | |
| 393 | 392 | transport.iCursor = 0; |
| 394 | 393 | transport.nUsed = 0; |
| 395 | 394 | } |
| 396 | 395 | if( transport.iCursor>0 ){ |
| 397 | 396 | for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ |
| @@ -401,12 +400,11 @@ | ||
| 401 | 400 | transport.iCursor = 0; |
| 402 | 401 | } |
| 403 | 402 | if( transport.nUsed + N > transport.nAlloc ){ |
| 404 | 403 | char *pNew; |
| 405 | 404 | transport.nAlloc = transport.nUsed + N; |
| 406 | - pNew = realloc(transport.pBuf, transport.nAlloc); | |
| 407 | - if( pNew==0 ) fossil_panic("out of memory"); | |
| 405 | + pNew = fossil_realloc(transport.pBuf, transport.nAlloc); | |
| 408 | 406 | transport.pBuf = pNew; |
| 409 | 407 | } |
| 410 | 408 | if( N>0 ){ |
| 411 | 409 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 412 | 410 | if( i>0 ){ |
| @@ -439,11 +437,11 @@ | ||
| 439 | 437 | break; |
| 440 | 438 | } |
| 441 | 439 | } |
| 442 | 440 | if( transport.pBuf[i]=='\n' ){ |
| 443 | 441 | transport.iCursor = i+1; |
| 444 | - while( i>=iStart && isspace(transport.pBuf[i]) ){ | |
| 442 | + while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){ | |
| 445 | 443 | transport.pBuf[i] = 0; |
| 446 | 444 | i--; |
| 447 | 445 | } |
| 448 | 446 | break; |
| 449 | 447 | } |
| 450 | 448 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -83,11 +83,11 @@ | |
| 83 | int got; |
| 84 | zBuf[0] = 0; |
| 85 | got = read(sshIn, zBuf, szBuf-1); |
| 86 | while( got>=0 ){ |
| 87 | zBuf[got] = 0; |
| 88 | if( got==0 || !isspace(zBuf[got-1]) ) break; |
| 89 | got--; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /* |
| @@ -296,11 +296,11 @@ | |
| 296 | char *zCmd; |
| 297 | fclose(transport.pFile); |
| 298 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 299 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 300 | ); |
| 301 | portable_system(zCmd); |
| 302 | free(zCmd); |
| 303 | transport.pFile = fopen(transport.zInFile, "rb"); |
| 304 | } |
| 305 | } |
| 306 | |
| @@ -386,12 +386,11 @@ | |
| 386 | */ |
| 387 | static void transport_load_buffer(int N){ |
| 388 | int i, j; |
| 389 | if( transport.nAlloc==0 ){ |
| 390 | transport.nAlloc = N; |
| 391 | transport.pBuf = malloc( N ); |
| 392 | if( transport.pBuf==0 ) fossil_panic("out of memory"); |
| 393 | transport.iCursor = 0; |
| 394 | transport.nUsed = 0; |
| 395 | } |
| 396 | if( transport.iCursor>0 ){ |
| 397 | for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ |
| @@ -401,12 +400,11 @@ | |
| 401 | transport.iCursor = 0; |
| 402 | } |
| 403 | if( transport.nUsed + N > transport.nAlloc ){ |
| 404 | char *pNew; |
| 405 | transport.nAlloc = transport.nUsed + N; |
| 406 | pNew = realloc(transport.pBuf, transport.nAlloc); |
| 407 | if( pNew==0 ) fossil_panic("out of memory"); |
| 408 | transport.pBuf = pNew; |
| 409 | } |
| 410 | if( N>0 ){ |
| 411 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 412 | if( i>0 ){ |
| @@ -439,11 +437,11 @@ | |
| 439 | break; |
| 440 | } |
| 441 | } |
| 442 | if( transport.pBuf[i]=='\n' ){ |
| 443 | transport.iCursor = i+1; |
| 444 | while( i>=iStart && isspace(transport.pBuf[i]) ){ |
| 445 | transport.pBuf[i] = 0; |
| 446 | i--; |
| 447 | } |
| 448 | break; |
| 449 | } |
| 450 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -83,11 +83,11 @@ | |
| 83 | int got; |
| 84 | zBuf[0] = 0; |
| 85 | got = read(sshIn, zBuf, szBuf-1); |
| 86 | while( got>=0 ){ |
| 87 | zBuf[got] = 0; |
| 88 | if( got==0 || !fossil_isspace(zBuf[got-1]) ) break; |
| 89 | got--; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /* |
| @@ -296,11 +296,11 @@ | |
| 296 | char *zCmd; |
| 297 | fclose(transport.pFile); |
| 298 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", |
| 299 | g.argv[0], g.urlName, transport.zOutFile, transport.zInFile |
| 300 | ); |
| 301 | fossil_system(zCmd); |
| 302 | free(zCmd); |
| 303 | transport.pFile = fopen(transport.zInFile, "rb"); |
| 304 | } |
| 305 | } |
| 306 | |
| @@ -386,12 +386,11 @@ | |
| 386 | */ |
| 387 | static void transport_load_buffer(int N){ |
| 388 | int i, j; |
| 389 | if( transport.nAlloc==0 ){ |
| 390 | transport.nAlloc = N; |
| 391 | transport.pBuf = fossil_malloc( N ); |
| 392 | transport.iCursor = 0; |
| 393 | transport.nUsed = 0; |
| 394 | } |
| 395 | if( transport.iCursor>0 ){ |
| 396 | for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){ |
| @@ -401,12 +400,11 @@ | |
| 400 | transport.iCursor = 0; |
| 401 | } |
| 402 | if( transport.nUsed + N > transport.nAlloc ){ |
| 403 | char *pNew; |
| 404 | transport.nAlloc = transport.nUsed + N; |
| 405 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 406 | transport.pBuf = pNew; |
| 407 | } |
| 408 | if( N>0 ){ |
| 409 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 410 | if( i>0 ){ |
| @@ -439,11 +437,11 @@ | |
| 437 | break; |
| 438 | } |
| 439 | } |
| 440 | if( transport.pBuf[i]=='\n' ){ |
| 441 | transport.iCursor = i+1; |
| 442 | while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){ |
| 443 | transport.pBuf[i] = 0; |
| 444 | i--; |
| 445 | } |
| 446 | break; |
| 447 | } |
| 448 |
+100
-94
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -234,14 +234,26 @@ | ||
| 234 | 234 | |
| 235 | 235 | |
| 236 | 236 | /* |
| 237 | 237 | ** Append the difference between two RIDs to the output |
| 238 | 238 | */ |
| 239 | -static void append_diff(int fromid, int toid){ | |
| 239 | +static void append_diff(const char *zFrom, const char *zTo){ | |
| 240 | + int fromid; | |
| 241 | + int toid; | |
| 240 | 242 | Blob from, to, out; |
| 241 | - content_get(fromid, &from); | |
| 242 | - content_get(toid, &to); | |
| 243 | + if( zFrom ){ | |
| 244 | + fromid = uuid_to_rid(zFrom, 0); | |
| 245 | + content_get(fromid, &from); | |
| 246 | + }else{ | |
| 247 | + blob_zero(&from); | |
| 248 | + } | |
| 249 | + if( zTo ){ | |
| 250 | + toid = uuid_to_rid(zTo, 0); | |
| 251 | + content_get(toid, &to); | |
| 252 | + }else{ | |
| 253 | + blob_zero(&to); | |
| 254 | + } | |
| 243 | 255 | blob_zero(&out); |
| 244 | 256 | text_diff(&from, &to, &out, 5, 1); |
| 245 | 257 | @ %h(blob_str(&out)) |
| 246 | 258 | blob_reset(&from); |
| 247 | 259 | blob_reset(&to); |
| @@ -263,39 +275,37 @@ | ||
| 263 | 275 | @ <p>Deleted %h(zName)</p> |
| 264 | 276 | }else if( zOld==0 ){ |
| 265 | 277 | @ <p>Added %h(zName)</p> |
| 266 | 278 | }else{ |
| 267 | 279 | @ <p>Changes to %h(zName)</p> |
| 268 | - if( showDiff ){ | |
| 269 | - int rid1 = uuid_to_rid(zOld, 0); | |
| 270 | - int rid2 = uuid_to_rid(zNew, 0); | |
| 271 | - @ <blockquote><pre> | |
| 272 | - append_diff(rid1, rid2); | |
| 273 | - @ </pre></blockquote> | |
| 274 | - } | |
| 275 | - } | |
| 276 | - }else if( zOld && zNew ){ | |
| 277 | - @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 278 | - @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> | |
| 279 | - @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> | |
| 280 | - if( !showDiff ){ | |
| 280 | + } | |
| 281 | + if( showDiff ){ | |
| 282 | + @ <blockquote><pre> | |
| 283 | + append_diff(zOld, zNew); | |
| 284 | + @ </pre></blockquote> | |
| 285 | + } | |
| 286 | + }else{ | |
| 287 | + if( zOld && zNew ){ | |
| 288 | + @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 289 | + @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> | |
| 290 | + @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> | |
| 291 | + }else if( zOld ){ | |
| 292 | + @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 293 | + @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> | |
| 294 | + }else{ | |
| 295 | + @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 296 | + @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> | |
| 297 | + } | |
| 298 | + if( showDiff ){ | |
| 299 | + @ <blockquote><pre> | |
| 300 | + append_diff(zOld, zNew); | |
| 301 | + @ </pre></blockquote> | |
| 302 | + }else if( zOld && zNew ){ | |
| 281 | 303 | @ |
| 282 | 304 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 283 | - }else{ | |
| 284 | - int rid1 = uuid_to_rid(zOld, 0); | |
| 285 | - int rid2 = uuid_to_rid(zNew, 0); | |
| 286 | - @ <blockquote><pre> | |
| 287 | - append_diff(rid1, rid2); | |
| 288 | - @ </pre></blockquote> | |
| 289 | 305 | } |
| 290 | 306 | @ </p> |
| 291 | - }else if( zOld ){ | |
| 292 | - @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 293 | - @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> | |
| 294 | - }else{ | |
| 295 | - @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> | |
| 296 | - @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> | |
| 297 | 307 | } |
| 298 | 308 | } |
| 299 | 309 | |
| 300 | 310 | |
| 301 | 311 | /* |
| @@ -533,24 +543,20 @@ | ||
| 533 | 543 | rid = 0; |
| 534 | 544 | } |
| 535 | 545 | db_finalize(&q); |
| 536 | 546 | showTags(rid, "wiki-*"); |
| 537 | 547 | if( rid ){ |
| 538 | - Blob content; | |
| 539 | - Manifest m; | |
| 540 | - memset(&m, 0, sizeof(m)); | |
| 541 | - blob_zero(&m.content); | |
| 542 | - content_get(rid, &content); | |
| 543 | - manifest_parse(&m, &content); | |
| 544 | - if( m.type==CFTYPE_WIKI ){ | |
| 548 | + Manifest *pWiki; | |
| 549 | + pWiki = manifest_get(rid, CFTYPE_WIKI); | |
| 550 | + if( pWiki ){ | |
| 545 | 551 | Blob wiki; |
| 546 | - blob_init(&wiki, m.zWiki, -1); | |
| 552 | + blob_init(&wiki, pWiki->zWiki, -1); | |
| 547 | 553 | @ <div class="section">Content</div> |
| 548 | 554 | wiki_convert(&wiki, 0, 0); |
| 549 | 555 | blob_reset(&wiki); |
| 550 | 556 | } |
| 551 | - manifest_clear(&m); | |
| 557 | + manifest_destroy(pWiki); | |
| 552 | 558 | } |
| 553 | 559 | style_footer(); |
| 554 | 560 | } |
| 555 | 561 | |
| 556 | 562 | /* |
| @@ -570,26 +576,23 @@ | ||
| 570 | 576 | |
| 571 | 577 | /* |
| 572 | 578 | ** Find an checkin based on query parameter zParam and parse its |
| 573 | 579 | ** manifest. Return the number of errors. |
| 574 | 580 | */ |
| 575 | -static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){ | |
| 581 | +static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){ | |
| 576 | 582 | int rid; |
| 577 | - Blob content; | |
| 578 | 583 | |
| 579 | 584 | *pRid = rid = name_to_rid_www(zParam); |
| 580 | 585 | if( rid==0 ){ |
| 581 | 586 | webpage_error("Missing \"%s\" query parameter.", zParam); |
| 582 | - return 1; | |
| 587 | + return 0; | |
| 583 | 588 | } |
| 584 | 589 | if( !is_a_version(rid) ){ |
| 585 | 590 | webpage_error("Artifact %s is not a checkin.", P(zParam)); |
| 586 | - return 1; | |
| 591 | + return 0; | |
| 587 | 592 | } |
| 588 | - content_get(rid, &content); | |
| 589 | - manifest_parse(pM, &content); | |
| 590 | - return 0; | |
| 593 | + return manifest_get(rid, CFTYPE_MANIFEST); | |
| 591 | 594 | } |
| 592 | 595 | |
| 593 | 596 | /* |
| 594 | 597 | ** Output a description of a check-in |
| 595 | 598 | */ |
| @@ -625,59 +628,64 @@ | ||
| 625 | 628 | ** Show all differences between two checkins. |
| 626 | 629 | */ |
| 627 | 630 | void vdiff_page(void){ |
| 628 | 631 | int ridFrom, ridTo; |
| 629 | 632 | int showDetail = 0; |
| 630 | - int iFrom, iTo; | |
| 631 | - Manifest mFrom, mTo; | |
| 633 | + Manifest *pFrom, *pTo; | |
| 634 | + ManifestFile *pFileFrom, *pFileTo; | |
| 632 | 635 | |
| 633 | 636 | login_check_credentials(); |
| 634 | 637 | if( !g.okRead ){ login_needed(); return; } |
| 635 | 638 | login_anonymous_available(); |
| 636 | 639 | |
| 637 | - if( vdiff_parse_manifest("from", &ridFrom, &mFrom) ) return; | |
| 638 | - if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return; | |
| 640 | + pFrom = vdiff_parse_manifest("from", &ridFrom); | |
| 641 | + if( pFrom==0 ) return; | |
| 642 | + pTo = vdiff_parse_manifest("to", &ridTo); | |
| 643 | + if( pTo==0 ) return; | |
| 639 | 644 | showDetail = atoi(PD("detail","0")); |
| 640 | 645 | style_header("Check-in Differences"); |
| 641 | 646 | @ <h2>Difference From:</h2><blockquote> |
| 642 | 647 | checkin_description(ridFrom); |
| 643 | 648 | @ </blockquote><h2>To:</h2><blockquote> |
| 644 | 649 | checkin_description(ridTo); |
| 645 | 650 | @ </blockquote><hr /><p> |
| 646 | 651 | |
| 647 | - iFrom = iTo = 0; | |
| 648 | - while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ | |
| 652 | + manifest_file_rewind(pFrom); | |
| 653 | + pFileFrom = manifest_file_next(pFrom, 0); | |
| 654 | + manifest_file_rewind(pTo); | |
| 655 | + pFileTo = manifest_file_next(pTo, 0); | |
| 656 | + while( pFileFrom || pFileTo ){ | |
| 649 | 657 | int cmp; |
| 650 | - if( iFrom>=mFrom.nFile ){ | |
| 658 | + if( pFileFrom==0 ){ | |
| 651 | 659 | cmp = +1; |
| 652 | - }else if( iTo>=mTo.nFile ){ | |
| 660 | + }else if( pFileTo==0 ){ | |
| 653 | 661 | cmp = -1; |
| 654 | 662 | }else{ |
| 655 | - cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); | |
| 663 | + cmp = strcmp(pFileFrom->zName, pFileTo->zName); | |
| 656 | 664 | } |
| 657 | 665 | if( cmp<0 ){ |
| 658 | - append_file_change_line(mFrom.aFile[iFrom].zName, | |
| 659 | - mFrom.aFile[iFrom].zUuid, 0, 0); | |
| 660 | - iFrom++; | |
| 666 | + append_file_change_line(pFileFrom->zName, | |
| 667 | + pFileFrom->zUuid, 0, 0); | |
| 668 | + pFileFrom = manifest_file_next(pFrom, 0); | |
| 661 | 669 | }else if( cmp>0 ){ |
| 662 | - append_file_change_line(mTo.aFile[iTo].zName, | |
| 663 | - 0, mTo.aFile[iTo].zUuid, 0); | |
| 664 | - iTo++; | |
| 665 | - }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ | |
| 670 | + append_file_change_line(pFileTo->zName, | |
| 671 | + 0, pFileTo->zUuid, 0); | |
| 672 | + pFileTo = manifest_file_next(pTo, 0); | |
| 673 | + }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ | |
| 666 | 674 | /* No changes */ |
| 667 | - iFrom++; | |
| 668 | - iTo++; | |
| 675 | + pFileFrom = manifest_file_next(pFrom, 0); | |
| 676 | + pFileTo = manifest_file_next(pTo, 0); | |
| 669 | 677 | }else{ |
| 670 | - append_file_change_line(mFrom.aFile[iFrom].zName, | |
| 671 | - mFrom.aFile[iFrom].zUuid, | |
| 672 | - mTo.aFile[iTo].zUuid, showDetail); | |
| 673 | - iFrom++; | |
| 674 | - iTo++; | |
| 678 | + append_file_change_line(pFileFrom->zName, | |
| 679 | + pFileFrom->zUuid, | |
| 680 | + pFileTo->zUuid, showDetail); | |
| 681 | + pFileFrom = manifest_file_next(pFrom, 0); | |
| 682 | + pFileTo = manifest_file_next(pTo, 0); | |
| 675 | 683 | } |
| 676 | 684 | } |
| 677 | - manifest_clear(&mFrom); | |
| 678 | - manifest_clear(&mTo); | |
| 685 | + manifest_destroy(pFrom); | |
| 686 | + manifest_destroy(pTo); | |
| 679 | 687 | |
| 680 | 688 | style_footer(); |
| 681 | 689 | } |
| 682 | 690 | |
| 683 | 691 | /* |
| @@ -1033,25 +1041,26 @@ | ||
| 1033 | 1041 | */ |
| 1034 | 1042 | int artifact_from_ci_and_filename(void){ |
| 1035 | 1043 | const char *zFilename; |
| 1036 | 1044 | const char *zCI; |
| 1037 | 1045 | int cirid; |
| 1038 | - Blob content; | |
| 1039 | - Manifest m; | |
| 1040 | - int i; | |
| 1046 | + Manifest *pManifest; | |
| 1047 | + ManifestFile *pFile; | |
| 1041 | 1048 | |
| 1042 | 1049 | zCI = P("ci"); |
| 1043 | 1050 | if( zCI==0 ) return 0; |
| 1044 | 1051 | zFilename = P("filename"); |
| 1045 | 1052 | if( zFilename==0 ) return 0; |
| 1046 | 1053 | cirid = name_to_rid_www("ci"); |
| 1047 | - if( !content_get(cirid, &content) ) return 0; | |
| 1048 | - if( !manifest_parse(&m, &content) ) return 0; | |
| 1049 | - if( m.type!=CFTYPE_MANIFEST ) return 0; | |
| 1050 | - for(i=0; i<m.nFile; i++){ | |
| 1051 | - if( strcmp(zFilename, m.aFile[i].zName)==0 ){ | |
| 1052 | - return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid); | |
| 1054 | + pManifest = manifest_get(cirid, CFTYPE_MANIFEST); | |
| 1055 | + if( pManifest==0 ) return 0; | |
| 1056 | + manifest_file_rewind(pManifest); | |
| 1057 | + while( (pFile = manifest_file_next(pManifest,0))!=0 ){ | |
| 1058 | + if( strcmp(zFilename, pFile->zName)==0 ){ | |
| 1059 | + int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); | |
| 1060 | + manifest_destroy(pManifest); | |
| 1061 | + return rid; | |
| 1053 | 1062 | } |
| 1054 | 1063 | } |
| 1055 | 1064 | return 0; |
| 1056 | 1065 | } |
| 1057 | 1066 | |
| @@ -1158,15 +1167,14 @@ | ||
| 1158 | 1167 | ** |
| 1159 | 1168 | ** Show the details of a ticket change control artifact. |
| 1160 | 1169 | */ |
| 1161 | 1170 | void tinfo_page(void){ |
| 1162 | 1171 | int rid; |
| 1163 | - Blob content; | |
| 1164 | 1172 | char *zDate; |
| 1165 | 1173 | const char *zUuid; |
| 1166 | 1174 | char zTktName[20]; |
| 1167 | - Manifest m; | |
| 1175 | + Manifest *pTktChng; | |
| 1168 | 1176 | |
| 1169 | 1177 | login_check_credentials(); |
| 1170 | 1178 | if( !g.okRdTkt ){ login_needed(); return; } |
| 1171 | 1179 | rid = name_to_rid_www("name"); |
| 1172 | 1180 | if( rid==0 ){ fossil_redirect_home(); } |
| @@ -1178,39 +1186,37 @@ | ||
| 1178 | 1186 | }else{ |
| 1179 | 1187 | style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", |
| 1180 | 1188 | g.zTop, zUuid); |
| 1181 | 1189 | } |
| 1182 | 1190 | } |
| 1183 | - content_get(rid, &content); | |
| 1184 | - if( manifest_parse(&m, &content)==0 ){ | |
| 1185 | - fossil_redirect_home(); | |
| 1186 | - } | |
| 1187 | - if( m.type!=CFTYPE_TICKET ){ | |
| 1191 | + pTktChng = manifest_get(rid, CFTYPE_TICKET); | |
| 1192 | + if( pTktChng==0 ){ | |
| 1188 | 1193 | fossil_redirect_home(); |
| 1189 | 1194 | } |
| 1190 | 1195 | style_header("Ticket Change Details"); |
| 1191 | - zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate); | |
| 1192 | - memcpy(zTktName, m.zTicketUuid, 10); | |
| 1196 | + zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate); | |
| 1197 | + memcpy(zTktName, pTktChng->zTicketUuid, 10); | |
| 1193 | 1198 | zTktName[10] = 0; |
| 1194 | 1199 | if( g.okHistory ){ |
| 1195 | - @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2> | |
| 1200 | + @ <h2>Changes to ticket | |
| 1201 | + @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2> | |
| 1196 | 1202 | @ |
| 1197 | - @ <p>By %h(m.zUser) on %s(zDate). See also: | |
| 1203 | + @ <p>By %h(pTktChng->zUser) on %s(zDate). See also: | |
| 1198 | 1204 | @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and |
| 1199 | - @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a> | |
| 1200 | - @ </p> | |
| 1205 | + @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket | |
| 1206 | + @ history</a></p> | |
| 1201 | 1207 | }else{ |
| 1202 | 1208 | @ <h2>Changes to ticket %s(zTktName)</h2> |
| 1203 | 1209 | @ |
| 1204 | - @ <p>By %h(m.zUser) on %s(zDate). | |
| 1210 | + @ <p>By %h(pTktChng->zUser) on %s(zDate). | |
| 1205 | 1211 | @ </p> |
| 1206 | 1212 | } |
| 1207 | 1213 | @ |
| 1208 | 1214 | @ <ol> |
| 1209 | 1215 | free(zDate); |
| 1210 | - ticket_output_change_artifact(&m); | |
| 1211 | - manifest_clear(&m); | |
| 1216 | + ticket_output_change_artifact(pTktChng); | |
| 1217 | + manifest_destroy(pTktChng); | |
| 1212 | 1218 | style_footer(); |
| 1213 | 1219 | } |
| 1214 | 1220 | |
| 1215 | 1221 | |
| 1216 | 1222 | /* |
| 1217 | 1223 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -234,14 +234,26 @@ | |
| 234 | |
| 235 | |
| 236 | /* |
| 237 | ** Append the difference between two RIDs to the output |
| 238 | */ |
| 239 | static void append_diff(int fromid, int toid){ |
| 240 | Blob from, to, out; |
| 241 | content_get(fromid, &from); |
| 242 | content_get(toid, &to); |
| 243 | blob_zero(&out); |
| 244 | text_diff(&from, &to, &out, 5, 1); |
| 245 | @ %h(blob_str(&out)) |
| 246 | blob_reset(&from); |
| 247 | blob_reset(&to); |
| @@ -263,39 +275,37 @@ | |
| 263 | @ <p>Deleted %h(zName)</p> |
| 264 | }else if( zOld==0 ){ |
| 265 | @ <p>Added %h(zName)</p> |
| 266 | }else{ |
| 267 | @ <p>Changes to %h(zName)</p> |
| 268 | if( showDiff ){ |
| 269 | int rid1 = uuid_to_rid(zOld, 0); |
| 270 | int rid2 = uuid_to_rid(zNew, 0); |
| 271 | @ <blockquote><pre> |
| 272 | append_diff(rid1, rid2); |
| 273 | @ </pre></blockquote> |
| 274 | } |
| 275 | } |
| 276 | }else if( zOld && zNew ){ |
| 277 | @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 278 | @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| 279 | @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> |
| 280 | if( !showDiff ){ |
| 281 | @ |
| 282 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 283 | }else{ |
| 284 | int rid1 = uuid_to_rid(zOld, 0); |
| 285 | int rid2 = uuid_to_rid(zNew, 0); |
| 286 | @ <blockquote><pre> |
| 287 | append_diff(rid1, rid2); |
| 288 | @ </pre></blockquote> |
| 289 | } |
| 290 | @ </p> |
| 291 | }else if( zOld ){ |
| 292 | @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 293 | @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p> |
| 294 | }else{ |
| 295 | @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 296 | @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p> |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | |
| 301 | /* |
| @@ -533,24 +543,20 @@ | |
| 533 | rid = 0; |
| 534 | } |
| 535 | db_finalize(&q); |
| 536 | showTags(rid, "wiki-*"); |
| 537 | if( rid ){ |
| 538 | Blob content; |
| 539 | Manifest m; |
| 540 | memset(&m, 0, sizeof(m)); |
| 541 | blob_zero(&m.content); |
| 542 | content_get(rid, &content); |
| 543 | manifest_parse(&m, &content); |
| 544 | if( m.type==CFTYPE_WIKI ){ |
| 545 | Blob wiki; |
| 546 | blob_init(&wiki, m.zWiki, -1); |
| 547 | @ <div class="section">Content</div> |
| 548 | wiki_convert(&wiki, 0, 0); |
| 549 | blob_reset(&wiki); |
| 550 | } |
| 551 | manifest_clear(&m); |
| 552 | } |
| 553 | style_footer(); |
| 554 | } |
| 555 | |
| 556 | /* |
| @@ -570,26 +576,23 @@ | |
| 570 | |
| 571 | /* |
| 572 | ** Find an checkin based on query parameter zParam and parse its |
| 573 | ** manifest. Return the number of errors. |
| 574 | */ |
| 575 | static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){ |
| 576 | int rid; |
| 577 | Blob content; |
| 578 | |
| 579 | *pRid = rid = name_to_rid_www(zParam); |
| 580 | if( rid==0 ){ |
| 581 | webpage_error("Missing \"%s\" query parameter.", zParam); |
| 582 | return 1; |
| 583 | } |
| 584 | if( !is_a_version(rid) ){ |
| 585 | webpage_error("Artifact %s is not a checkin.", P(zParam)); |
| 586 | return 1; |
| 587 | } |
| 588 | content_get(rid, &content); |
| 589 | manifest_parse(pM, &content); |
| 590 | return 0; |
| 591 | } |
| 592 | |
| 593 | /* |
| 594 | ** Output a description of a check-in |
| 595 | */ |
| @@ -625,59 +628,64 @@ | |
| 625 | ** Show all differences between two checkins. |
| 626 | */ |
| 627 | void vdiff_page(void){ |
| 628 | int ridFrom, ridTo; |
| 629 | int showDetail = 0; |
| 630 | int iFrom, iTo; |
| 631 | Manifest mFrom, mTo; |
| 632 | |
| 633 | login_check_credentials(); |
| 634 | if( !g.okRead ){ login_needed(); return; } |
| 635 | login_anonymous_available(); |
| 636 | |
| 637 | if( vdiff_parse_manifest("from", &ridFrom, &mFrom) ) return; |
| 638 | if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return; |
| 639 | showDetail = atoi(PD("detail","0")); |
| 640 | style_header("Check-in Differences"); |
| 641 | @ <h2>Difference From:</h2><blockquote> |
| 642 | checkin_description(ridFrom); |
| 643 | @ </blockquote><h2>To:</h2><blockquote> |
| 644 | checkin_description(ridTo); |
| 645 | @ </blockquote><hr /><p> |
| 646 | |
| 647 | iFrom = iTo = 0; |
| 648 | while( iFrom<mFrom.nFile && iTo<mTo.nFile ){ |
| 649 | int cmp; |
| 650 | if( iFrom>=mFrom.nFile ){ |
| 651 | cmp = +1; |
| 652 | }else if( iTo>=mTo.nFile ){ |
| 653 | cmp = -1; |
| 654 | }else{ |
| 655 | cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName); |
| 656 | } |
| 657 | if( cmp<0 ){ |
| 658 | append_file_change_line(mFrom.aFile[iFrom].zName, |
| 659 | mFrom.aFile[iFrom].zUuid, 0, 0); |
| 660 | iFrom++; |
| 661 | }else if( cmp>0 ){ |
| 662 | append_file_change_line(mTo.aFile[iTo].zName, |
| 663 | 0, mTo.aFile[iTo].zUuid, 0); |
| 664 | iTo++; |
| 665 | }else if( strcmp(mFrom.aFile[iFrom].zUuid, mTo.aFile[iTo].zUuid)==0 ){ |
| 666 | /* No changes */ |
| 667 | iFrom++; |
| 668 | iTo++; |
| 669 | }else{ |
| 670 | append_file_change_line(mFrom.aFile[iFrom].zName, |
| 671 | mFrom.aFile[iFrom].zUuid, |
| 672 | mTo.aFile[iTo].zUuid, showDetail); |
| 673 | iFrom++; |
| 674 | iTo++; |
| 675 | } |
| 676 | } |
| 677 | manifest_clear(&mFrom); |
| 678 | manifest_clear(&mTo); |
| 679 | |
| 680 | style_footer(); |
| 681 | } |
| 682 | |
| 683 | /* |
| @@ -1033,25 +1041,26 @@ | |
| 1033 | */ |
| 1034 | int artifact_from_ci_and_filename(void){ |
| 1035 | const char *zFilename; |
| 1036 | const char *zCI; |
| 1037 | int cirid; |
| 1038 | Blob content; |
| 1039 | Manifest m; |
| 1040 | int i; |
| 1041 | |
| 1042 | zCI = P("ci"); |
| 1043 | if( zCI==0 ) return 0; |
| 1044 | zFilename = P("filename"); |
| 1045 | if( zFilename==0 ) return 0; |
| 1046 | cirid = name_to_rid_www("ci"); |
| 1047 | if( !content_get(cirid, &content) ) return 0; |
| 1048 | if( !manifest_parse(&m, &content) ) return 0; |
| 1049 | if( m.type!=CFTYPE_MANIFEST ) return 0; |
| 1050 | for(i=0; i<m.nFile; i++){ |
| 1051 | if( strcmp(zFilename, m.aFile[i].zName)==0 ){ |
| 1052 | return db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", m.aFile[i].zUuid); |
| 1053 | } |
| 1054 | } |
| 1055 | return 0; |
| 1056 | } |
| 1057 | |
| @@ -1158,15 +1167,14 @@ | |
| 1158 | ** |
| 1159 | ** Show the details of a ticket change control artifact. |
| 1160 | */ |
| 1161 | void tinfo_page(void){ |
| 1162 | int rid; |
| 1163 | Blob content; |
| 1164 | char *zDate; |
| 1165 | const char *zUuid; |
| 1166 | char zTktName[20]; |
| 1167 | Manifest m; |
| 1168 | |
| 1169 | login_check_credentials(); |
| 1170 | if( !g.okRdTkt ){ login_needed(); return; } |
| 1171 | rid = name_to_rid_www("name"); |
| 1172 | if( rid==0 ){ fossil_redirect_home(); } |
| @@ -1178,39 +1186,37 @@ | |
| 1178 | }else{ |
| 1179 | style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", |
| 1180 | g.zTop, zUuid); |
| 1181 | } |
| 1182 | } |
| 1183 | content_get(rid, &content); |
| 1184 | if( manifest_parse(&m, &content)==0 ){ |
| 1185 | fossil_redirect_home(); |
| 1186 | } |
| 1187 | if( m.type!=CFTYPE_TICKET ){ |
| 1188 | fossil_redirect_home(); |
| 1189 | } |
| 1190 | style_header("Ticket Change Details"); |
| 1191 | zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate); |
| 1192 | memcpy(zTktName, m.zTicketUuid, 10); |
| 1193 | zTktName[10] = 0; |
| 1194 | if( g.okHistory ){ |
| 1195 | @ <h2>Changes to ticket <a href="%s(m.zTicketUuid)">%s(zTktName)</a></h2> |
| 1196 | @ |
| 1197 | @ <p>By %h(m.zUser) on %s(zDate). See also: |
| 1198 | @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and |
| 1199 | @ <a href="%s(g.zTop)/tkthistory/%s(m.zTicketUuid)">ticket history</a> |
| 1200 | @ </p> |
| 1201 | }else{ |
| 1202 | @ <h2>Changes to ticket %s(zTktName)</h2> |
| 1203 | @ |
| 1204 | @ <p>By %h(m.zUser) on %s(zDate). |
| 1205 | @ </p> |
| 1206 | } |
| 1207 | @ |
| 1208 | @ <ol> |
| 1209 | free(zDate); |
| 1210 | ticket_output_change_artifact(&m); |
| 1211 | manifest_clear(&m); |
| 1212 | style_footer(); |
| 1213 | } |
| 1214 | |
| 1215 | |
| 1216 | /* |
| 1217 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -234,14 +234,26 @@ | |
| 234 | |
| 235 | |
| 236 | /* |
| 237 | ** Append the difference between two RIDs to the output |
| 238 | */ |
| 239 | static void append_diff(const char *zFrom, const char *zTo){ |
| 240 | int fromid; |
| 241 | int toid; |
| 242 | Blob from, to, out; |
| 243 | if( zFrom ){ |
| 244 | fromid = uuid_to_rid(zFrom, 0); |
| 245 | content_get(fromid, &from); |
| 246 | }else{ |
| 247 | blob_zero(&from); |
| 248 | } |
| 249 | if( zTo ){ |
| 250 | toid = uuid_to_rid(zTo, 0); |
| 251 | content_get(toid, &to); |
| 252 | }else{ |
| 253 | blob_zero(&to); |
| 254 | } |
| 255 | blob_zero(&out); |
| 256 | text_diff(&from, &to, &out, 5, 1); |
| 257 | @ %h(blob_str(&out)) |
| 258 | blob_reset(&from); |
| 259 | blob_reset(&to); |
| @@ -263,39 +275,37 @@ | |
| 275 | @ <p>Deleted %h(zName)</p> |
| 276 | }else if( zOld==0 ){ |
| 277 | @ <p>Added %h(zName)</p> |
| 278 | }else{ |
| 279 | @ <p>Changes to %h(zName)</p> |
| 280 | } |
| 281 | if( showDiff ){ |
| 282 | @ <blockquote><pre> |
| 283 | append_diff(zOld, zNew); |
| 284 | @ </pre></blockquote> |
| 285 | } |
| 286 | }else{ |
| 287 | if( zOld && zNew ){ |
| 288 | @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 289 | @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| 290 | @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> |
| 291 | }else if( zOld ){ |
| 292 | @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 293 | @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| 294 | }else{ |
| 295 | @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 296 | @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> |
| 297 | } |
| 298 | if( showDiff ){ |
| 299 | @ <blockquote><pre> |
| 300 | append_diff(zOld, zNew); |
| 301 | @ </pre></blockquote> |
| 302 | }else if( zOld && zNew ){ |
| 303 | @ |
| 304 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 305 | } |
| 306 | @ </p> |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | |
| 311 | /* |
| @@ -533,24 +543,20 @@ | |
| 543 | rid = 0; |
| 544 | } |
| 545 | db_finalize(&q); |
| 546 | showTags(rid, "wiki-*"); |
| 547 | if( rid ){ |
| 548 | Manifest *pWiki; |
| 549 | pWiki = manifest_get(rid, CFTYPE_WIKI); |
| 550 | if( pWiki ){ |
| 551 | Blob wiki; |
| 552 | blob_init(&wiki, pWiki->zWiki, -1); |
| 553 | @ <div class="section">Content</div> |
| 554 | wiki_convert(&wiki, 0, 0); |
| 555 | blob_reset(&wiki); |
| 556 | } |
| 557 | manifest_destroy(pWiki); |
| 558 | } |
| 559 | style_footer(); |
| 560 | } |
| 561 | |
| 562 | /* |
| @@ -570,26 +576,23 @@ | |
| 576 | |
| 577 | /* |
| 578 | ** Find an checkin based on query parameter zParam and parse its |
| 579 | ** manifest. Return the number of errors. |
| 580 | */ |
| 581 | static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){ |
| 582 | int rid; |
| 583 | |
| 584 | *pRid = rid = name_to_rid_www(zParam); |
| 585 | if( rid==0 ){ |
| 586 | webpage_error("Missing \"%s\" query parameter.", zParam); |
| 587 | return 0; |
| 588 | } |
| 589 | if( !is_a_version(rid) ){ |
| 590 | webpage_error("Artifact %s is not a checkin.", P(zParam)); |
| 591 | return 0; |
| 592 | } |
| 593 | return manifest_get(rid, CFTYPE_MANIFEST); |
| 594 | } |
| 595 | |
| 596 | /* |
| 597 | ** Output a description of a check-in |
| 598 | */ |
| @@ -625,59 +628,64 @@ | |
| 628 | ** Show all differences between two checkins. |
| 629 | */ |
| 630 | void vdiff_page(void){ |
| 631 | int ridFrom, ridTo; |
| 632 | int showDetail = 0; |
| 633 | Manifest *pFrom, *pTo; |
| 634 | ManifestFile *pFileFrom, *pFileTo; |
| 635 | |
| 636 | login_check_credentials(); |
| 637 | if( !g.okRead ){ login_needed(); return; } |
| 638 | login_anonymous_available(); |
| 639 | |
| 640 | pFrom = vdiff_parse_manifest("from", &ridFrom); |
| 641 | if( pFrom==0 ) return; |
| 642 | pTo = vdiff_parse_manifest("to", &ridTo); |
| 643 | if( pTo==0 ) return; |
| 644 | showDetail = atoi(PD("detail","0")); |
| 645 | style_header("Check-in Differences"); |
| 646 | @ <h2>Difference From:</h2><blockquote> |
| 647 | checkin_description(ridFrom); |
| 648 | @ </blockquote><h2>To:</h2><blockquote> |
| 649 | checkin_description(ridTo); |
| 650 | @ </blockquote><hr /><p> |
| 651 | |
| 652 | manifest_file_rewind(pFrom); |
| 653 | pFileFrom = manifest_file_next(pFrom, 0); |
| 654 | manifest_file_rewind(pTo); |
| 655 | pFileTo = manifest_file_next(pTo, 0); |
| 656 | while( pFileFrom || pFileTo ){ |
| 657 | int cmp; |
| 658 | if( pFileFrom==0 ){ |
| 659 | cmp = +1; |
| 660 | }else if( pFileTo==0 ){ |
| 661 | cmp = -1; |
| 662 | }else{ |
| 663 | cmp = strcmp(pFileFrom->zName, pFileTo->zName); |
| 664 | } |
| 665 | if( cmp<0 ){ |
| 666 | append_file_change_line(pFileFrom->zName, |
| 667 | pFileFrom->zUuid, 0, 0); |
| 668 | pFileFrom = manifest_file_next(pFrom, 0); |
| 669 | }else if( cmp>0 ){ |
| 670 | append_file_change_line(pFileTo->zName, |
| 671 | 0, pFileTo->zUuid, 0); |
| 672 | pFileTo = manifest_file_next(pTo, 0); |
| 673 | }else if( strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 674 | /* No changes */ |
| 675 | pFileFrom = manifest_file_next(pFrom, 0); |
| 676 | pFileTo = manifest_file_next(pTo, 0); |
| 677 | }else{ |
| 678 | append_file_change_line(pFileFrom->zName, |
| 679 | pFileFrom->zUuid, |
| 680 | pFileTo->zUuid, showDetail); |
| 681 | pFileFrom = manifest_file_next(pFrom, 0); |
| 682 | pFileTo = manifest_file_next(pTo, 0); |
| 683 | } |
| 684 | } |
| 685 | manifest_destroy(pFrom); |
| 686 | manifest_destroy(pTo); |
| 687 | |
| 688 | style_footer(); |
| 689 | } |
| 690 | |
| 691 | /* |
| @@ -1033,25 +1041,26 @@ | |
| 1041 | */ |
| 1042 | int artifact_from_ci_and_filename(void){ |
| 1043 | const char *zFilename; |
| 1044 | const char *zCI; |
| 1045 | int cirid; |
| 1046 | Manifest *pManifest; |
| 1047 | ManifestFile *pFile; |
| 1048 | |
| 1049 | zCI = P("ci"); |
| 1050 | if( zCI==0 ) return 0; |
| 1051 | zFilename = P("filename"); |
| 1052 | if( zFilename==0 ) return 0; |
| 1053 | cirid = name_to_rid_www("ci"); |
| 1054 | pManifest = manifest_get(cirid, CFTYPE_MANIFEST); |
| 1055 | if( pManifest==0 ) return 0; |
| 1056 | manifest_file_rewind(pManifest); |
| 1057 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 1058 | if( strcmp(zFilename, pFile->zName)==0 ){ |
| 1059 | int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); |
| 1060 | manifest_destroy(pManifest); |
| 1061 | return rid; |
| 1062 | } |
| 1063 | } |
| 1064 | return 0; |
| 1065 | } |
| 1066 | |
| @@ -1158,15 +1167,14 @@ | |
| 1167 | ** |
| 1168 | ** Show the details of a ticket change control artifact. |
| 1169 | */ |
| 1170 | void tinfo_page(void){ |
| 1171 | int rid; |
| 1172 | char *zDate; |
| 1173 | const char *zUuid; |
| 1174 | char zTktName[20]; |
| 1175 | Manifest *pTktChng; |
| 1176 | |
| 1177 | login_check_credentials(); |
| 1178 | if( !g.okRdTkt ){ login_needed(); return; } |
| 1179 | rid = name_to_rid_www("name"); |
| 1180 | if( rid==0 ){ fossil_redirect_home(); } |
| @@ -1178,39 +1186,37 @@ | |
| 1186 | }else{ |
| 1187 | style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", |
| 1188 | g.zTop, zUuid); |
| 1189 | } |
| 1190 | } |
| 1191 | pTktChng = manifest_get(rid, CFTYPE_TICKET); |
| 1192 | if( pTktChng==0 ){ |
| 1193 | fossil_redirect_home(); |
| 1194 | } |
| 1195 | style_header("Ticket Change Details"); |
| 1196 | zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate); |
| 1197 | memcpy(zTktName, pTktChng->zTicketUuid, 10); |
| 1198 | zTktName[10] = 0; |
| 1199 | if( g.okHistory ){ |
| 1200 | @ <h2>Changes to ticket |
| 1201 | @ <a href="%s(pTktChng->zTicketUuid)">%s(zTktName)</a></h2> |
| 1202 | @ |
| 1203 | @ <p>By %h(pTktChng->zUser) on %s(zDate). See also: |
| 1204 | @ <a href="%s(g.zTop)/artifact/%T(zUuid)">artifact content</a>, and |
| 1205 | @ <a href="%s(g.zTop)/tkthistory/%s(pTktChng->zTicketUuid)">ticket |
| 1206 | @ history</a></p> |
| 1207 | }else{ |
| 1208 | @ <h2>Changes to ticket %s(zTktName)</h2> |
| 1209 | @ |
| 1210 | @ <p>By %h(pTktChng->zUser) on %s(zDate). |
| 1211 | @ </p> |
| 1212 | } |
| 1213 | @ |
| 1214 | @ <ol> |
| 1215 | free(zDate); |
| 1216 | ticket_output_change_artifact(pTktChng); |
| 1217 | manifest_destroy(pTktChng); |
| 1218 | style_footer(); |
| 1219 | } |
| 1220 | |
| 1221 | |
| 1222 | /* |
| 1223 |
+5
-6
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -51,15 +51,14 @@ | ||
| 51 | 51 | ** Return the name of the login cookie |
| 52 | 52 | */ |
| 53 | 53 | static char *login_cookie_name(void){ |
| 54 | 54 | static char *zCookieName = 0; |
| 55 | 55 | if( zCookieName==0 ){ |
| 56 | - int n = strlen(g.zTop); | |
| 57 | - zCookieName = malloc( n*2+16 ); | |
| 58 | - /* 0123456789 12345 */ | |
| 59 | - strcpy(zCookieName, "fossil_login_"); | |
| 60 | - encode16((unsigned char*)g.zTop, (unsigned char*)&zCookieName[13], n); | |
| 56 | + unsigned int h = 0; | |
| 57 | + const char *z = g.zBaseURL; | |
| 58 | + while( *z ){ h = (h<<3) ^ (h>>26) ^ *(z++); } | |
| 59 | + zCookieName = mprintf("fossil_login_%08x", h); | |
| 61 | 60 | } |
| 62 | 61 | return zCookieName; |
| 63 | 62 | } |
| 64 | 63 | |
| 65 | 64 | /* |
| @@ -353,11 +352,11 @@ | ||
| 353 | 352 | } |
| 354 | 353 | |
| 355 | 354 | /* Check the login cookie to see if it matches a known valid user. |
| 356 | 355 | */ |
| 357 | 356 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| 358 | - if( isdigit(zCookie[0]) ){ | |
| 357 | + if( fossil_isdigit(zCookie[0]) ){ | |
| 359 | 358 | /* Cookies of the form "uid/randomness". There must be a |
| 360 | 359 | ** corresponding entry in the user table. */ |
| 361 | 360 | uid = db_int(0, |
| 362 | 361 | "SELECT uid FROM user" |
| 363 | 362 | " WHERE uid=%d" |
| 364 | 363 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -51,15 +51,14 @@ | |
| 51 | ** Return the name of the login cookie |
| 52 | */ |
| 53 | static char *login_cookie_name(void){ |
| 54 | static char *zCookieName = 0; |
| 55 | if( zCookieName==0 ){ |
| 56 | int n = strlen(g.zTop); |
| 57 | zCookieName = malloc( n*2+16 ); |
| 58 | /* 0123456789 12345 */ |
| 59 | strcpy(zCookieName, "fossil_login_"); |
| 60 | encode16((unsigned char*)g.zTop, (unsigned char*)&zCookieName[13], n); |
| 61 | } |
| 62 | return zCookieName; |
| 63 | } |
| 64 | |
| 65 | /* |
| @@ -353,11 +352,11 @@ | |
| 353 | } |
| 354 | |
| 355 | /* Check the login cookie to see if it matches a known valid user. |
| 356 | */ |
| 357 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| 358 | if( isdigit(zCookie[0]) ){ |
| 359 | /* Cookies of the form "uid/randomness". There must be a |
| 360 | ** corresponding entry in the user table. */ |
| 361 | uid = db_int(0, |
| 362 | "SELECT uid FROM user" |
| 363 | " WHERE uid=%d" |
| 364 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -51,15 +51,14 @@ | |
| 51 | ** Return the name of the login cookie |
| 52 | */ |
| 53 | static char *login_cookie_name(void){ |
| 54 | static char *zCookieName = 0; |
| 55 | if( zCookieName==0 ){ |
| 56 | unsigned int h = 0; |
| 57 | const char *z = g.zBaseURL; |
| 58 | while( *z ){ h = (h<<3) ^ (h>>26) ^ *(z++); } |
| 59 | zCookieName = mprintf("fossil_login_%08x", h); |
| 60 | } |
| 61 | return zCookieName; |
| 62 | } |
| 63 | |
| 64 | /* |
| @@ -353,11 +352,11 @@ | |
| 352 | } |
| 353 | |
| 354 | /* Check the login cookie to see if it matches a known valid user. |
| 355 | */ |
| 356 | if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ |
| 357 | if( fossil_isdigit(zCookie[0]) ){ |
| 358 | /* Cookies of the form "uid/randomness". There must be a |
| 359 | ** corresponding entry in the user table. */ |
| 360 | uid = db_int(0, |
| 361 | "SELECT uid FROM user" |
| 362 | " WHERE uid=%d" |
| 363 |
+135
-9
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -223,32 +223,39 @@ | ||
| 223 | 223 | */ |
| 224 | 224 | int main(int argc, char **argv){ |
| 225 | 225 | const char *zCmdName = "unknown"; |
| 226 | 226 | int idx; |
| 227 | 227 | int rc; |
| 228 | + int mightBeCgi; | |
| 228 | 229 | |
| 229 | 230 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 230 | 231 | g.now = time(0); |
| 231 | 232 | g.argc = argc; |
| 232 | 233 | g.argv = argv; |
| 233 | - if( getenv("GATEWAY_INTERFACE")!=0 ){ | |
| 234 | - zCmdName = "cgi"; | |
| 235 | - }else if( argc<2 ){ | |
| 236 | - fprintf(stderr, "Usage: %s COMMAND ...\n" | |
| 237 | - "\"%s help\" for a list of available commands\n" | |
| 238 | - "\"%s help COMMAND\" for specific details\n", | |
| 239 | - argv[0], argv[0], argv[0]); | |
| 240 | - fossil_exit(1); | |
| 234 | + mightBeCgi = getenv("GATEWAY_INTERFACE")!=0; | |
| 235 | + if( argc<2 ){ | |
| 236 | + if( mightBeCgi ){ | |
| 237 | + zCmdName = "cgi"; | |
| 238 | + }else{ | |
| 239 | + fprintf(stderr, "Usage: %s COMMAND ...\n" | |
| 240 | + "\"%s help\" for a list of available commands\n" | |
| 241 | + "\"%s help COMMAND\" for specific details\n", | |
| 242 | + argv[0], argv[0], argv[0]); | |
| 243 | + fossil_exit(1); | |
| 244 | + } | |
| 241 | 245 | }else{ |
| 242 | 246 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 243 | 247 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 244 | 248 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 245 | 249 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 246 | 250 | g.zLogin = find_option("user", "U", 1); |
| 247 | 251 | zCmdName = argv[1]; |
| 248 | 252 | } |
| 249 | 253 | rc = name_search(zCmdName, aCommand, count(aCommand), &idx); |
| 254 | + if( rc==1 && mightBeCgi ){ | |
| 255 | + rc = name_search("cgi", aCommand, count(aCommand), &idx); | |
| 256 | + } | |
| 250 | 257 | if( rc==1 ){ |
| 251 | 258 | fprintf(stderr,"%s: unknown command: %s\n" |
| 252 | 259 | "%s: use \"help\" for more information\n", |
| 253 | 260 | argv[0], zCmdName, argv[0]); |
| 254 | 261 | fossil_exit(1); |
| @@ -359,10 +366,49 @@ | ||
| 359 | 366 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 360 | 367 | }else{ |
| 361 | 368 | fprintf(stderr, "%s: %s\n", g.argv[0], z); |
| 362 | 369 | } |
| 363 | 370 | } |
| 371 | + | |
| 372 | +/* | |
| 373 | +** Malloc and free routines that cannot fail | |
| 374 | +*/ | |
| 375 | +void *fossil_malloc(size_t n){ | |
| 376 | + void *p = malloc(n); | |
| 377 | + if( p==0 ) fossil_panic("out of memory"); | |
| 378 | + return p; | |
| 379 | +} | |
| 380 | +void fossil_free(void *p){ | |
| 381 | + free(p); | |
| 382 | +} | |
| 383 | +void *fossil_realloc(void *p, size_t n){ | |
| 384 | + p = realloc(p, n); | |
| 385 | + if( p==0 ) fossil_panic("out of memory"); | |
| 386 | + return p; | |
| 387 | +} | |
| 388 | + | |
| 389 | +/* | |
| 390 | +** This function implements a cross-platform "system()" interface. | |
| 391 | +*/ | |
| 392 | +int fossil_system(const char *zOrigCmd){ | |
| 393 | + int rc; | |
| 394 | +#if defined(_WIN32) | |
| 395 | + /* On windows, we have to put double-quotes around the entire command. | |
| 396 | + ** Who knows why - this is just the way windows works. | |
| 397 | + */ | |
| 398 | + char *zNewCmd = mprintf("\"%s\"", zOrigCmd); | |
| 399 | + rc = system(zNewCmd); | |
| 400 | + free(zNewCmd); | |
| 401 | +#else | |
| 402 | + /* On unix, evaluate the command directly. | |
| 403 | + */ | |
| 404 | + rc = system(zOrigCmd); | |
| 405 | +#endif | |
| 406 | + return rc; | |
| 407 | +} | |
| 408 | + | |
| 409 | + | |
| 364 | 410 | |
| 365 | 411 | /* |
| 366 | 412 | ** Return a name for an SQLite error code |
| 367 | 413 | */ |
| 368 | 414 | static const char *sqlite_error_code_name(int iCode){ |
| @@ -591,10 +637,62 @@ | ||
| 591 | 637 | z++; |
| 592 | 638 | } |
| 593 | 639 | } |
| 594 | 640 | putchar('\n'); |
| 595 | 641 | } |
| 642 | + | |
| 643 | +/* | |
| 644 | +** WEBPAGE: help | |
| 645 | +** URL: /help?cmd=CMD | |
| 646 | +*/ | |
| 647 | +void help_page(void){ | |
| 648 | + const char * zCmd = P("cmd"); | |
| 649 | + | |
| 650 | + style_header("Command line help %s%s",zCmd?" - ":"",zCmd?zCmd:""); | |
| 651 | + if( zCmd ){ | |
| 652 | + int rc, idx; | |
| 653 | + char *z, *s, *d; | |
| 654 | + | |
| 655 | + @ <h1>%s(zCmd)</h1> | |
| 656 | + rc = name_search(zCmd, aCommand, count(aCommand), &idx); | |
| 657 | + if( rc==1 ){ | |
| 658 | + @ unknown command: %s(zCmd) | |
| 659 | + }else if( rc==2 ){ | |
| 660 | + @ ambiguous command prefix: %s(zCmd) | |
| 661 | + }else{ | |
| 662 | + z = (char*)aCmdHelp[idx]; | |
| 663 | + if( z==0 ){ | |
| 664 | + @ no help available for the %s(aCommand[idx].zName) command | |
| 665 | + }else{ | |
| 666 | + z=s=d=mprintf("%s",z); | |
| 667 | + while( *s ){ | |
| 668 | + if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){ | |
| 669 | + s++; | |
| 670 | + }else{ | |
| 671 | + *d++ = *s++; | |
| 672 | + } | |
| 673 | + } | |
| 674 | + *d = 0; | |
| 675 | + @ <pre>%s(z)</pre> | |
| 676 | + free(z); | |
| 677 | + } | |
| 678 | + } | |
| 679 | + @ <hr/><a href="help">available commands</a> in fossil | |
| 680 | + @ version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC | |
| 681 | + }else{ | |
| 682 | + int i; | |
| 683 | + | |
| 684 | + @ <h1>Available commands</h1> | |
| 685 | + for(i=0; i<count(aCommand); i++){ | |
| 686 | + if( strncmp(aCommand[i].zName,"test",4)==0 ) continue; | |
| 687 | + @ <kbd><a href="help?cmd=%s(aCommand[i].zName)"> | |
| 688 | + @ %s(aCommand[i].zName)</a></kbd> | |
| 689 | + } | |
| 690 | + @ <hr/>fossil version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC | |
| 691 | + } | |
| 692 | + style_footer(); | |
| 693 | +} | |
| 596 | 694 | |
| 597 | 695 | /* |
| 598 | 696 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 599 | 697 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 600 | 698 | ** leading "http://" and the host and port. |
| @@ -705,11 +803,11 @@ | ||
| 705 | 803 | |
| 706 | 804 | /* To avoid mischief, make sure the repository basename contains no |
| 707 | 805 | ** characters other than alphanumerics, "-", and "_". |
| 708 | 806 | */ |
| 709 | 807 | for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ |
| 710 | - if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; | |
| 808 | + if( !fossil_isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; | |
| 711 | 809 | } |
| 712 | 810 | if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++; |
| 713 | 811 | |
| 714 | 812 | if( file_size(zRepo)<1024 ){ |
| 715 | 813 | if( zNotFound ){ |
| @@ -1076,5 +1174,33 @@ | ||
| 1076 | 1174 | } |
| 1077 | 1175 | db_close(); |
| 1078 | 1176 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); |
| 1079 | 1177 | #endif |
| 1080 | 1178 | } |
| 1179 | + | |
| 1180 | +/* | |
| 1181 | +** COMMAND: sqlite3 | |
| 1182 | +** | |
| 1183 | +** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? | |
| 1184 | +** | |
| 1185 | +** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. | |
| 1186 | +** If DATABASE is omitted, then the repository that serves the working | |
| 1187 | +** directory is opened. | |
| 1188 | +** | |
| 1189 | +** WARNING: Careless use of this command can corrupt a Fossil repository | |
| 1190 | +** in ways that are unrecoverable. Be sure you know what you are doing before | |
| 1191 | +** running any SQL commands that modifies the repository database. | |
| 1192 | +*/ | |
| 1193 | +void sqlite3_cmd(void){ | |
| 1194 | + extern int sqlite3_shell(int, char**); | |
| 1195 | + sqlite3_shell(g.argc-1, g.argv+1); | |
| 1196 | +} | |
| 1197 | + | |
| 1198 | +/* | |
| 1199 | +** This routine is called by the patched sqlite3 command-line shell in order | |
| 1200 | +** to load the name and database connection for the open Fossil database. | |
| 1201 | +*/ | |
| 1202 | +void fossil_open(sqlite3 **pDb, const char **pzRepoName){ | |
| 1203 | + db_must_be_within_tree(); | |
| 1204 | + *pDb = 0; | |
| 1205 | + *pzRepoName = g.zRepositoryName; | |
| 1206 | +} | |
| 1081 | 1207 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -223,32 +223,39 @@ | |
| 223 | */ |
| 224 | int main(int argc, char **argv){ |
| 225 | const char *zCmdName = "unknown"; |
| 226 | int idx; |
| 227 | int rc; |
| 228 | |
| 229 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 230 | g.now = time(0); |
| 231 | g.argc = argc; |
| 232 | g.argv = argv; |
| 233 | if( getenv("GATEWAY_INTERFACE")!=0 ){ |
| 234 | zCmdName = "cgi"; |
| 235 | }else if( argc<2 ){ |
| 236 | fprintf(stderr, "Usage: %s COMMAND ...\n" |
| 237 | "\"%s help\" for a list of available commands\n" |
| 238 | "\"%s help COMMAND\" for specific details\n", |
| 239 | argv[0], argv[0], argv[0]); |
| 240 | fossil_exit(1); |
| 241 | }else{ |
| 242 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 243 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 244 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 245 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 246 | g.zLogin = find_option("user", "U", 1); |
| 247 | zCmdName = argv[1]; |
| 248 | } |
| 249 | rc = name_search(zCmdName, aCommand, count(aCommand), &idx); |
| 250 | if( rc==1 ){ |
| 251 | fprintf(stderr,"%s: unknown command: %s\n" |
| 252 | "%s: use \"help\" for more information\n", |
| 253 | argv[0], zCmdName, argv[0]); |
| 254 | fossil_exit(1); |
| @@ -359,10 +366,49 @@ | |
| 359 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 360 | }else{ |
| 361 | fprintf(stderr, "%s: %s\n", g.argv[0], z); |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | /* |
| 366 | ** Return a name for an SQLite error code |
| 367 | */ |
| 368 | static const char *sqlite_error_code_name(int iCode){ |
| @@ -591,10 +637,62 @@ | |
| 591 | z++; |
| 592 | } |
| 593 | } |
| 594 | putchar('\n'); |
| 595 | } |
| 596 | |
| 597 | /* |
| 598 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 599 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 600 | ** leading "http://" and the host and port. |
| @@ -705,11 +803,11 @@ | |
| 705 | |
| 706 | /* To avoid mischief, make sure the repository basename contains no |
| 707 | ** characters other than alphanumerics, "-", and "_". |
| 708 | */ |
| 709 | for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ |
| 710 | if( !isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; |
| 711 | } |
| 712 | if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++; |
| 713 | |
| 714 | if( file_size(zRepo)<1024 ){ |
| 715 | if( zNotFound ){ |
| @@ -1076,5 +1174,33 @@ | |
| 1076 | } |
| 1077 | db_close(); |
| 1078 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); |
| 1079 | #endif |
| 1080 | } |
| 1081 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -223,32 +223,39 @@ | |
| 223 | */ |
| 224 | int main(int argc, char **argv){ |
| 225 | const char *zCmdName = "unknown"; |
| 226 | int idx; |
| 227 | int rc; |
| 228 | int mightBeCgi; |
| 229 | |
| 230 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 231 | g.now = time(0); |
| 232 | g.argc = argc; |
| 233 | g.argv = argv; |
| 234 | mightBeCgi = getenv("GATEWAY_INTERFACE")!=0; |
| 235 | if( argc<2 ){ |
| 236 | if( mightBeCgi ){ |
| 237 | zCmdName = "cgi"; |
| 238 | }else{ |
| 239 | fprintf(stderr, "Usage: %s COMMAND ...\n" |
| 240 | "\"%s help\" for a list of available commands\n" |
| 241 | "\"%s help COMMAND\" for specific details\n", |
| 242 | argv[0], argv[0], argv[0]); |
| 243 | fossil_exit(1); |
| 244 | } |
| 245 | }else{ |
| 246 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 247 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 248 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 249 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 250 | g.zLogin = find_option("user", "U", 1); |
| 251 | zCmdName = argv[1]; |
| 252 | } |
| 253 | rc = name_search(zCmdName, aCommand, count(aCommand), &idx); |
| 254 | if( rc==1 && mightBeCgi ){ |
| 255 | rc = name_search("cgi", aCommand, count(aCommand), &idx); |
| 256 | } |
| 257 | if( rc==1 ){ |
| 258 | fprintf(stderr,"%s: unknown command: %s\n" |
| 259 | "%s: use \"help\" for more information\n", |
| 260 | argv[0], zCmdName, argv[0]); |
| 261 | fossil_exit(1); |
| @@ -359,10 +366,49 @@ | |
| 366 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 367 | }else{ |
| 368 | fprintf(stderr, "%s: %s\n", g.argv[0], z); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | ** Malloc and free routines that cannot fail |
| 374 | */ |
| 375 | void *fossil_malloc(size_t n){ |
| 376 | void *p = malloc(n); |
| 377 | if( p==0 ) fossil_panic("out of memory"); |
| 378 | return p; |
| 379 | } |
| 380 | void fossil_free(void *p){ |
| 381 | free(p); |
| 382 | } |
| 383 | void *fossil_realloc(void *p, size_t n){ |
| 384 | p = realloc(p, n); |
| 385 | if( p==0 ) fossil_panic("out of memory"); |
| 386 | return p; |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 | ** This function implements a cross-platform "system()" interface. |
| 391 | */ |
| 392 | int fossil_system(const char *zOrigCmd){ |
| 393 | int rc; |
| 394 | #if defined(_WIN32) |
| 395 | /* On windows, we have to put double-quotes around the entire command. |
| 396 | ** Who knows why - this is just the way windows works. |
| 397 | */ |
| 398 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 399 | rc = system(zNewCmd); |
| 400 | free(zNewCmd); |
| 401 | #else |
| 402 | /* On unix, evaluate the command directly. |
| 403 | */ |
| 404 | rc = system(zOrigCmd); |
| 405 | #endif |
| 406 | return rc; |
| 407 | } |
| 408 | |
| 409 | |
| 410 | |
| 411 | /* |
| 412 | ** Return a name for an SQLite error code |
| 413 | */ |
| 414 | static const char *sqlite_error_code_name(int iCode){ |
| @@ -591,10 +637,62 @@ | |
| 637 | z++; |
| 638 | } |
| 639 | } |
| 640 | putchar('\n'); |
| 641 | } |
| 642 | |
| 643 | /* |
| 644 | ** WEBPAGE: help |
| 645 | ** URL: /help?cmd=CMD |
| 646 | */ |
| 647 | void help_page(void){ |
| 648 | const char * zCmd = P("cmd"); |
| 649 | |
| 650 | style_header("Command line help %s%s",zCmd?" - ":"",zCmd?zCmd:""); |
| 651 | if( zCmd ){ |
| 652 | int rc, idx; |
| 653 | char *z, *s, *d; |
| 654 | |
| 655 | @ <h1>%s(zCmd)</h1> |
| 656 | rc = name_search(zCmd, aCommand, count(aCommand), &idx); |
| 657 | if( rc==1 ){ |
| 658 | @ unknown command: %s(zCmd) |
| 659 | }else if( rc==2 ){ |
| 660 | @ ambiguous command prefix: %s(zCmd) |
| 661 | }else{ |
| 662 | z = (char*)aCmdHelp[idx]; |
| 663 | if( z==0 ){ |
| 664 | @ no help available for the %s(aCommand[idx].zName) command |
| 665 | }else{ |
| 666 | z=s=d=mprintf("%s",z); |
| 667 | while( *s ){ |
| 668 | if( *s=='%' && strncmp(s, "%fossil", 7)==0 ){ |
| 669 | s++; |
| 670 | }else{ |
| 671 | *d++ = *s++; |
| 672 | } |
| 673 | } |
| 674 | *d = 0; |
| 675 | @ <pre>%s(z)</pre> |
| 676 | free(z); |
| 677 | } |
| 678 | } |
| 679 | @ <hr/><a href="help">available commands</a> in fossil |
| 680 | @ version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC |
| 681 | }else{ |
| 682 | int i; |
| 683 | |
| 684 | @ <h1>Available commands</h1> |
| 685 | for(i=0; i<count(aCommand); i++){ |
| 686 | if( strncmp(aCommand[i].zName,"test",4)==0 ) continue; |
| 687 | @ <kbd><a href="help?cmd=%s(aCommand[i].zName)"> |
| 688 | @ %s(aCommand[i].zName)</a></kbd> |
| 689 | } |
| 690 | @ <hr/>fossil version %s(MANIFEST_VERSION" "MANIFEST_DATE) UTC |
| 691 | } |
| 692 | style_footer(); |
| 693 | } |
| 694 | |
| 695 | /* |
| 696 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 697 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 698 | ** leading "http://" and the host and port. |
| @@ -705,11 +803,11 @@ | |
| 803 | |
| 804 | /* To avoid mischief, make sure the repository basename contains no |
| 805 | ** characters other than alphanumerics, "-", and "_". |
| 806 | */ |
| 807 | for(j=strlen(g.zRepositoryName)+1, k=0; k<i-1; j++, k++){ |
| 808 | if( !fossil_isalnum(zRepo[j]) && zRepo[j]!='-' ) zRepo[j] = '_'; |
| 809 | } |
| 810 | if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++; |
| 811 | |
| 812 | if( file_size(zRepo)<1024 ){ |
| 813 | if( zNotFound ){ |
| @@ -1076,5 +1174,33 @@ | |
| 1174 | } |
| 1175 | db_close(); |
| 1176 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); |
| 1177 | #endif |
| 1178 | } |
| 1179 | |
| 1180 | /* |
| 1181 | ** COMMAND: sqlite3 |
| 1182 | ** |
| 1183 | ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? |
| 1184 | ** |
| 1185 | ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. |
| 1186 | ** If DATABASE is omitted, then the repository that serves the working |
| 1187 | ** directory is opened. |
| 1188 | ** |
| 1189 | ** WARNING: Careless use of this command can corrupt a Fossil repository |
| 1190 | ** in ways that are unrecoverable. Be sure you know what you are doing before |
| 1191 | ** running any SQL commands that modifies the repository database. |
| 1192 | */ |
| 1193 | void sqlite3_cmd(void){ |
| 1194 | extern int sqlite3_shell(int, char**); |
| 1195 | sqlite3_shell(g.argc-1, g.argv+1); |
| 1196 | } |
| 1197 | |
| 1198 | /* |
| 1199 | ** This routine is called by the patched sqlite3 command-line shell in order |
| 1200 | ** to load the name and database connection for the open Fossil database. |
| 1201 | */ |
| 1202 | void fossil_open(sqlite3 **pDb, const char **pzRepoName){ |
| 1203 | db_must_be_within_tree(); |
| 1204 | *pDb = 0; |
| 1205 | *pzRepoName = g.zRepositoryName; |
| 1206 | } |
| 1207 |
+7
-2
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -264,12 +264,14 @@ | ||
| 264 | 264 | VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest |
| 265 | 265 | awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h |
| 266 | 266 | awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 267 | 267 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h |
| 268 | 268 | |
| 269 | -$(APPNAME): headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o | |
| 270 | - $(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB) | |
| 269 | +EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o | |
| 270 | + | |
| 271 | +$(APPNAME): headers $(OBJ) $(EXTRAOBJ) | |
| 272 | + $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) | |
| 271 | 273 | |
| 272 | 274 | # This rule prevents make from using its default rules to try build |
| 273 | 275 | # an executable named "manifest" out of the file named "manifest.c" |
| 274 | 276 | # |
| 275 | 277 | $(SRCDIR)/../manifest: |
| @@ -792,11 +794,14 @@ | ||
| 792 | 794 | |
| 793 | 795 | zip.h: headers |
| 794 | 796 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 795 | 797 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 796 | 798 | |
| 799 | +$(OBJDIR)/shell.o: $(SRCDIR)/shell.c | |
| 800 | + $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o | |
| 801 | + | |
| 797 | 802 | $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| 798 | 803 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o |
| 799 | 804 | |
| 800 | 805 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 801 | 806 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
| 802 | 807 | |
| 803 | 808 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -264,12 +264,14 @@ | |
| 264 | VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest |
| 265 | awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h |
| 266 | awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 267 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h |
| 268 | |
| 269 | $(APPNAME): headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o |
| 270 | $(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB) |
| 271 | |
| 272 | # This rule prevents make from using its default rules to try build |
| 273 | # an executable named "manifest" out of the file named "manifest.c" |
| 274 | # |
| 275 | $(SRCDIR)/../manifest: |
| @@ -792,11 +794,14 @@ | |
| 792 | |
| 793 | zip.h: headers |
| 794 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 795 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 796 | |
| 797 | $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| 798 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o |
| 799 | |
| 800 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 801 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
| 802 | |
| 803 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -264,12 +264,14 @@ | |
| 264 | VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest |
| 265 | awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h |
| 266 | awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 267 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h |
| 268 | |
| 269 | EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o |
| 270 | |
| 271 | $(APPNAME): headers $(OBJ) $(EXTRAOBJ) |
| 272 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) |
| 273 | |
| 274 | # This rule prevents make from using its default rules to try build |
| 275 | # an executable named "manifest" out of the file named "manifest.c" |
| 276 | # |
| 277 | $(SRCDIR)/../manifest: |
| @@ -792,11 +794,14 @@ | |
| 794 | |
| 795 | zip.h: headers |
| 796 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 797 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 798 | |
| 799 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c |
| 800 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 801 | |
| 802 | $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| 803 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o |
| 804 | |
| 805 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 806 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
| 807 | |
| 808 |
+1
-1
| --- src/makeheaders.c | ||
| +++ src/makeheaders.c | ||
| @@ -13,11 +13,11 @@ | ||
| 13 | 13 | #include <stdlib.h> |
| 14 | 14 | #include <ctype.h> |
| 15 | 15 | #include <memory.h> |
| 16 | 16 | #include <sys/stat.h> |
| 17 | 17 | #include <assert.h> |
| 18 | -#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) | |
| 18 | +#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) | |
| 19 | 19 | # ifndef WIN32 |
| 20 | 20 | # define WIN32 |
| 21 | 21 | # endif |
| 22 | 22 | # include <string.h> |
| 23 | 23 | #else |
| 24 | 24 |
| --- src/makeheaders.c | |
| +++ src/makeheaders.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | #include <stdlib.h> |
| 14 | #include <ctype.h> |
| 15 | #include <memory.h> |
| 16 | #include <sys/stat.h> |
| 17 | #include <assert.h> |
| 18 | #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) |
| 19 | # ifndef WIN32 |
| 20 | # define WIN32 |
| 21 | # endif |
| 22 | # include <string.h> |
| 23 | #else |
| 24 |
| --- src/makeheaders.c | |
| +++ src/makeheaders.c | |
| @@ -13,11 +13,11 @@ | |
| 13 | #include <stdlib.h> |
| 14 | #include <ctype.h> |
| 15 | #include <memory.h> |
| 16 | #include <sys/stat.h> |
| 17 | #include <assert.h> |
| 18 | #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) |
| 19 | # ifndef WIN32 |
| 20 | # define WIN32 |
| 21 | # endif |
| 22 | # include <string.h> |
| 23 | #else |
| 24 |
+13
-2
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -148,12 +148,18 @@ | ||
| 148 | 148 | $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 149 | 149 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\ |
| 150 | 150 | substr($$2,1,10),substr($$2,12)}' \ |
| 151 | 151 | $(SRCDIR)/../manifest >>VERSION.h |
| 152 | 152 | |
| 153 | -$(APPNAME): headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o | |
| 154 | - $(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB) | |
| 153 | +EXTRAOBJ = \ | |
| 154 | + $(OBJDIR)/sqlite3.o \ | |
| 155 | + $(OBJDIR)/shell.o \ | |
| 156 | + $(OBJDIR)/th.o \ | |
| 157 | + $(OBJDIR)/th_lang.o | |
| 158 | + | |
| 159 | +$(APPNAME): headers $(OBJ) $(EXTRAOBJ) | |
| 160 | + $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) | |
| 155 | 161 | |
| 156 | 162 | # This rule prevents make from using its default rules to try build |
| 157 | 163 | # an executable named "manifest" out of the file named "manifest.c" |
| 158 | 164 | # |
| 159 | 165 | $(SRCDIR)/../manifest: |
| @@ -201,10 +207,15 @@ | ||
| 201 | 207 | #append opt " -DSQLITE_ENABLE_FTS3=1" |
| 202 | 208 | append opt " -Dlocaltime=fossil_localtime" |
| 203 | 209 | append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0" |
| 204 | 210 | puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 205 | 211 | |
| 212 | +puts "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c" | |
| 213 | +set opt {-Dmain=sqlite3_shell} | |
| 214 | +append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" | |
| 215 | +puts "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" | |
| 216 | + | |
| 206 | 217 | puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" |
| 207 | 218 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" |
| 208 | 219 | |
| 209 | 220 | puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" |
| 210 | 221 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" |
| 211 | 222 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -148,12 +148,18 @@ | |
| 148 | $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 149 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\ |
| 150 | substr($$2,1,10),substr($$2,12)}' \ |
| 151 | $(SRCDIR)/../manifest >>VERSION.h |
| 152 | |
| 153 | $(APPNAME): headers $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o |
| 154 | $(TCC) -o $(APPNAME) $(OBJ) $(OBJDIR)/sqlite3.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(LIB) |
| 155 | |
| 156 | # This rule prevents make from using its default rules to try build |
| 157 | # an executable named "manifest" out of the file named "manifest.c" |
| 158 | # |
| 159 | $(SRCDIR)/../manifest: |
| @@ -201,10 +207,15 @@ | |
| 201 | #append opt " -DSQLITE_ENABLE_FTS3=1" |
| 202 | append opt " -Dlocaltime=fossil_localtime" |
| 203 | append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0" |
| 204 | puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 205 | |
| 206 | puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" |
| 207 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" |
| 208 | |
| 209 | puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" |
| 210 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" |
| 211 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -148,12 +148,18 @@ | |
| 148 | $(SRCDIR)/../manifest.uuid >>VERSION.h |
| 149 | awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\ |
| 150 | substr($$2,1,10),substr($$2,12)}' \ |
| 151 | $(SRCDIR)/../manifest >>VERSION.h |
| 152 | |
| 153 | EXTRAOBJ = \ |
| 154 | $(OBJDIR)/sqlite3.o \ |
| 155 | $(OBJDIR)/shell.o \ |
| 156 | $(OBJDIR)/th.o \ |
| 157 | $(OBJDIR)/th_lang.o |
| 158 | |
| 159 | $(APPNAME): headers $(OBJ) $(EXTRAOBJ) |
| 160 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) |
| 161 | |
| 162 | # This rule prevents make from using its default rules to try build |
| 163 | # an executable named "manifest" out of the file named "manifest.c" |
| 164 | # |
| 165 | $(SRCDIR)/../manifest: |
| @@ -201,10 +207,15 @@ | |
| 207 | #append opt " -DSQLITE_ENABLE_FTS3=1" |
| 208 | append opt " -Dlocaltime=fossil_localtime" |
| 209 | append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0" |
| 210 | puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 211 | |
| 212 | puts "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c" |
| 213 | set opt {-Dmain=sqlite3_shell} |
| 214 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| 215 | puts "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" |
| 216 | |
| 217 | puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" |
| 218 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" |
| 219 | |
| 220 | puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" |
| 221 | puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" |
| 222 |
+731
-369
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -26,24 +26,39 @@ | ||
| 26 | 26 | |
| 27 | 27 | #if INTERFACE |
| 28 | 28 | /* |
| 29 | 29 | ** Types of control files |
| 30 | 30 | */ |
| 31 | +#define CFTYPE_ANY 0 | |
| 31 | 32 | #define CFTYPE_MANIFEST 1 |
| 32 | 33 | #define CFTYPE_CLUSTER 2 |
| 33 | 34 | #define CFTYPE_CONTROL 3 |
| 34 | 35 | #define CFTYPE_WIKI 4 |
| 35 | 36 | #define CFTYPE_TICKET 5 |
| 36 | 37 | #define CFTYPE_ATTACHMENT 6 |
| 37 | 38 | #define CFTYPE_EVENT 7 |
| 39 | + | |
| 40 | +/* | |
| 41 | +** A single F-card within a manifest | |
| 42 | +*/ | |
| 43 | +struct ManifestFile { | |
| 44 | + char *zName; /* Name of a file */ | |
| 45 | + char *zUuid; /* UUID of the file */ | |
| 46 | + char *zPerm; /* File permissions */ | |
| 47 | + char *zPrior; /* Prior name if the name was changed */ | |
| 48 | +}; | |
| 49 | + | |
| 38 | 50 | |
| 39 | 51 | /* |
| 40 | 52 | ** A parsed manifest or cluster. |
| 41 | 53 | */ |
| 42 | 54 | struct Manifest { |
| 43 | 55 | Blob content; /* The original content blob */ |
| 44 | 56 | int type; /* Type of artifact. One of CFTYPE_xxxxx */ |
| 57 | + int rid; /* The blob-id for this manifest */ | |
| 58 | + char *zBaseline; /* Baseline manifest. The B card. */ | |
| 59 | + Manifest *pBaseline; /* The actual baseline manifest */ | |
| 45 | 60 | char *zComment; /* Decoded comment. The C card. */ |
| 46 | 61 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 47 | 62 | char *zUser; /* Name of the user from the U card. */ |
| 48 | 63 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 49 | 64 | char *zWiki; /* Text of the wiki page. W card. */ |
| @@ -54,17 +69,12 @@ | ||
| 54 | 69 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 55 | 70 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 56 | 71 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 57 | 72 | int nFile; /* Number of F cards */ |
| 58 | 73 | int nFileAlloc; /* Slots allocated in aFile[] */ |
| 59 | - struct { | |
| 60 | - char *zName; /* Name of a file */ | |
| 61 | - char *zUuid; /* UUID of the file */ | |
| 62 | - char *zPerm; /* File permissions */ | |
| 63 | - char *zPrior; /* Prior name if the name was changed */ | |
| 64 | - int iRename; /* index of renamed name in prior/next manifest */ | |
| 65 | - } *aFile; /* One entry for each F card */ | |
| 74 | + int iFile; /* Index of current file in iterator */ | |
| 75 | + ManifestFile *aFile; /* One entry for each F-card */ | |
| 66 | 76 | int nParent; /* Number of parents. */ |
| 67 | 77 | int nParentAlloc; /* Slots allocated in azParent[] */ |
| 68 | 78 | char **azParent; /* UUIDs of parents. One for each P card argument */ |
| 69 | 79 | int nCChild; /* Number of cluster children */ |
| 70 | 80 | int nCChildAlloc; /* Number of closts allocated in azCChild[] */ |
| @@ -83,22 +93,192 @@ | ||
| 83 | 93 | char *zValue; /* Value of the field */ |
| 84 | 94 | } *aField; /* One for each J card */ |
| 85 | 95 | }; |
| 86 | 96 | #endif |
| 87 | 97 | |
| 98 | +/* | |
| 99 | +** A cache of parsed manifests. This reduces the number of | |
| 100 | +** calls to manifest_parse() when doing a rebuild. | |
| 101 | +*/ | |
| 102 | +#define MX_MANIFEST_CACHE 6 | |
| 103 | +static struct { | |
| 104 | + int nxAge; | |
| 105 | + int aAge[MX_MANIFEST_CACHE]; | |
| 106 | + Manifest *apManifest[MX_MANIFEST_CACHE]; | |
| 107 | +} manifestCache; | |
| 108 | + | |
| 88 | 109 | |
| 89 | 110 | /* |
| 90 | 111 | ** Clear the memory allocated in a manifest object |
| 91 | 112 | */ |
| 92 | -void manifest_clear(Manifest *p){ | |
| 93 | - blob_reset(&p->content); | |
| 94 | - free(p->aFile); | |
| 95 | - free(p->azParent); | |
| 96 | - free(p->azCChild); | |
| 97 | - free(p->aTag); | |
| 98 | - free(p->aField); | |
| 99 | - memset(p, 0, sizeof(*p)); | |
| 113 | +void manifest_destroy(Manifest *p){ | |
| 114 | + if( p ){ | |
| 115 | + blob_reset(&p->content); | |
| 116 | + free(p->aFile); | |
| 117 | + free(p->azParent); | |
| 118 | + free(p->azCChild); | |
| 119 | + free(p->aTag); | |
| 120 | + free(p->aField); | |
| 121 | + if( p->pBaseline ) manifest_destroy(p->pBaseline); | |
| 122 | + fossil_free(p); | |
| 123 | + } | |
| 124 | +} | |
| 125 | + | |
| 126 | +/* | |
| 127 | +** Add an element to the manifest cache using LRU replacement. | |
| 128 | +*/ | |
| 129 | +void manifest_cache_insert(Manifest *p){ | |
| 130 | + while( p ){ | |
| 131 | + int i; | |
| 132 | + Manifest *pBaseline = p->pBaseline; | |
| 133 | + p->pBaseline = 0; | |
| 134 | + for(i=0; i<MX_MANIFEST_CACHE; i++){ | |
| 135 | + if( manifestCache.apManifest[i]==0 ) break; | |
| 136 | + } | |
| 137 | + if( i>=MX_MANIFEST_CACHE ){ | |
| 138 | + int oldest = 0; | |
| 139 | + int oldestAge = manifestCache.aAge[0]; | |
| 140 | + for(i=1; i<MX_MANIFEST_CACHE; i++){ | |
| 141 | + if( manifestCache.aAge[i]<oldestAge ){ | |
| 142 | + oldest = i; | |
| 143 | + oldestAge = manifestCache.aAge[i]; | |
| 144 | + } | |
| 145 | + } | |
| 146 | + manifest_destroy(manifestCache.apManifest[oldest]); | |
| 147 | + i = oldest; | |
| 148 | + } | |
| 149 | + manifestCache.aAge[i] = ++manifestCache.nxAge; | |
| 150 | + manifestCache.apManifest[i] = p; | |
| 151 | + p = pBaseline; | |
| 152 | + } | |
| 153 | +} | |
| 154 | + | |
| 155 | +/* | |
| 156 | +** Try to extract a line from the manifest cache. Return 1 if found. | |
| 157 | +** Return 0 if not found. | |
| 158 | +*/ | |
| 159 | +static Manifest *manifest_cache_find(int rid){ | |
| 160 | + int i; | |
| 161 | + Manifest *p; | |
| 162 | + for(i=0; i<MX_MANIFEST_CACHE; i++){ | |
| 163 | + if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){ | |
| 164 | + p = manifestCache.apManifest[i]; | |
| 165 | + manifestCache.apManifest[i] = 0; | |
| 166 | + return p; | |
| 167 | + } | |
| 168 | + } | |
| 169 | + return 0; | |
| 170 | +} | |
| 171 | + | |
| 172 | +/* | |
| 173 | +** Clear the manifest cache. | |
| 174 | +*/ | |
| 175 | +void manifest_cache_clear(void){ | |
| 176 | + int i; | |
| 177 | + for(i=0; i<MX_MANIFEST_CACHE; i++){ | |
| 178 | + if( manifestCache.apManifest[i] ){ | |
| 179 | + manifest_destroy(manifestCache.apManifest[i]); | |
| 180 | + } | |
| 181 | + } | |
| 182 | + memset(&manifestCache, 0, sizeof(manifestCache)); | |
| 183 | +} | |
| 184 | + | |
| 185 | +#ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM | |
| 186 | +# define md5sum_init(X) | |
| 187 | +# define md5sum_step_text(X,Y) | |
| 188 | +#endif | |
| 189 | + | |
| 190 | +/* | |
| 191 | +** Remove the PGP signature from the artifact, if there is one. | |
| 192 | +*/ | |
| 193 | +static void remove_pgp_signature(char **pz, int *pn){ | |
| 194 | + char *z = *pz; | |
| 195 | + int n = *pn; | |
| 196 | + int i; | |
| 197 | + if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; | |
| 198 | + for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){} | |
| 199 | + if( i>=n ) return; | |
| 200 | + z += i; | |
| 201 | + n -= i; | |
| 202 | + *pz = z; | |
| 203 | + for(i=n-1; i>=0; i--){ | |
| 204 | + if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ | |
| 205 | + n = i+1; | |
| 206 | + break; | |
| 207 | + } | |
| 208 | + } | |
| 209 | + *pn = n; | |
| 210 | + return; | |
| 211 | +} | |
| 212 | + | |
| 213 | +/* | |
| 214 | +** Verify the Z-card checksum on the artifact, if there is such a | |
| 215 | +** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card | |
| 216 | +** exists and is correct. Return 2 if the Z-card exists and has the wrong | |
| 217 | +** value. | |
| 218 | +** | |
| 219 | +** 0123456789 123456789 123456789 123456789 | |
| 220 | +** Z aea84f4f863865a8d59d0384e4d2a41c | |
| 221 | +*/ | |
| 222 | +static int verify_z_card(const char *z, int n){ | |
| 223 | + if( n<35 ) return 0; | |
| 224 | + if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0; | |
| 225 | + md5sum_init(); | |
| 226 | + md5sum_step_text(z, n-35); | |
| 227 | + if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){ | |
| 228 | + return 1; | |
| 229 | + }else{ | |
| 230 | + return 2; | |
| 231 | + } | |
| 232 | +} | |
| 233 | + | |
| 234 | +/* | |
| 235 | +** A structure used for rapid parsing of the Manifest file | |
| 236 | +*/ | |
| 237 | +typedef struct ManifestText ManifestText; | |
| 238 | +struct ManifestText { | |
| 239 | + char *z; /* The first character of the next token */ | |
| 240 | + char *zEnd; /* One character beyond the end of the manifest */ | |
| 241 | + int atEol; /* True if z points to the start of a new line */ | |
| 242 | +}; | |
| 243 | + | |
| 244 | +/* | |
| 245 | +** Return a pointer to the next token. The token is zero-terminated. | |
| 246 | +** Return NULL if there are no more tokens on the current line. | |
| 247 | +*/ | |
| 248 | +static char *next_token(ManifestText *p, int *pLen){ | |
| 249 | + char *z; | |
| 250 | + char *zStart; | |
| 251 | + int c; | |
| 252 | + if( p->atEol ) return 0; | |
| 253 | + zStart = z = p->z; | |
| 254 | + while( (c=(*z))!=' ' && c!='\n' ){ z++; } | |
| 255 | + *z = 0; | |
| 256 | + p->z = &z[1]; | |
| 257 | + p->atEol = c=='\n'; | |
| 258 | + if( pLen ) *pLen = z - zStart; | |
| 259 | + return zStart; | |
| 260 | +} | |
| 261 | + | |
| 262 | +/* | |
| 263 | +** Return the card-type for the next card. Or, return 0 if there are no | |
| 264 | +** more cards or if we are not at the end of the current card. | |
| 265 | +*/ | |
| 266 | +static char next_card(ManifestText *p){ | |
| 267 | + char c; | |
| 268 | + if( !p->atEol || p->z>=p->zEnd ) return 0; | |
| 269 | + c = p->z[0]; | |
| 270 | + if( p->z[1]==' ' ){ | |
| 271 | + p->z += 2; | |
| 272 | + p->atEol = 0; | |
| 273 | + }else if( p->z[1]=='\n' ){ | |
| 274 | + p->z += 2; | |
| 275 | + p->atEol = 1; | |
| 276 | + }else{ | |
| 277 | + c = 0; | |
| 278 | + } | |
| 279 | + return c; | |
| 100 | 280 | } |
| 101 | 281 | |
| 102 | 282 | /* |
| 103 | 283 | ** Parse a blob into a Manifest object. The Manifest object |
| 104 | 284 | ** takes over the input blob and will free it when the |
| @@ -124,48 +304,66 @@ | ||
| 124 | 304 | ** Each card is divided into tokens by a single space character. |
| 125 | 305 | ** The first token is a single upper-case letter which is the card type. |
| 126 | 306 | ** The card type determines the other parameters to the card. |
| 127 | 307 | ** Cards must occur in lexicographical order. |
| 128 | 308 | */ |
| 129 | -int manifest_parse(Manifest *p, Blob *pContent){ | |
| 130 | - int seenHeader = 0; | |
| 309 | +static Manifest *manifest_parse(Blob *pContent, int rid){ | |
| 310 | + Manifest *p; | |
| 131 | 311 | int seenZ = 0; |
| 132 | 312 | int i, lineNo=0; |
| 133 | - Blob line, token, a1, a2, a3, a4; | |
| 313 | + ManifestText x; | |
| 134 | 314 | char cPrevType = 0; |
| 315 | + char cType; | |
| 316 | + char *z; | |
| 317 | + int n; | |
| 318 | + char *zUuid; | |
| 319 | + int sz = 0; | |
| 320 | + | |
| 321 | + /* Every control artifact ends with a '\n' character. Exit early | |
| 322 | + ** if that is not the case for this artifact. | |
| 323 | + */ | |
| 324 | + z = blob_buffer(pContent); | |
| 325 | + n = blob_size(pContent); | |
| 326 | + if( n<=0 || z[n-1]!='\n' ){ | |
| 327 | + blob_reset(pContent); | |
| 328 | + return 0; | |
| 329 | + } | |
| 330 | + | |
| 331 | + /* Strip off the PGP signature if there is one. Then verify the | |
| 332 | + ** Z-card. | |
| 333 | + */ | |
| 334 | + remove_pgp_signature(&z, &n); | |
| 335 | + if( verify_z_card(z, n)==0 ){ | |
| 336 | + blob_reset(pContent); | |
| 337 | + return 0; | |
| 338 | + } | |
| 339 | + | |
| 340 | + /* Verify that the first few characters of the artifact look like | |
| 341 | + ** a control artifact. | |
| 342 | + */ | |
| 343 | + if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ | |
| 344 | + blob_reset(pContent); | |
| 345 | + return 0; | |
| 346 | + } | |
| 135 | 347 | |
| 348 | + /* Allocate a Manifest object to hold the parsed control artifact. | |
| 349 | + */ | |
| 350 | + p = fossil_malloc( sizeof(*p) ); | |
| 136 | 351 | memset(p, 0, sizeof(*p)); |
| 137 | 352 | memcpy(&p->content, pContent, sizeof(p->content)); |
| 353 | + p->rid = rid; | |
| 138 | 354 | blob_zero(pContent); |
| 139 | 355 | pContent = &p->content; |
| 140 | 356 | |
| 141 | - blob_zero(&a1); | |
| 142 | - blob_zero(&a2); | |
| 143 | - blob_zero(&a3); | |
| 144 | - md5sum_init(); | |
| 145 | - while( blob_line(pContent, &line) ){ | |
| 146 | - char *z = blob_buffer(&line); | |
| 357 | + /* Begin parsing, card by card. | |
| 358 | + */ | |
| 359 | + x.z = z; | |
| 360 | + x.zEnd = &z[n]; | |
| 361 | + x.atEol = 1; | |
| 362 | + while( (cType = next_card(&x))!=0 && cType>=cPrevType ){ | |
| 147 | 363 | lineNo++; |
| 148 | - if( z[0]=='-' ){ | |
| 149 | - if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){ | |
| 150 | - goto manifest_syntax_error; | |
| 151 | - } | |
| 152 | - if( seenHeader ){ | |
| 153 | - break; | |
| 154 | - } | |
| 155 | - while( blob_line(pContent, &line)>2 ){} | |
| 156 | - if( blob_line(pContent, &line)==0 ) break; | |
| 157 | - z = blob_buffer(&line); | |
| 158 | - } | |
| 159 | - if( z[0]<cPrevType ){ | |
| 160 | - /* Lines of a manifest must occur in lexicographical order */ | |
| 161 | - goto manifest_syntax_error; | |
| 162 | - } | |
| 163 | - cPrevType = z[0]; | |
| 164 | - seenHeader = 1; | |
| 165 | - if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error; | |
| 166 | - switch( z[0] ){ | |
| 364 | + switch( cType ){ | |
| 167 | 365 | /* |
| 168 | 366 | ** A <filename> <target> ?<source>? |
| 169 | 367 | ** |
| 170 | 368 | ** Identifies an attachment to either a wiki page or a ticket. |
| 171 | 369 | ** <source> is the artifact that is the attachment. <source> |
| @@ -172,50 +370,60 @@ | ||
| 172 | 370 | ** is omitted to delete an attachment. <target> is the name of |
| 173 | 371 | ** a wiki page or ticket to which that attachment is connected. |
| 174 | 372 | */ |
| 175 | 373 | case 'A': { |
| 176 | 374 | char *zName, *zTarget, *zSrc; |
| 177 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 178 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 179 | - if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; | |
| 375 | + int nTarget = 0, nSrc = 0; | |
| 376 | + zName = next_token(&x, 0); | |
| 377 | + zTarget = next_token(&x, &nTarget); | |
| 378 | + zSrc = next_token(&x, &nSrc); | |
| 379 | + if( zName==0 || zTarget==0 ) goto manifest_syntax_error; | |
| 180 | 380 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 181 | - zName = blob_terminate(&a1); | |
| 182 | - zTarget = blob_terminate(&a2); | |
| 183 | - blob_token(&line, &a3); | |
| 184 | - zSrc = blob_terminate(&a3); | |
| 185 | 381 | defossilize(zName); |
| 186 | 382 | if( !file_is_simple_pathname(zName) ){ |
| 187 | 383 | goto manifest_syntax_error; |
| 188 | 384 | } |
| 189 | 385 | defossilize(zTarget); |
| 190 | - if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) | |
| 386 | + if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) | |
| 191 | 387 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 192 | 388 | goto manifest_syntax_error; |
| 193 | 389 | } |
| 194 | - if( blob_size(&a3)>0 | |
| 195 | - && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ | |
| 390 | + if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ | |
| 196 | 391 | goto manifest_syntax_error; |
| 197 | 392 | } |
| 198 | 393 | p->zAttachName = (char*)file_tail(zName); |
| 199 | 394 | p->zAttachSrc = zSrc; |
| 200 | 395 | p->zAttachTarget = zTarget; |
| 201 | 396 | break; |
| 202 | 397 | } |
| 398 | + | |
| 399 | + /* | |
| 400 | + ** B <uuid> | |
| 401 | + ** | |
| 402 | + ** A B-line gives the UUID for the baselinen of a delta-manifest. | |
| 403 | + */ | |
| 404 | + case 'B': { | |
| 405 | + if( p->zBaseline ) goto manifest_syntax_error; | |
| 406 | + p->zBaseline = next_token(&x, &sz); | |
| 407 | + if( p->zBaseline==0 ) goto manifest_syntax_error; | |
| 408 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 409 | + if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error; | |
| 410 | + break; | |
| 411 | + } | |
| 412 | + | |
| 203 | 413 | |
| 204 | 414 | /* |
| 205 | 415 | ** C <comment> |
| 206 | 416 | ** |
| 207 | 417 | ** Comment text is fossil-encoded. There may be no more than |
| 208 | 418 | ** one C line. C lines are required for manifests and are |
| 209 | 419 | ** disallowed on all other control files. |
| 210 | 420 | */ |
| 211 | 421 | case 'C': { |
| 212 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 213 | 422 | if( p->zComment!=0 ) goto manifest_syntax_error; |
| 214 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 215 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 216 | - p->zComment = blob_terminate(&a1); | |
| 423 | + p->zComment = next_token(&x, 0); | |
| 424 | + if( p->zComment==0 ) goto manifest_syntax_error; | |
| 217 | 425 | defossilize(p->zComment); |
| 218 | 426 | break; |
| 219 | 427 | } |
| 220 | 428 | |
| 221 | 429 | /* |
| @@ -224,17 +432,13 @@ | ||
| 224 | 432 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 225 | 433 | ** There can be no more than 1 D line. D lines are required |
| 226 | 434 | ** for all control files except for clusters. |
| 227 | 435 | */ |
| 228 | 436 | case 'D': { |
| 229 | - char *zDate; | |
| 230 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 231 | - if( p->rDate!=0.0 ) goto manifest_syntax_error; | |
| 232 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 233 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 234 | - zDate = blob_terminate(&a1); | |
| 235 | - p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); | |
| 437 | + if( p->rDate>0.0 ) goto manifest_syntax_error; | |
| 438 | + p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); | |
| 439 | + if( p->rDate<=0.0 ) goto manifest_syntax_error; | |
| 236 | 440 | break; |
| 237 | 441 | } |
| 238 | 442 | |
| 239 | 443 | /* |
| 240 | 444 | ** E <timestamp> <uuid> |
| @@ -244,68 +448,57 @@ | ||
| 244 | 448 | ** The event timestamp is distinct from the D timestamp. The D |
| 245 | 449 | ** timestamp is when the artifact was created whereas the E timestamp |
| 246 | 450 | ** is when the specific event is said to occur. |
| 247 | 451 | */ |
| 248 | 452 | case 'E': { |
| 249 | - char *zEDate; | |
| 250 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 251 | - if( p->rEventDate!=0.0 ) goto manifest_syntax_error; | |
| 252 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 253 | - if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; | |
| 254 | - if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; | |
| 255 | - zEDate = blob_terminate(&a1); | |
| 256 | - p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate); | |
| 453 | + if( p->rEventDate>0.0 ) goto manifest_syntax_error; | |
| 454 | + p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); | |
| 257 | 455 | if( p->rEventDate<=0.0 ) goto manifest_syntax_error; |
| 258 | - if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 259 | - p->zEventId = blob_terminate(&a2); | |
| 456 | + p->zEventId = next_token(&x, &sz); | |
| 457 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 260 | 458 | if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; |
| 261 | 459 | break; |
| 262 | 460 | } |
| 263 | 461 | |
| 264 | 462 | /* |
| 265 | - ** F <filename> <uuid> ?<permissions>? ?<old-name>? | |
| 463 | + ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? | |
| 266 | 464 | ** |
| 267 | 465 | ** Identifies a file in a manifest. Multiple F lines are |
| 268 | 466 | ** allowed in a manifest. F lines are not allowed in any |
| 269 | 467 | ** other control file. The filename and old-name are fossil-encoded. |
| 270 | 468 | */ |
| 271 | 469 | case 'F': { |
| 272 | - char *zName, *zUuid, *zPerm, *zPriorName; | |
| 273 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 274 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 275 | - if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; | |
| 276 | - zName = blob_terminate(&a1); | |
| 277 | - zUuid = blob_terminate(&a2); | |
| 278 | - blob_token(&line, &a3); | |
| 279 | - zPerm = blob_terminate(&a3); | |
| 280 | - if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 281 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 470 | + char *zName, *zPerm, *zPriorName; | |
| 471 | + zName = next_token(&x,0); | |
| 472 | + if( zName==0 ) goto manifest_syntax_error; | |
| 282 | 473 | defossilize(zName); |
| 283 | 474 | if( !file_is_simple_pathname(zName) ){ |
| 284 | 475 | goto manifest_syntax_error; |
| 285 | 476 | } |
| 286 | - blob_token(&line, &a4); | |
| 287 | - zPriorName = blob_terminate(&a4); | |
| 288 | - if( zPriorName[0] ){ | |
| 477 | + zUuid = next_token(&x, &sz); | |
| 478 | + if( p->zBaseline==0 || zUuid!=0 ){ | |
| 479 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 480 | + if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 481 | + } | |
| 482 | + zPerm = next_token(&x,0); | |
| 483 | + zPriorName = next_token(&x,0); | |
| 484 | + if( zPriorName ){ | |
| 289 | 485 | defossilize(zPriorName); |
| 290 | 486 | if( !file_is_simple_pathname(zPriorName) ){ |
| 291 | 487 | goto manifest_syntax_error; |
| 292 | 488 | } |
| 293 | - }else{ | |
| 294 | - zPriorName = 0; | |
| 295 | 489 | } |
| 296 | 490 | if( p->nFile>=p->nFileAlloc ){ |
| 297 | 491 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 298 | - p->aFile = realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) ); | |
| 299 | - if( p->aFile==0 ) fossil_panic("out of memory"); | |
| 492 | + p->aFile = fossil_realloc(p->aFile, | |
| 493 | + p->nFileAlloc*sizeof(p->aFile[0]) ); | |
| 300 | 494 | } |
| 301 | 495 | i = p->nFile++; |
| 302 | 496 | p->aFile[i].zName = zName; |
| 303 | 497 | p->aFile[i].zUuid = zUuid; |
| 304 | 498 | p->aFile[i].zPerm = zPerm; |
| 305 | 499 | p->aFile[i].zPrior = zPriorName; |
| 306 | - p->aFile[i].iRename = -1; | |
| 307 | 500 | if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 308 | 501 | goto manifest_syntax_error; |
| 309 | 502 | } |
| 310 | 503 | break; |
| 311 | 504 | } |
| @@ -318,22 +511,19 @@ | ||
| 318 | 511 | ** value. If <value> is omitted then it is understood to be an |
| 319 | 512 | ** empty string. |
| 320 | 513 | */ |
| 321 | 514 | case 'J': { |
| 322 | 515 | char *zName, *zValue; |
| 323 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 324 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 325 | - blob_token(&line, &a2); | |
| 326 | - if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; | |
| 327 | - zName = blob_terminate(&a1); | |
| 328 | - zValue = blob_terminate(&a2); | |
| 516 | + zName = next_token(&x,0); | |
| 517 | + zValue = next_token(&x,0); | |
| 518 | + if( zName==0 ) goto manifest_syntax_error; | |
| 519 | + if( zValue==0 ) zValue = ""; | |
| 329 | 520 | defossilize(zValue); |
| 330 | 521 | if( p->nField>=p->nFieldAlloc ){ |
| 331 | 522 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 332 | - p->aField = realloc(p->aField, | |
| 523 | + p->aField = fossil_realloc(p->aField, | |
| 333 | 524 | p->nFieldAlloc*sizeof(p->aField[0]) ); |
| 334 | - if( p->aField==0 ) fossil_panic("out of memory"); | |
| 335 | 525 | } |
| 336 | 526 | i = p->nField++; |
| 337 | 527 | p->aField[i].zName = zName; |
| 338 | 528 | p->aField[i].zValue = zValue; |
| 339 | 529 | if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| @@ -348,18 +538,14 @@ | ||
| 348 | 538 | ** |
| 349 | 539 | ** A K-line gives the UUID for the ticket which this control file |
| 350 | 540 | ** is amending. |
| 351 | 541 | */ |
| 352 | 542 | case 'K': { |
| 353 | - char *zUuid; | |
| 354 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 355 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 356 | - zUuid = blob_terminate(&a1); | |
| 357 | - if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 358 | - if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 359 | 543 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 360 | - p->zTicketUuid = zUuid; | |
| 544 | + p->zTicketUuid = next_token(&x, &sz); | |
| 545 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 546 | + if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error; | |
| 361 | 547 | break; |
| 362 | 548 | } |
| 363 | 549 | |
| 364 | 550 | /* |
| 365 | 551 | ** L <wikititle> |
| @@ -366,15 +552,13 @@ | ||
| 366 | 552 | ** |
| 367 | 553 | ** The wiki page title is fossil-encoded. There may be no more than |
| 368 | 554 | ** one L line. |
| 369 | 555 | */ |
| 370 | 556 | case 'L': { |
| 371 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 372 | 557 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 373 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 374 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 375 | - p->zWikiTitle = blob_terminate(&a1); | |
| 558 | + p->zWikiTitle = next_token(&x,0); | |
| 559 | + if( p->zWikiTitle==0 ) goto manifest_syntax_error; | |
| 376 | 560 | defossilize(p->zWikiTitle); |
| 377 | 561 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 378 | 562 | goto manifest_syntax_error; |
| 379 | 563 | } |
| 380 | 564 | break; |
| @@ -385,21 +569,18 @@ | ||
| 385 | 569 | ** |
| 386 | 570 | ** An M-line identifies another artifact by its UUID. M-lines |
| 387 | 571 | ** occur in clusters only. |
| 388 | 572 | */ |
| 389 | 573 | case 'M': { |
| 390 | - char *zUuid; | |
| 391 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 392 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 393 | - zUuid = blob_terminate(&a1); | |
| 394 | - if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 574 | + zUuid = next_token(&x, &sz); | |
| 575 | + if( zUuid==0 ) goto manifest_syntax_error; | |
| 576 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 395 | 577 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 396 | 578 | if( p->nCChild>=p->nCChildAlloc ){ |
| 397 | 579 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 398 | - p->azCChild = | |
| 399 | - realloc(p->azCChild, p->nCChildAlloc*sizeof(p->azCChild[0]) ); | |
| 400 | - if( p->azCChild==0 ) fossil_panic("out of memory"); | |
| 580 | + p->azCChild = fossil_realloc(p->azCChild | |
| 581 | + , p->nCChildAlloc*sizeof(p->azCChild[0]) ); | |
| 401 | 582 | } |
| 402 | 583 | i = p->nCChild++; |
| 403 | 584 | p->azCChild[i] = zUuid; |
| 404 | 585 | if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 405 | 586 | goto manifest_syntax_error; |
| @@ -413,20 +594,17 @@ | ||
| 413 | 594 | ** Specify one or more other artifacts where are the parents of |
| 414 | 595 | ** this artifact. The first parent is the primary parent. All |
| 415 | 596 | ** others are parents by merge. |
| 416 | 597 | */ |
| 417 | 598 | case 'P': { |
| 418 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 419 | - while( blob_token(&line, &a1) ){ | |
| 420 | - char *zUuid; | |
| 421 | - if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; | |
| 422 | - zUuid = blob_terminate(&a1); | |
| 599 | + while( (zUuid = next_token(&x, &sz))!=0 ){ | |
| 600 | + if( sz!=UUID_SIZE ) goto manifest_syntax_error; | |
| 423 | 601 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 424 | 602 | if( p->nParent>=p->nParentAlloc ){ |
| 425 | 603 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 426 | - p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*)); | |
| 427 | - if( p->azParent==0 ) fossil_panic("out of memory"); | |
| 604 | + p->azParent = fossil_realloc(p->azParent, | |
| 605 | + p->nParentAlloc*sizeof(char*)); | |
| 428 | 606 | } |
| 429 | 607 | i = p->nParent++; |
| 430 | 608 | p->azParent[i] = zUuid; |
| 431 | 609 | } |
| 432 | 610 | break; |
| @@ -437,16 +615,13 @@ | ||
| 437 | 615 | ** |
| 438 | 616 | ** Specify the MD5 checksum over the name and content of all files |
| 439 | 617 | ** in the manifest. |
| 440 | 618 | */ |
| 441 | 619 | case 'R': { |
| 442 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 443 | 620 | if( p->zRepoCksum!=0 ) goto manifest_syntax_error; |
| 444 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 445 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 446 | - if( blob_size(&a1)!=32 ) goto manifest_syntax_error; | |
| 447 | - p->zRepoCksum = blob_terminate(&a1); | |
| 621 | + p->zRepoCksum = next_token(&x, &sz); | |
| 622 | + if( sz!=32 ) goto manifest_syntax_error; | |
| 448 | 623 | if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; |
| 449 | 624 | break; |
| 450 | 625 | } |
| 451 | 626 | |
| 452 | 627 | /* |
| @@ -463,29 +638,20 @@ | ||
| 463 | 638 | ** the tag is really a property with the given value. |
| 464 | 639 | ** |
| 465 | 640 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 466 | 641 | */ |
| 467 | 642 | case 'T': { |
| 468 | - char *zName, *zUuid, *zValue; | |
| 469 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 470 | - if( blob_token(&line, &a1)==0 ){ | |
| 471 | - goto manifest_syntax_error; | |
| 472 | - } | |
| 473 | - if( blob_token(&line, &a2)==0 ){ | |
| 474 | - goto manifest_syntax_error; | |
| 475 | - } | |
| 476 | - zName = blob_terminate(&a1); | |
| 477 | - zUuid = blob_terminate(&a2); | |
| 478 | - if( blob_token(&line, &a3)==0 ){ | |
| 479 | - zValue = 0; | |
| 480 | - }else{ | |
| 481 | - zValue = blob_terminate(&a3); | |
| 482 | - defossilize(zValue); | |
| 483 | - } | |
| 484 | - if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ | |
| 643 | + char *zName, *zValue; | |
| 644 | + zName = next_token(&x, 0); | |
| 645 | + if( zName==0 ) goto manifest_syntax_error; | |
| 646 | + zUuid = next_token(&x, &sz); | |
| 647 | + if( zUuid==0 ) goto manifest_syntax_error; | |
| 648 | + zValue = next_token(&x, 0); | |
| 649 | + if( zValue ) defossilize(zValue); | |
| 650 | + if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ | |
| 485 | 651 | /* A valid uuid */ |
| 486 | - }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){ | |
| 652 | + }else if( sz==1 && zUuid[0]=='*' ){ | |
| 487 | 653 | zUuid = 0; |
| 488 | 654 | }else{ |
| 489 | 655 | goto manifest_syntax_error; |
| 490 | 656 | } |
| 491 | 657 | defossilize(zName); |
| @@ -496,12 +662,11 @@ | ||
| 496 | 662 | /* Do not allow tags whose names look like UUIDs */ |
| 497 | 663 | goto manifest_syntax_error; |
| 498 | 664 | } |
| 499 | 665 | if( p->nTag>=p->nTagAlloc ){ |
| 500 | 666 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 501 | - p->aTag = realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); | |
| 502 | - if( p->aTag==0 ) fossil_panic("out of memory"); | |
| 667 | + p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); | |
| 503 | 668 | } |
| 504 | 669 | i = p->nTag++; |
| 505 | 670 | p->aTag[i].zName = zName; |
| 506 | 671 | p->aTag[i].zUuid = zUuid; |
| 507 | 672 | p->aTag[i].zValue = zValue; |
| @@ -517,19 +682,17 @@ | ||
| 517 | 682 | ** Identify the user who created this control file by their |
| 518 | 683 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 519 | 684 | ** If the user name is omitted, take that to be "anonymous". |
| 520 | 685 | */ |
| 521 | 686 | case 'U': { |
| 522 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 523 | 687 | if( p->zUser!=0 ) goto manifest_syntax_error; |
| 524 | - if( blob_token(&line, &a1)==0 ){ | |
| 688 | + p->zUser = next_token(&x, 0); | |
| 689 | + if( p->zUser==0 ){ | |
| 525 | 690 | p->zUser = "anonymous"; |
| 526 | 691 | }else{ |
| 527 | - p->zUser = blob_terminate(&a1); | |
| 528 | 692 | defossilize(p->zUser); |
| 529 | 693 | } |
| 530 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 531 | 694 | break; |
| 532 | 695 | } |
| 533 | 696 | |
| 534 | 697 | /* |
| 535 | 698 | ** W <size> |
| @@ -537,26 +700,28 @@ | ||
| 537 | 700 | ** The next <size> bytes of the file contain the text of the wiki |
| 538 | 701 | ** page. There is always an extra \n before the start of the next |
| 539 | 702 | ** record. |
| 540 | 703 | */ |
| 541 | 704 | case 'W': { |
| 542 | - int size; | |
| 705 | + char *zSize; | |
| 706 | + int size, c; | |
| 543 | 707 | Blob wiki; |
| 544 | - md5sum_step_text(blob_buffer(&line), blob_size(&line)); | |
| 545 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 546 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 547 | - if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error; | |
| 708 | + zSize = next_token(&x, 0); | |
| 709 | + if( zSize==0 ) goto manifest_syntax_error; | |
| 710 | + if( x.atEol==0 ) goto manifest_syntax_error; | |
| 711 | + for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ | |
| 712 | + size = size*10 + c - '0'; | |
| 713 | + } | |
| 548 | 714 | if( size<0 ) goto manifest_syntax_error; |
| 549 | 715 | if( p->zWiki!=0 ) goto manifest_syntax_error; |
| 550 | 716 | blob_zero(&wiki); |
| 551 | - if( blob_extract(pContent, size+1, &wiki)!=size+1 ){ | |
| 552 | - goto manifest_syntax_error; | |
| 553 | - } | |
| 554 | - p->zWiki = blob_buffer(&wiki); | |
| 555 | - md5sum_step_text(p->zWiki, size+1); | |
| 556 | - if( p->zWiki[size]!='\n' ) goto manifest_syntax_error; | |
| 557 | - p->zWiki[size] = 0; | |
| 717 | + if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error; | |
| 718 | + p->zWiki = x.z; | |
| 719 | + x.z += size; | |
| 720 | + if( x.z[0]!='\n' ) goto manifest_syntax_error; | |
| 721 | + x.z[0] = 0; | |
| 722 | + x.z++; | |
| 558 | 723 | break; |
| 559 | 724 | } |
| 560 | 725 | |
| 561 | 726 | |
| 562 | 727 | /* |
| @@ -569,31 +734,24 @@ | ||
| 569 | 734 | ** This card is required for all control file types except for |
| 570 | 735 | ** Manifest. It is not required for manifest only for historical |
| 571 | 736 | ** compatibility reasons. |
| 572 | 737 | */ |
| 573 | 738 | case 'Z': { |
| 574 | - int rc; | |
| 575 | - Blob hash; | |
| 576 | - if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; | |
| 577 | - if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; | |
| 578 | - if( blob_size(&a1)!=32 ) goto manifest_syntax_error; | |
| 579 | - if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error; | |
| 580 | - md5sum_finish(&hash); | |
| 581 | - rc = blob_compare(&hash, &a1); | |
| 582 | - blob_reset(&hash); | |
| 583 | - if( rc!=0 ) goto manifest_syntax_error; | |
| 739 | + zUuid = next_token(&x, &sz); | |
| 740 | + if( sz!=32 ) goto manifest_syntax_error; | |
| 741 | + if( !validate16(zUuid, 32) ) goto manifest_syntax_error; | |
| 584 | 742 | seenZ = 1; |
| 585 | 743 | break; |
| 586 | 744 | } |
| 587 | 745 | default: { |
| 588 | 746 | goto manifest_syntax_error; |
| 589 | 747 | } |
| 590 | 748 | } |
| 591 | 749 | } |
| 592 | - if( !seenHeader ) goto manifest_syntax_error; | |
| 750 | + if( x.z<x.zEnd ) goto manifest_syntax_error; | |
| 593 | 751 | |
| 594 | - if( p->nFile>0 || p->zRepoCksum!=0 ){ | |
| 752 | + if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ | |
| 595 | 753 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 596 | 754 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 597 | 755 | if( p->nField>0 ) goto manifest_syntax_error; |
| 598 | 756 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 599 | 757 | if( p->zWiki ) goto manifest_syntax_error; |
| @@ -674,36 +832,194 @@ | ||
| 674 | 832 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 675 | 833 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 676 | 834 | p->type = CFTYPE_MANIFEST; |
| 677 | 835 | } |
| 678 | 836 | md5sum_init(); |
| 679 | - return 1; | |
| 837 | + return p; | |
| 680 | 838 | |
| 681 | 839 | manifest_syntax_error: |
| 682 | 840 | /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ |
| 683 | 841 | md5sum_init(); |
| 684 | - manifest_clear(p); | |
| 842 | + manifest_destroy(p); | |
| 685 | 843 | return 0; |
| 686 | 844 | } |
| 845 | + | |
| 846 | +/* | |
| 847 | +** Get a manifest given the rid for the control artifact. Return | |
| 848 | +** a pointer to the manifest on success or NULL if there is a failure. | |
| 849 | +*/ | |
| 850 | +Manifest *manifest_get(int rid, int cfType){ | |
| 851 | + Blob content; | |
| 852 | + Manifest *p; | |
| 853 | + p = manifest_cache_find(rid); | |
| 854 | + if( p ){ | |
| 855 | + if( cfType!=CFTYPE_ANY && cfType!=p->type ){ | |
| 856 | + manifest_cache_insert(p); | |
| 857 | + p = 0; | |
| 858 | + } | |
| 859 | + return p; | |
| 860 | + } | |
| 861 | + content_get(rid, &content); | |
| 862 | + p = manifest_parse(&content, rid); | |
| 863 | + if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ | |
| 864 | + manifest_destroy(p); | |
| 865 | + p = 0; | |
| 866 | + } | |
| 867 | + return p; | |
| 868 | +} | |
| 869 | + | |
| 870 | +/* | |
| 871 | +** Given a checkin name, load and parse the manifest for that checkin. | |
| 872 | +** Throw a fatal error if anything goes wrong. | |
| 873 | +*/ | |
| 874 | +Manifest *manifest_get_by_name(const char *zName, int *pRid){ | |
| 875 | + int rid; | |
| 876 | + Manifest *p; | |
| 877 | + | |
| 878 | + rid = name_to_rid(zName); | |
| 879 | + if( !is_a_version(rid) ){ | |
| 880 | + fossil_fatal("no such checkin: %s", zName); | |
| 881 | + } | |
| 882 | + if( pRid ) *pRid = rid; | |
| 883 | + p = manifest_get(rid, CFTYPE_MANIFEST); | |
| 884 | + if( p==0 ){ | |
| 885 | + fossil_fatal("cannot parse manifest for checkin: %s", zName); | |
| 886 | + } | |
| 887 | + return p; | |
| 888 | +} | |
| 687 | 889 | |
| 688 | 890 | /* |
| 689 | 891 | ** COMMAND: test-parse-manifest |
| 690 | 892 | ** |
| 691 | -** Usage: %fossil test-parse-manifest FILENAME | |
| 893 | +** Usage: %fossil test-parse-manifest FILENAME ?N? | |
| 692 | 894 | ** |
| 693 | 895 | ** Parse the manifest and discarded. Use for testing only. |
| 694 | 896 | */ |
| 695 | 897 | void manifest_test_parse_cmd(void){ |
| 696 | - Manifest m; | |
| 898 | + Manifest *p; | |
| 697 | 899 | Blob b; |
| 698 | - if( g.argc!=3 ){ | |
| 900 | + int i; | |
| 901 | + int n = 1; | |
| 902 | + sqlite3_open(":memory:", &g.db); | |
| 903 | + if( g.argc!=3 && g.argc!=4 ){ | |
| 699 | 904 | usage("FILENAME"); |
| 700 | 905 | } |
| 701 | - db_must_be_within_tree(); | |
| 702 | 906 | blob_read_from_file(&b, g.argv[2]); |
| 703 | - manifest_parse(&m, &b); | |
| 704 | - manifest_clear(&m); | |
| 907 | + if( g.argc>3 ) n = atoi(g.argv[3]); | |
| 908 | + for(i=0; i<n; i++){ | |
| 909 | + Blob b2; | |
| 910 | + blob_copy(&b2, &b); | |
| 911 | + p = manifest_parse(&b2, 0); | |
| 912 | + manifest_destroy(p); | |
| 913 | + } | |
| 914 | +} | |
| 915 | + | |
| 916 | +/* | |
| 917 | +** Fetch the baseline associated with the delta-manifest p. | |
| 918 | +** Return 0 on success. If unable to parse the baseline, | |
| 919 | +** throw an error. If the baseline is a manifest, throw an | |
| 920 | +** error if throwError is true, or record that p is an orphan | |
| 921 | +** and return 1 throwError is false. | |
| 922 | +*/ | |
| 923 | +static int fetch_baseline(Manifest *p, int throwError){ | |
| 924 | + if( p->zBaseline!=0 && p->pBaseline==0 ){ | |
| 925 | + int rid = uuid_to_rid(p->zBaseline, 0); | |
| 926 | + if( rid==0 && !throwError ){ | |
| 927 | + rid = content_new(p->zBaseline); | |
| 928 | + db_multi_exec( | |
| 929 | + "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", | |
| 930 | + rid, p->rid | |
| 931 | + ); | |
| 932 | + return 1; | |
| 933 | + } | |
| 934 | + p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST); | |
| 935 | + if( p->pBaseline==0 ){ | |
| 936 | + if( !throwError && db_exists("SELECT 1 FROM phantom WHERE rid=%d",rid) ){ | |
| 937 | + db_multi_exec( | |
| 938 | + "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", | |
| 939 | + rid, p->rid | |
| 940 | + ); | |
| 941 | + return 1; | |
| 942 | + } | |
| 943 | + fossil_fatal("cannot access baseline manifest %S", p->zBaseline); | |
| 944 | + } | |
| 945 | + } | |
| 946 | + return 0; | |
| 947 | +} | |
| 948 | + | |
| 949 | +/* | |
| 950 | +** Rewind a manifest-file iterator back to the beginning of the manifest. | |
| 951 | +*/ | |
| 952 | +void manifest_file_rewind(Manifest *p){ | |
| 953 | + p->iFile = 0; | |
| 954 | + fetch_baseline(p, 1); | |
| 955 | + if( p->pBaseline ){ | |
| 956 | + p->pBaseline->iFile = 0; | |
| 957 | + } | |
| 958 | +} | |
| 959 | + | |
| 960 | +/* | |
| 961 | +** Advance to the next manifest-file. | |
| 962 | +** | |
| 963 | +** Return NULL for end-of-records or if there is an error. If an error | |
| 964 | +** occurs and pErr!=0 then store 1 in *pErr. | |
| 965 | +*/ | |
| 966 | +ManifestFile *manifest_file_next( | |
| 967 | + Manifest *p, | |
| 968 | + int *pErr | |
| 969 | +){ | |
| 970 | + ManifestFile *pOut = 0; | |
| 971 | + if( pErr ) *pErr = 0; | |
| 972 | + if( p->pBaseline==0 ){ | |
| 973 | + /* Manifest p is a baseline-manifest. Just scan down the list | |
| 974 | + ** of files. */ | |
| 975 | + if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; | |
| 976 | + }else{ | |
| 977 | + /* Manifest p is a delta-manifest. Scan the baseline but amend the | |
| 978 | + ** file list in the baseline with changes described by p. | |
| 979 | + */ | |
| 980 | + Manifest *pB = p->pBaseline; | |
| 981 | + int cmp; | |
| 982 | + while(1){ | |
| 983 | + if( pB->iFile>=pB->nFile ){ | |
| 984 | + /* We have used all entries out of the baseline. Return the next | |
| 985 | + ** entry from the delta. */ | |
| 986 | + if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; | |
| 987 | + break; | |
| 988 | + }else if( p->iFile>=p->nFile ){ | |
| 989 | + /* We have used all entries from the delta. Return the next | |
| 990 | + ** entry from the baseline. */ | |
| 991 | + if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++]; | |
| 992 | + break; | |
| 993 | + }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName, | |
| 994 | + p->aFile[p->iFile].zName)) < 0 ){ | |
| 995 | + /* The next baseline entry comes before the next delta entry. | |
| 996 | + ** So return the baseline entry. */ | |
| 997 | + pOut = &pB->aFile[pB->iFile++]; | |
| 998 | + break; | |
| 999 | + }else if( cmp>0 ){ | |
| 1000 | + /* The next delta entry comes before the next baseline | |
| 1001 | + ** entry so return the delta entry */ | |
| 1002 | + pOut = &p->aFile[p->iFile++]; | |
| 1003 | + break; | |
| 1004 | + }else if( p->aFile[p->iFile].zUuid ){ | |
| 1005 | + /* The next delta entry is a replacement for the next baseline | |
| 1006 | + ** entry. Skip the baseline entry and return the delta entry */ | |
| 1007 | + pB->iFile++; | |
| 1008 | + pOut = &p->aFile[p->iFile++]; | |
| 1009 | + break; | |
| 1010 | + }else{ | |
| 1011 | + /* The next delta entry is a delete of the next baseline | |
| 1012 | + ** entry. Skip them both. Repeat the loop to find the next | |
| 1013 | + ** non-delete entry. */ | |
| 1014 | + pB->iFile++; | |
| 1015 | + p->iFile++; | |
| 1016 | + continue; | |
| 1017 | + } | |
| 1018 | + } | |
| 1019 | + } | |
| 1020 | + return pOut; | |
| 705 | 1021 | } |
| 706 | 1022 | |
| 707 | 1023 | /* |
| 708 | 1024 | ** Translate a filename into a filename-id (fnid). Create a new fnid |
| 709 | 1025 | ** if no previously exists. |
| @@ -731,14 +1047,14 @@ | ||
| 731 | 1047 | ** Add a single entry to the mlink table. Also add the filename to |
| 732 | 1048 | ** the filename table if it is not there already. |
| 733 | 1049 | */ |
| 734 | 1050 | static void add_one_mlink( |
| 735 | 1051 | int mid, /* The record ID of the manifest */ |
| 736 | - const char *zFromUuid, /* UUID for the mlink.pid field */ | |
| 737 | - const char *zToUuid, /* UUID for the mlink.fid field */ | |
| 1052 | + const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ | |
| 1053 | + const char *zToUuid, /* UUID for the mlink.fid. "" to delele */ | |
| 738 | 1054 | const char *zFilename, /* Filename */ |
| 739 | - const char *zPrior /* Previous filename. NULL if unchanged */ | |
| 1055 | + const char *zPrior /* Previous filename. NULL if unchanged */ | |
| 740 | 1056 | ){ |
| 741 | 1057 | int fnid, pfnid, pid, fid; |
| 742 | 1058 | static Stmt s1; |
| 743 | 1059 | |
| 744 | 1060 | fnid = filename_to_fnid(zFilename); |
| @@ -745,16 +1061,16 @@ | ||
| 745 | 1061 | if( zPrior==0 ){ |
| 746 | 1062 | pfnid = 0; |
| 747 | 1063 | }else{ |
| 748 | 1064 | pfnid = filename_to_fnid(zPrior); |
| 749 | 1065 | } |
| 750 | - if( zFromUuid==0 ){ | |
| 1066 | + if( zFromUuid==0 || zFromUuid[0]==0 ){ | |
| 751 | 1067 | pid = 0; |
| 752 | 1068 | }else{ |
| 753 | 1069 | pid = uuid_to_rid(zFromUuid, 1); |
| 754 | 1070 | } |
| 755 | - if( zToUuid==0 ){ | |
| 1071 | + if( zToUuid==0 || zToUuid[0]==0 ){ | |
| 756 | 1072 | fid = 0; |
| 757 | 1073 | }else{ |
| 758 | 1074 | fid = uuid_to_rid(zToUuid, 1); |
| 759 | 1075 | } |
| 760 | 1076 | db_static_prepare(&s1, |
| @@ -771,33 +1087,83 @@ | ||
| 771 | 1087 | content_deltify(pid, fid, 0); |
| 772 | 1088 | } |
| 773 | 1089 | } |
| 774 | 1090 | |
| 775 | 1091 | /* |
| 776 | -** Locate a file named zName in the aFile[] array of the given | |
| 777 | -** manifest. We assume that filenames are in sorted order. | |
| 778 | -** Use a binary search. Return turn the index of the matching | |
| 779 | -** entry. Or return -1 if not found. | |
| 1092 | +** Do a binary search to find a file in the p->aFile[] array. | |
| 1093 | +** | |
| 1094 | +** As an optimization, guess that the file we seek is at index p->iFile. | |
| 1095 | +** That will usually be the case. If it is not found there, then do the | |
| 1096 | +** actual binary search. | |
| 1097 | +** | |
| 1098 | +** Update p->iFile to be the index of the file that is found. | |
| 780 | 1099 | */ |
| 781 | -static int find_file_in_manifest(Manifest *p, const char *zName){ | |
| 1100 | +static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){ | |
| 782 | 1101 | int lwr, upr; |
| 783 | 1102 | int c; |
| 784 | 1103 | int i; |
| 785 | 1104 | lwr = 0; |
| 786 | 1105 | upr = p->nFile - 1; |
| 1106 | + if( p->iFile>=lwr && p->iFile<upr ){ | |
| 1107 | + c = strcmp(p->aFile[p->iFile+1].zName, zName); | |
| 1108 | + if( c==0 ){ | |
| 1109 | + return &p->aFile[++p->iFile]; | |
| 1110 | + }else if( c>0 ){ | |
| 1111 | + upr = p->iFile; | |
| 1112 | + }else{ | |
| 1113 | + lwr = p->iFile+1; | |
| 1114 | + } | |
| 1115 | + } | |
| 787 | 1116 | while( lwr<=upr ){ |
| 788 | 1117 | i = (lwr+upr)/2; |
| 789 | 1118 | c = strcmp(p->aFile[i].zName, zName); |
| 790 | 1119 | if( c<0 ){ |
| 791 | 1120 | lwr = i+1; |
| 792 | 1121 | }else if( c>0 ){ |
| 793 | 1122 | upr = i-1; |
| 794 | 1123 | }else{ |
| 795 | - return i; | |
| 1124 | + p->iFile = i; | |
| 1125 | + return &p->aFile[i]; | |
| 796 | 1126 | } |
| 797 | 1127 | } |
| 798 | - return -1; | |
| 1128 | + return 0; | |
| 1129 | +} | |
| 1130 | + | |
| 1131 | +/* | |
| 1132 | +** Locate a file named zName in the aFile[] array of the given manifest. | |
| 1133 | +** Return a pointer to the appropriate ManifestFile object. Return NULL | |
| 1134 | +** if not found. | |
| 1135 | +** | |
| 1136 | +** This routine works even if p is a delta-manifest. The pointer | |
| 1137 | +** returned might be to the baseline. | |
| 1138 | +** | |
| 1139 | +** We assume that filenames are in sorted order and use a binary search. | |
| 1140 | +*/ | |
| 1141 | +ManifestFile *manifest_file_seek(Manifest *p, const char *zName){ | |
| 1142 | + ManifestFile *pFile; | |
| 1143 | + | |
| 1144 | + pFile = manifest_file_seek_base(p, zName); | |
| 1145 | + if( pFile && pFile->zUuid==0 ) return 0; | |
| 1146 | + if( pFile==0 && p->zBaseline ){ | |
| 1147 | + fetch_baseline(p, 1); | |
| 1148 | + pFile = manifest_file_seek_base(p->pBaseline, zName); | |
| 1149 | + } | |
| 1150 | + return pFile; | |
| 1151 | +} | |
| 1152 | + | |
| 1153 | +/* | |
| 1154 | +** This strcmp() function handles NULL arguments. NULLs sort first. | |
| 1155 | +*/ | |
| 1156 | +static int strcmp_null(const char *zOne, const char *zTwo){ | |
| 1157 | + if( zOne==0 ){ | |
| 1158 | + if( zTwo==0 ) return 0; | |
| 1159 | + return -1; | |
| 1160 | + }else if( zTwo==0 ){ | |
| 1161 | + return +1; | |
| 1162 | + }else{ | |
| 1163 | + return strcmp(zOne, zTwo); | |
| 1164 | + } | |
| 799 | 1165 | } |
| 800 | 1166 | |
| 801 | 1167 | /* |
| 802 | 1168 | ** Add mlink table entries associated with manifest cid. The |
| 803 | 1169 | ** parent manifest is pid. |
| @@ -808,85 +1174,76 @@ | ||
| 808 | 1174 | ** Deleted files have mlink.fid=0. |
| 809 | 1175 | ** Added files have mlink.pid=0. |
| 810 | 1176 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 811 | 1177 | */ |
| 812 | 1178 | static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ |
| 813 | - Manifest other; | |
| 814 | 1179 | Blob otherContent; |
| 815 | - int i, j; | |
| 1180 | + int otherRid; | |
| 1181 | + int i, rc; | |
| 1182 | + ManifestFile *pChildFile, *pParentFile; | |
| 1183 | + Manifest **ppOther; | |
| 1184 | + static Stmt eq; | |
| 816 | 1185 | |
| 817 | - if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){ | |
| 818 | - return; | |
| 819 | - } | |
| 1186 | + db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid"); | |
| 1187 | + db_bind_int(&eq, ":mid", cid); | |
| 1188 | + rc = db_step(&eq); | |
| 1189 | + db_reset(&eq); | |
| 1190 | + if( rc==SQLITE_ROW ) return; | |
| 1191 | + | |
| 820 | 1192 | assert( pParent==0 || pChild==0 ); |
| 821 | 1193 | if( pParent==0 ){ |
| 822 | - pParent = &other; | |
| 823 | - content_get(pid, &otherContent); | |
| 824 | - }else{ | |
| 825 | - pChild = &other; | |
| 826 | - content_get(cid, &otherContent); | |
| 827 | - } | |
| 828 | - if( blob_size(&otherContent)==0 ) return; | |
| 829 | - if( manifest_parse(&other, &otherContent)==0 ) return; | |
| 830 | - content_deltify(pid, cid, 0); | |
| 831 | - | |
| 832 | - /* Use the iRename fields to find the cross-linkage between | |
| 833 | - ** renamed files. */ | |
| 834 | - for(j=0; j<pChild->nFile; j++){ | |
| 835 | - const char *zPrior = pChild->aFile[j].zPrior; | |
| 836 | - if( zPrior && zPrior[0] ){ | |
| 837 | - i = find_file_in_manifest(pParent, zPrior); | |
| 838 | - if( i>=0 ){ | |
| 839 | - pChild->aFile[j].iRename = i; | |
| 840 | - pParent->aFile[i].iRename = j; | |
| 841 | - } | |
| 842 | - } | |
| 843 | - } | |
| 844 | - | |
| 845 | - /* Construct the mlink entries */ | |
| 846 | - for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){ | |
| 847 | - int c; | |
| 848 | - if( pParent->aFile[i].iRename>=0 ){ | |
| 849 | - i++; | |
| 850 | - }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){ | |
| 851 | - add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0); | |
| 852 | - i++; | |
| 853 | - }else if( c>0 ){ | |
| 854 | - int rn = pChild->aFile[j].iRename; | |
| 855 | - if( rn>=0 ){ | |
| 856 | - add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, | |
| 857 | - pChild->aFile[j].zName, pParent->aFile[rn].zName); | |
| 858 | - }else{ | |
| 859 | - add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0); | |
| 860 | - } | |
| 861 | - j++; | |
| 862 | - }else{ | |
| 863 | - if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){ | |
| 864 | - add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid, | |
| 865 | - pChild->aFile[j].zName, 0); | |
| 866 | - } | |
| 867 | - i++; | |
| 868 | - j++; | |
| 869 | - } | |
| 870 | - } | |
| 871 | - while( i<pParent->nFile ){ | |
| 872 | - if( pParent->aFile[i].iRename<0 ){ | |
| 873 | - add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0); | |
| 874 | - } | |
| 875 | - i++; | |
| 876 | - } | |
| 877 | - while( j<pChild->nFile ){ | |
| 878 | - int rn = pChild->aFile[j].iRename; | |
| 879 | - if( rn>=0 ){ | |
| 880 | - add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, | |
| 881 | - pChild->aFile[j].zName, pParent->aFile[rn].zName); | |
| 882 | - }else{ | |
| 883 | - add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0); | |
| 884 | - } | |
| 885 | - j++; | |
| 886 | - } | |
| 887 | - manifest_clear(&other); | |
| 1194 | + ppOther = &pParent; | |
| 1195 | + otherRid = pid; | |
| 1196 | + }else{ | |
| 1197 | + ppOther = &pChild; | |
| 1198 | + otherRid = cid; | |
| 1199 | + } | |
| 1200 | + if( (*ppOther = manifest_cache_find(otherRid))==0 ){ | |
| 1201 | + content_get(otherRid, &otherContent); | |
| 1202 | + if( blob_size(&otherContent)==0 ) return; | |
| 1203 | + *ppOther = manifest_parse(&otherContent, otherRid); | |
| 1204 | + if( *ppOther==0 ) return; | |
| 1205 | + } | |
| 1206 | + if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ | |
| 1207 | + manifest_destroy(*ppOther); | |
| 1208 | + return; | |
| 1209 | + } | |
| 1210 | + if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ | |
| 1211 | + content_deltify(pid, cid, 0); | |
| 1212 | + }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ | |
| 1213 | + content_deltify(pParent->pBaseline->rid, cid, 0); | |
| 1214 | + } | |
| 1215 | + | |
| 1216 | + for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){ | |
| 1217 | + if( pChildFile->zPrior ){ | |
| 1218 | + pParentFile = manifest_file_seek(pParent, pChildFile->zPrior); | |
| 1219 | + if( pParentFile ){ | |
| 1220 | + add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1221 | + pChildFile->zName, pChildFile->zPrior); | |
| 1222 | + } | |
| 1223 | + }else{ | |
| 1224 | + pParentFile = manifest_file_seek(pParent, pChildFile->zName); | |
| 1225 | + if( pParentFile==0 ){ | |
| 1226 | + if( pChildFile->zUuid ){ | |
| 1227 | + add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); | |
| 1228 | + } | |
| 1229 | + }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){ | |
| 1230 | + add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1231 | + pChildFile->zName, 0); | |
| 1232 | + } | |
| 1233 | + } | |
| 1234 | + } | |
| 1235 | + if( pParent->zBaseline && pChild->zBaseline ){ | |
| 1236 | + for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ | |
| 1237 | + if( pParentFile->zUuid ) continue; | |
| 1238 | + pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1239 | + if( pChildFile ){ | |
| 1240 | + add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); | |
| 1241 | + } | |
| 1242 | + } | |
| 1243 | + } | |
| 1244 | + manifest_cache_insert(*ppOther); | |
| 888 | 1245 | } |
| 889 | 1246 | |
| 890 | 1247 | /* |
| 891 | 1248 | ** True if manifest_crosslink_begin() has been called but |
| 892 | 1249 | ** manifest_crosslink_end() is still pending. |
| @@ -1023,38 +1380,44 @@ | ||
| 1023 | 1380 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1024 | 1381 | ** file, is a legacy of its original use. |
| 1025 | 1382 | */ |
| 1026 | 1383 | int manifest_crosslink(int rid, Blob *pContent){ |
| 1027 | 1384 | int i; |
| 1028 | - Manifest m; | |
| 1385 | + Manifest *p; | |
| 1029 | 1386 | Stmt q; |
| 1030 | 1387 | int parentid = 0; |
| 1031 | 1388 | |
| 1032 | - if( manifest_parse(&m, pContent)==0 ){ | |
| 1389 | + if( (p = manifest_cache_find(rid))!=0 ){ | |
| 1390 | + blob_reset(pContent); | |
| 1391 | + }else if( (p = manifest_parse(pContent, rid))==0 ){ | |
| 1392 | + return 0; | |
| 1393 | + } | |
| 1394 | + if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ | |
| 1395 | + manifest_destroy(p); | |
| 1033 | 1396 | return 0; |
| 1034 | 1397 | } |
| 1035 | - if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){ | |
| 1036 | - manifest_clear(&m); | |
| 1398 | + if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ | |
| 1399 | + manifest_destroy(p); | |
| 1037 | 1400 | return 0; |
| 1038 | 1401 | } |
| 1039 | 1402 | db_begin_transaction(); |
| 1040 | - if( m.type==CFTYPE_MANIFEST ){ | |
| 1403 | + if( p->type==CFTYPE_MANIFEST ){ | |
| 1041 | 1404 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1042 | 1405 | char *zCom; |
| 1043 | - for(i=0; i<m.nParent; i++){ | |
| 1044 | - int pid = uuid_to_rid(m.azParent[i], 1); | |
| 1406 | + for(i=0; i<p->nParent; i++){ | |
| 1407 | + int pid = uuid_to_rid(p->azParent[i], 1); | |
| 1045 | 1408 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| 1046 | - "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate); | |
| 1409 | + "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate); | |
| 1047 | 1410 | if( i==0 ){ |
| 1048 | - add_mlink(pid, 0, rid, &m); | |
| 1411 | + add_mlink(pid, 0, rid, p); | |
| 1049 | 1412 | parentid = pid; |
| 1050 | 1413 | } |
| 1051 | 1414 | } |
| 1052 | 1415 | db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); |
| 1053 | 1416 | while( db_step(&q)==SQLITE_ROW ){ |
| 1054 | 1417 | int cid = db_column_int(&q, 0); |
| 1055 | - add_mlink(rid, &m, cid, 0); | |
| 1418 | + add_mlink(rid, p, cid, 0); | |
| 1056 | 1419 | } |
| 1057 | 1420 | db_finalize(&q); |
| 1058 | 1421 | db_multi_exec( |
| 1059 | 1422 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1060 | 1423 | "bgcolor,euser,ecomment)" |
| @@ -1065,118 +1428,134 @@ | ||
| 1065 | 1428 | " )," |
| 1066 | 1429 | " %d,%Q,%Q," |
| 1067 | 1430 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0)," |
| 1068 | 1431 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1069 | 1432 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1070 | - TAG_DATE, rid, m.rDate, | |
| 1071 | - rid, m.zUser, m.zComment, | |
| 1433 | + TAG_DATE, rid, p->rDate, | |
| 1434 | + rid, p->zUser, p->zComment, | |
| 1072 | 1435 | TAG_BGCOLOR, rid, |
| 1073 | 1436 | TAG_USER, rid, |
| 1074 | 1437 | TAG_COMMENT, rid |
| 1075 | 1438 | ); |
| 1076 | 1439 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 1077 | 1440 | " WHERE rowid=last_insert_rowid()"); |
| 1078 | - wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE); | |
| 1079 | - free(zCom); | |
| 1080 | - } | |
| 1081 | - } | |
| 1082 | - if( m.type==CFTYPE_CLUSTER ){ | |
| 1083 | - tag_insert("cluster", 1, 0, rid, m.rDate, rid); | |
| 1084 | - for(i=0; i<m.nCChild; i++){ | |
| 1085 | - int mid; | |
| 1086 | - mid = uuid_to_rid(m.azCChild[i], 1); | |
| 1087 | - if( mid>0 ){ | |
| 1088 | - db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid); | |
| 1089 | - } | |
| 1090 | - } | |
| 1091 | - } | |
| 1092 | - if( m.type==CFTYPE_CONTROL | |
| 1093 | - || m.type==CFTYPE_MANIFEST | |
| 1094 | - || m.type==CFTYPE_EVENT | |
| 1095 | - ){ | |
| 1096 | - for(i=0; i<m.nTag; i++){ | |
| 1097 | - int tid; | |
| 1098 | - int type; | |
| 1099 | - if( m.aTag[i].zUuid ){ | |
| 1100 | - tid = uuid_to_rid(m.aTag[i].zUuid, 1); | |
| 1441 | + wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); | |
| 1442 | + free(zCom); | |
| 1443 | + | |
| 1444 | + /* If this is a delta-manifest, record the fact that this repository | |
| 1445 | + ** contains delta manifests, to free the "commit" logic to generate | |
| 1446 | + ** new delta manifests. | |
| 1447 | + */ | |
| 1448 | + if( p->zBaseline!=0 ){ | |
| 1449 | + static int once = 0; | |
| 1450 | + if( !once ){ | |
| 1451 | + db_set_int("seen-delta-manifest", 1, 0); | |
| 1452 | + once = 0; | |
| 1453 | + } | |
| 1454 | + } | |
| 1455 | + } | |
| 1456 | + } | |
| 1457 | + if( p->type==CFTYPE_CLUSTER ){ | |
| 1458 | + static Stmt del1; | |
| 1459 | + tag_insert("cluster", 1, 0, rid, p->rDate, rid); | |
| 1460 | + db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid"); | |
| 1461 | + for(i=0; i<p->nCChild; i++){ | |
| 1462 | + int mid; | |
| 1463 | + mid = uuid_to_rid(p->azCChild[i], 1); | |
| 1464 | + if( mid>0 ){ | |
| 1465 | + db_bind_int(&del1, ":rid", mid); | |
| 1466 | + db_step(&del1); | |
| 1467 | + db_reset(&del1); | |
| 1468 | + } | |
| 1469 | + } | |
| 1470 | + } | |
| 1471 | + if( p->type==CFTYPE_CONTROL | |
| 1472 | + || p->type==CFTYPE_MANIFEST | |
| 1473 | + || p->type==CFTYPE_EVENT | |
| 1474 | + ){ | |
| 1475 | + for(i=0; i<p->nTag; i++){ | |
| 1476 | + int tid; | |
| 1477 | + int type; | |
| 1478 | + if( p->aTag[i].zUuid ){ | |
| 1479 | + tid = uuid_to_rid(p->aTag[i].zUuid, 1); | |
| 1101 | 1480 | }else{ |
| 1102 | 1481 | tid = rid; |
| 1103 | 1482 | } |
| 1104 | 1483 | if( tid ){ |
| 1105 | - switch( m.aTag[i].zName[0] ){ | |
| 1484 | + switch( p->aTag[i].zName[0] ){ | |
| 1106 | 1485 | case '-': type = 0; break; /* Cancel prior occurances */ |
| 1107 | 1486 | case '+': type = 1; break; /* Apply to target only */ |
| 1108 | 1487 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1109 | 1488 | default: |
| 1110 | - fossil_fatal("unknown tag type in manifest: %s", m.aTag); | |
| 1489 | + fossil_fatal("unknown tag type in manifest: %s", p->aTag); | |
| 1111 | 1490 | return 0; |
| 1112 | 1491 | } |
| 1113 | - tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue, | |
| 1114 | - rid, m.rDate, tid); | |
| 1492 | + tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, | |
| 1493 | + rid, p->rDate, tid); | |
| 1115 | 1494 | } |
| 1116 | 1495 | } |
| 1117 | 1496 | if( parentid ){ |
| 1118 | 1497 | tag_propagate_all(parentid); |
| 1119 | 1498 | } |
| 1120 | 1499 | } |
| 1121 | - if( m.type==CFTYPE_WIKI ){ | |
| 1122 | - char *zTag = mprintf("wiki-%s", m.zWikiTitle); | |
| 1500 | + if( p->type==CFTYPE_WIKI ){ | |
| 1501 | + char *zTag = mprintf("wiki-%s", p->zWikiTitle); | |
| 1123 | 1502 | int tagid = tag_findid(zTag, 1); |
| 1124 | 1503 | int prior; |
| 1125 | 1504 | char *zComment; |
| 1126 | 1505 | int nWiki; |
| 1127 | 1506 | char zLength[40]; |
| 1128 | - while( isspace(m.zWiki[0]) ) m.zWiki++; | |
| 1129 | - nWiki = strlen(m.zWiki); | |
| 1507 | + while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; | |
| 1508 | + nWiki = strlen(p->zWiki); | |
| 1130 | 1509 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1131 | - tag_insert(zTag, 1, zLength, rid, m.rDate, rid); | |
| 1510 | + tag_insert(zTag, 1, zLength, rid, p->rDate, rid); | |
| 1132 | 1511 | free(zTag); |
| 1133 | 1512 | prior = db_int(0, |
| 1134 | 1513 | "SELECT rid FROM tagxref" |
| 1135 | 1514 | " WHERE tagid=%d AND mtime<%.17g" |
| 1136 | 1515 | " ORDER BY mtime DESC", |
| 1137 | - tagid, m.rDate | |
| 1516 | + tagid, p->rDate | |
| 1138 | 1517 | ); |
| 1139 | 1518 | if( prior ){ |
| 1140 | 1519 | content_deltify(prior, rid, 0); |
| 1141 | 1520 | } |
| 1142 | 1521 | if( nWiki>0 ){ |
| 1143 | - zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle); | |
| 1522 | + zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); | |
| 1144 | 1523 | }else{ |
| 1145 | - zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle); | |
| 1524 | + zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); | |
| 1146 | 1525 | } |
| 1147 | 1526 | db_multi_exec( |
| 1148 | 1527 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1149 | 1528 | " bgcolor,euser,ecomment)" |
| 1150 | 1529 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 1151 | 1530 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| 1152 | 1531 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1153 | 1532 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1154 | - m.rDate, rid, m.zUser, zComment, | |
| 1533 | + p->rDate, rid, p->zUser, zComment, | |
| 1155 | 1534 | TAG_BGCOLOR, rid, |
| 1156 | 1535 | TAG_BGCOLOR, rid, |
| 1157 | 1536 | TAG_USER, rid, |
| 1158 | 1537 | TAG_COMMENT, rid |
| 1159 | 1538 | ); |
| 1160 | 1539 | free(zComment); |
| 1161 | 1540 | } |
| 1162 | - if( m.type==CFTYPE_EVENT ){ | |
| 1163 | - char *zTag = mprintf("event-%s", m.zEventId); | |
| 1541 | + if( p->type==CFTYPE_EVENT ){ | |
| 1542 | + char *zTag = mprintf("event-%s", p->zEventId); | |
| 1164 | 1543 | int tagid = tag_findid(zTag, 1); |
| 1165 | 1544 | int prior, subsequent; |
| 1166 | 1545 | int nWiki; |
| 1167 | 1546 | char zLength[40]; |
| 1168 | - while( isspace(m.zWiki[0]) ) m.zWiki++; | |
| 1169 | - nWiki = strlen(m.zWiki); | |
| 1547 | + while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; | |
| 1548 | + nWiki = strlen(p->zWiki); | |
| 1170 | 1549 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1171 | - tag_insert(zTag, 1, zLength, rid, m.rDate, rid); | |
| 1550 | + tag_insert(zTag, 1, zLength, rid, p->rDate, rid); | |
| 1172 | 1551 | free(zTag); |
| 1173 | 1552 | prior = db_int(0, |
| 1174 | 1553 | "SELECT rid FROM tagxref" |
| 1175 | 1554 | " WHERE tagid=%d AND mtime<%.17g" |
| 1176 | 1555 | " ORDER BY mtime DESC", |
| 1177 | - tagid, m.rDate | |
| 1556 | + tagid, p->rDate | |
| 1178 | 1557 | ); |
| 1179 | 1558 | if( prior ){ |
| 1180 | 1559 | content_deltify(prior, rid, 0); |
| 1181 | 1560 | db_multi_exec( |
| 1182 | 1561 | "DELETE FROM event" |
| @@ -1188,104 +1567,87 @@ | ||
| 1188 | 1567 | } |
| 1189 | 1568 | subsequent = db_int(0, |
| 1190 | 1569 | "SELECT rid FROM tagxref" |
| 1191 | 1570 | " WHERE tagid=%d AND mtime>%.17g" |
| 1192 | 1571 | " ORDER BY mtime", |
| 1193 | - tagid, m.rDate | |
| 1572 | + tagid, p->rDate | |
| 1194 | 1573 | ); |
| 1195 | 1574 | if( subsequent ){ |
| 1196 | 1575 | content_deltify(rid, subsequent, 0); |
| 1197 | 1576 | }else{ |
| 1198 | 1577 | db_multi_exec( |
| 1199 | 1578 | "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" |
| 1200 | 1579 | "VALUES('e',%.17g,%d,%d,%Q,%Q," |
| 1201 | 1580 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1202 | - m.rEventDate, rid, tagid, m.zUser, m.zComment, | |
| 1581 | + p->rEventDate, rid, tagid, p->zUser, p->zComment, | |
| 1203 | 1582 | TAG_BGCOLOR, rid |
| 1204 | 1583 | ); |
| 1205 | 1584 | } |
| 1206 | 1585 | } |
| 1207 | - if( m.type==CFTYPE_TICKET ){ | |
| 1586 | + if( p->type==CFTYPE_TICKET ){ | |
| 1208 | 1587 | char *zTag; |
| 1209 | 1588 | |
| 1210 | 1589 | assert( manifest_crosslink_busy==1 ); |
| 1211 | - zTag = mprintf("tkt-%s", m.zTicketUuid); | |
| 1212 | - tag_insert(zTag, 1, 0, rid, m.rDate, rid); | |
| 1590 | + zTag = mprintf("tkt-%s", p->zTicketUuid); | |
| 1591 | + tag_insert(zTag, 1, 0, rid, p->rDate, rid); | |
| 1213 | 1592 | free(zTag); |
| 1214 | 1593 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| 1215 | - m.zTicketUuid); | |
| 1594 | + p->zTicketUuid); | |
| 1216 | 1595 | } |
| 1217 | - if( m.type==CFTYPE_ATTACHMENT ){ | |
| 1596 | + if( p->type==CFTYPE_ATTACHMENT ){ | |
| 1218 | 1597 | db_multi_exec( |
| 1219 | 1598 | "INSERT INTO attachment(attachid, mtime, src, target," |
| 1220 | 1599 | "filename, comment, user)" |
| 1221 | 1600 | "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", |
| 1222 | - rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName, | |
| 1223 | - (m.zComment ? m.zComment : ""), m.zUser | |
| 1601 | + rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName, | |
| 1602 | + (p->zComment ? p->zComment : ""), p->zUser | |
| 1224 | 1603 | ); |
| 1225 | 1604 | db_multi_exec( |
| 1226 | 1605 | "UPDATE attachment SET isLatest = (mtime==" |
| 1227 | 1606 | "(SELECT max(mtime) FROM attachment" |
| 1228 | 1607 | " WHERE target=%Q AND filename=%Q))" |
| 1229 | 1608 | " WHERE target=%Q AND filename=%Q", |
| 1230 | - m.zAttachTarget, m.zAttachName, | |
| 1231 | - m.zAttachTarget, m.zAttachName | |
| 1609 | + p->zAttachTarget, p->zAttachName, | |
| 1610 | + p->zAttachTarget, p->zAttachName | |
| 1232 | 1611 | ); |
| 1233 | - if( strlen(m.zAttachTarget)!=UUID_SIZE | |
| 1234 | - || !validate16(m.zAttachTarget, UUID_SIZE) | |
| 1612 | + if( strlen(p->zAttachTarget)!=UUID_SIZE | |
| 1613 | + || !validate16(p->zAttachTarget, UUID_SIZE) | |
| 1235 | 1614 | ){ |
| 1236 | 1615 | char *zComment; |
| 1237 | - if( m.zAttachSrc && m.zAttachSrc[0] ){ | |
| 1616 | + if( p->zAttachSrc && p->zAttachSrc[0] ){ | |
| 1238 | 1617 | zComment = mprintf("Add attachment \"%h\" to wiki page [%h]", |
| 1239 | - m.zAttachName, m.zAttachTarget); | |
| 1618 | + p->zAttachName, p->zAttachTarget); | |
| 1240 | 1619 | }else{ |
| 1241 | 1620 | zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", |
| 1242 | - m.zAttachName, m.zAttachTarget); | |
| 1621 | + p->zAttachName, p->zAttachTarget); | |
| 1243 | 1622 | } |
| 1244 | 1623 | db_multi_exec( |
| 1245 | 1624 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1246 | 1625 | "VALUES('w',%.17g,%d,%Q,%Q)", |
| 1247 | - m.rDate, rid, m.zUser, zComment | |
| 1626 | + p->rDate, rid, p->zUser, zComment | |
| 1248 | 1627 | ); |
| 1249 | 1628 | free(zComment); |
| 1250 | 1629 | }else{ |
| 1251 | 1630 | char *zComment; |
| 1252 | - if( m.zAttachSrc && m.zAttachSrc[0] ){ | |
| 1631 | + if( p->zAttachSrc && p->zAttachSrc[0] ){ | |
| 1253 | 1632 | zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]", |
| 1254 | - m.zAttachName, m.zAttachTarget); | |
| 1633 | + p->zAttachName, p->zAttachTarget); | |
| 1255 | 1634 | }else{ |
| 1256 | 1635 | zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]", |
| 1257 | - m.zAttachName, m.zAttachTarget); | |
| 1636 | + p->zAttachName, p->zAttachTarget); | |
| 1258 | 1637 | } |
| 1259 | 1638 | db_multi_exec( |
| 1260 | 1639 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1261 | 1640 | "VALUES('t',%.17g,%d,%Q,%Q)", |
| 1262 | - m.rDate, rid, m.zUser, zComment | |
| 1641 | + p->rDate, rid, p->zUser, zComment | |
| 1263 | 1642 | ); |
| 1264 | 1643 | free(zComment); |
| 1265 | 1644 | } |
| 1266 | 1645 | } |
| 1267 | 1646 | db_end_transaction(0); |
| 1268 | - manifest_clear(&m); | |
| 1269 | - return 1; | |
| 1270 | -} | |
| 1271 | - | |
| 1272 | -/* | |
| 1273 | -** Given a checkin name, load and parse the manifest for that checkin. | |
| 1274 | -** Throw a fatal error if anything goes wrong. | |
| 1275 | -*/ | |
| 1276 | -void manifest_from_name( | |
| 1277 | - const char *zName, | |
| 1278 | - Manifest *pM | |
| 1279 | -){ | |
| 1280 | - int rid; | |
| 1281 | - Blob content; | |
| 1282 | - | |
| 1283 | - rid = name_to_rid(zName); | |
| 1284 | - if( !is_a_version(rid) ){ | |
| 1285 | - fossil_fatal("no such checkin: %s", zName); | |
| 1286 | - } | |
| 1287 | - content_get(rid, &content); | |
| 1288 | - if( !manifest_parse(pM, &content) ){ | |
| 1289 | - fossil_fatal("cannot parse manifest for checkin: %s", zName); | |
| 1290 | - } | |
| 1647 | + if( p->type==CFTYPE_MANIFEST ){ | |
| 1648 | + manifest_cache_insert(p); | |
| 1649 | + }else{ | |
| 1650 | + manifest_destroy(p); | |
| 1651 | + } | |
| 1652 | + return 1; | |
| 1291 | 1653 | } |
| 1292 | 1654 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -26,24 +26,39 @@ | |
| 26 | |
| 27 | #if INTERFACE |
| 28 | /* |
| 29 | ** Types of control files |
| 30 | */ |
| 31 | #define CFTYPE_MANIFEST 1 |
| 32 | #define CFTYPE_CLUSTER 2 |
| 33 | #define CFTYPE_CONTROL 3 |
| 34 | #define CFTYPE_WIKI 4 |
| 35 | #define CFTYPE_TICKET 5 |
| 36 | #define CFTYPE_ATTACHMENT 6 |
| 37 | #define CFTYPE_EVENT 7 |
| 38 | |
| 39 | /* |
| 40 | ** A parsed manifest or cluster. |
| 41 | */ |
| 42 | struct Manifest { |
| 43 | Blob content; /* The original content blob */ |
| 44 | int type; /* Type of artifact. One of CFTYPE_xxxxx */ |
| 45 | char *zComment; /* Decoded comment. The C card. */ |
| 46 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 47 | char *zUser; /* Name of the user from the U card. */ |
| 48 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 49 | char *zWiki; /* Text of the wiki page. W card. */ |
| @@ -54,17 +69,12 @@ | |
| 54 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 55 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 56 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 57 | int nFile; /* Number of F cards */ |
| 58 | int nFileAlloc; /* Slots allocated in aFile[] */ |
| 59 | struct { |
| 60 | char *zName; /* Name of a file */ |
| 61 | char *zUuid; /* UUID of the file */ |
| 62 | char *zPerm; /* File permissions */ |
| 63 | char *zPrior; /* Prior name if the name was changed */ |
| 64 | int iRename; /* index of renamed name in prior/next manifest */ |
| 65 | } *aFile; /* One entry for each F card */ |
| 66 | int nParent; /* Number of parents. */ |
| 67 | int nParentAlloc; /* Slots allocated in azParent[] */ |
| 68 | char **azParent; /* UUIDs of parents. One for each P card argument */ |
| 69 | int nCChild; /* Number of cluster children */ |
| 70 | int nCChildAlloc; /* Number of closts allocated in azCChild[] */ |
| @@ -83,22 +93,192 @@ | |
| 83 | char *zValue; /* Value of the field */ |
| 84 | } *aField; /* One for each J card */ |
| 85 | }; |
| 86 | #endif |
| 87 | |
| 88 | |
| 89 | /* |
| 90 | ** Clear the memory allocated in a manifest object |
| 91 | */ |
| 92 | void manifest_clear(Manifest *p){ |
| 93 | blob_reset(&p->content); |
| 94 | free(p->aFile); |
| 95 | free(p->azParent); |
| 96 | free(p->azCChild); |
| 97 | free(p->aTag); |
| 98 | free(p->aField); |
| 99 | memset(p, 0, sizeof(*p)); |
| 100 | } |
| 101 | |
| 102 | /* |
| 103 | ** Parse a blob into a Manifest object. The Manifest object |
| 104 | ** takes over the input blob and will free it when the |
| @@ -124,48 +304,66 @@ | |
| 124 | ** Each card is divided into tokens by a single space character. |
| 125 | ** The first token is a single upper-case letter which is the card type. |
| 126 | ** The card type determines the other parameters to the card. |
| 127 | ** Cards must occur in lexicographical order. |
| 128 | */ |
| 129 | int manifest_parse(Manifest *p, Blob *pContent){ |
| 130 | int seenHeader = 0; |
| 131 | int seenZ = 0; |
| 132 | int i, lineNo=0; |
| 133 | Blob line, token, a1, a2, a3, a4; |
| 134 | char cPrevType = 0; |
| 135 | |
| 136 | memset(p, 0, sizeof(*p)); |
| 137 | memcpy(&p->content, pContent, sizeof(p->content)); |
| 138 | blob_zero(pContent); |
| 139 | pContent = &p->content; |
| 140 | |
| 141 | blob_zero(&a1); |
| 142 | blob_zero(&a2); |
| 143 | blob_zero(&a3); |
| 144 | md5sum_init(); |
| 145 | while( blob_line(pContent, &line) ){ |
| 146 | char *z = blob_buffer(&line); |
| 147 | lineNo++; |
| 148 | if( z[0]=='-' ){ |
| 149 | if( strncmp(z, "-----BEGIN PGP ", 15)!=0 ){ |
| 150 | goto manifest_syntax_error; |
| 151 | } |
| 152 | if( seenHeader ){ |
| 153 | break; |
| 154 | } |
| 155 | while( blob_line(pContent, &line)>2 ){} |
| 156 | if( blob_line(pContent, &line)==0 ) break; |
| 157 | z = blob_buffer(&line); |
| 158 | } |
| 159 | if( z[0]<cPrevType ){ |
| 160 | /* Lines of a manifest must occur in lexicographical order */ |
| 161 | goto manifest_syntax_error; |
| 162 | } |
| 163 | cPrevType = z[0]; |
| 164 | seenHeader = 1; |
| 165 | if( blob_token(&line, &token)!=1 ) goto manifest_syntax_error; |
| 166 | switch( z[0] ){ |
| 167 | /* |
| 168 | ** A <filename> <target> ?<source>? |
| 169 | ** |
| 170 | ** Identifies an attachment to either a wiki page or a ticket. |
| 171 | ** <source> is the artifact that is the attachment. <source> |
| @@ -172,50 +370,60 @@ | |
| 172 | ** is omitted to delete an attachment. <target> is the name of |
| 173 | ** a wiki page or ticket to which that attachment is connected. |
| 174 | */ |
| 175 | case 'A': { |
| 176 | char *zName, *zTarget, *zSrc; |
| 177 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 178 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 179 | if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; |
| 180 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 181 | zName = blob_terminate(&a1); |
| 182 | zTarget = blob_terminate(&a2); |
| 183 | blob_token(&line, &a3); |
| 184 | zSrc = blob_terminate(&a3); |
| 185 | defossilize(zName); |
| 186 | if( !file_is_simple_pathname(zName) ){ |
| 187 | goto manifest_syntax_error; |
| 188 | } |
| 189 | defossilize(zTarget); |
| 190 | if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 191 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 192 | goto manifest_syntax_error; |
| 193 | } |
| 194 | if( blob_size(&a3)>0 |
| 195 | && (blob_size(&a3)!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 196 | goto manifest_syntax_error; |
| 197 | } |
| 198 | p->zAttachName = (char*)file_tail(zName); |
| 199 | p->zAttachSrc = zSrc; |
| 200 | p->zAttachTarget = zTarget; |
| 201 | break; |
| 202 | } |
| 203 | |
| 204 | /* |
| 205 | ** C <comment> |
| 206 | ** |
| 207 | ** Comment text is fossil-encoded. There may be no more than |
| 208 | ** one C line. C lines are required for manifests and are |
| 209 | ** disallowed on all other control files. |
| 210 | */ |
| 211 | case 'C': { |
| 212 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 213 | if( p->zComment!=0 ) goto manifest_syntax_error; |
| 214 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 215 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 216 | p->zComment = blob_terminate(&a1); |
| 217 | defossilize(p->zComment); |
| 218 | break; |
| 219 | } |
| 220 | |
| 221 | /* |
| @@ -224,17 +432,13 @@ | |
| 224 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 225 | ** There can be no more than 1 D line. D lines are required |
| 226 | ** for all control files except for clusters. |
| 227 | */ |
| 228 | case 'D': { |
| 229 | char *zDate; |
| 230 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 231 | if( p->rDate!=0.0 ) goto manifest_syntax_error; |
| 232 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 233 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 234 | zDate = blob_terminate(&a1); |
| 235 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", zDate); |
| 236 | break; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | ** E <timestamp> <uuid> |
| @@ -244,68 +448,57 @@ | |
| 244 | ** The event timestamp is distinct from the D timestamp. The D |
| 245 | ** timestamp is when the artifact was created whereas the E timestamp |
| 246 | ** is when the specific event is said to occur. |
| 247 | */ |
| 248 | case 'E': { |
| 249 | char *zEDate; |
| 250 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 251 | if( p->rEventDate!=0.0 ) goto manifest_syntax_error; |
| 252 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 253 | if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; |
| 254 | if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; |
| 255 | zEDate = blob_terminate(&a1); |
| 256 | p->rEventDate = db_double(0.0, "SELECT julianday(%Q)", zEDate); |
| 257 | if( p->rEventDate<=0.0 ) goto manifest_syntax_error; |
| 258 | if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; |
| 259 | p->zEventId = blob_terminate(&a2); |
| 260 | if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; |
| 261 | break; |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | ** F <filename> <uuid> ?<permissions>? ?<old-name>? |
| 266 | ** |
| 267 | ** Identifies a file in a manifest. Multiple F lines are |
| 268 | ** allowed in a manifest. F lines are not allowed in any |
| 269 | ** other control file. The filename and old-name are fossil-encoded. |
| 270 | */ |
| 271 | case 'F': { |
| 272 | char *zName, *zUuid, *zPerm, *zPriorName; |
| 273 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 274 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 275 | if( blob_token(&line, &a2)==0 ) goto manifest_syntax_error; |
| 276 | zName = blob_terminate(&a1); |
| 277 | zUuid = blob_terminate(&a2); |
| 278 | blob_token(&line, &a3); |
| 279 | zPerm = blob_terminate(&a3); |
| 280 | if( blob_size(&a2)!=UUID_SIZE ) goto manifest_syntax_error; |
| 281 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 282 | defossilize(zName); |
| 283 | if( !file_is_simple_pathname(zName) ){ |
| 284 | goto manifest_syntax_error; |
| 285 | } |
| 286 | blob_token(&line, &a4); |
| 287 | zPriorName = blob_terminate(&a4); |
| 288 | if( zPriorName[0] ){ |
| 289 | defossilize(zPriorName); |
| 290 | if( !file_is_simple_pathname(zPriorName) ){ |
| 291 | goto manifest_syntax_error; |
| 292 | } |
| 293 | }else{ |
| 294 | zPriorName = 0; |
| 295 | } |
| 296 | if( p->nFile>=p->nFileAlloc ){ |
| 297 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 298 | p->aFile = realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) ); |
| 299 | if( p->aFile==0 ) fossil_panic("out of memory"); |
| 300 | } |
| 301 | i = p->nFile++; |
| 302 | p->aFile[i].zName = zName; |
| 303 | p->aFile[i].zUuid = zUuid; |
| 304 | p->aFile[i].zPerm = zPerm; |
| 305 | p->aFile[i].zPrior = zPriorName; |
| 306 | p->aFile[i].iRename = -1; |
| 307 | if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 308 | goto manifest_syntax_error; |
| 309 | } |
| 310 | break; |
| 311 | } |
| @@ -318,22 +511,19 @@ | |
| 318 | ** value. If <value> is omitted then it is understood to be an |
| 319 | ** empty string. |
| 320 | */ |
| 321 | case 'J': { |
| 322 | char *zName, *zValue; |
| 323 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 324 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 325 | blob_token(&line, &a2); |
| 326 | if( blob_token(&line, &a3)!=0 ) goto manifest_syntax_error; |
| 327 | zName = blob_terminate(&a1); |
| 328 | zValue = blob_terminate(&a2); |
| 329 | defossilize(zValue); |
| 330 | if( p->nField>=p->nFieldAlloc ){ |
| 331 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 332 | p->aField = realloc(p->aField, |
| 333 | p->nFieldAlloc*sizeof(p->aField[0]) ); |
| 334 | if( p->aField==0 ) fossil_panic("out of memory"); |
| 335 | } |
| 336 | i = p->nField++; |
| 337 | p->aField[i].zName = zName; |
| 338 | p->aField[i].zValue = zValue; |
| 339 | if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| @@ -348,18 +538,14 @@ | |
| 348 | ** |
| 349 | ** A K-line gives the UUID for the ticket which this control file |
| 350 | ** is amending. |
| 351 | */ |
| 352 | case 'K': { |
| 353 | char *zUuid; |
| 354 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 355 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 356 | zUuid = blob_terminate(&a1); |
| 357 | if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; |
| 358 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 359 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 360 | p->zTicketUuid = zUuid; |
| 361 | break; |
| 362 | } |
| 363 | |
| 364 | /* |
| 365 | ** L <wikititle> |
| @@ -366,15 +552,13 @@ | |
| 366 | ** |
| 367 | ** The wiki page title is fossil-encoded. There may be no more than |
| 368 | ** one L line. |
| 369 | */ |
| 370 | case 'L': { |
| 371 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 372 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 373 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 374 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 375 | p->zWikiTitle = blob_terminate(&a1); |
| 376 | defossilize(p->zWikiTitle); |
| 377 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 378 | goto manifest_syntax_error; |
| 379 | } |
| 380 | break; |
| @@ -385,21 +569,18 @@ | |
| 385 | ** |
| 386 | ** An M-line identifies another artifact by its UUID. M-lines |
| 387 | ** occur in clusters only. |
| 388 | */ |
| 389 | case 'M': { |
| 390 | char *zUuid; |
| 391 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 392 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 393 | zUuid = blob_terminate(&a1); |
| 394 | if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; |
| 395 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 396 | if( p->nCChild>=p->nCChildAlloc ){ |
| 397 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 398 | p->azCChild = |
| 399 | realloc(p->azCChild, p->nCChildAlloc*sizeof(p->azCChild[0]) ); |
| 400 | if( p->azCChild==0 ) fossil_panic("out of memory"); |
| 401 | } |
| 402 | i = p->nCChild++; |
| 403 | p->azCChild[i] = zUuid; |
| 404 | if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 405 | goto manifest_syntax_error; |
| @@ -413,20 +594,17 @@ | |
| 413 | ** Specify one or more other artifacts where are the parents of |
| 414 | ** this artifact. The first parent is the primary parent. All |
| 415 | ** others are parents by merge. |
| 416 | */ |
| 417 | case 'P': { |
| 418 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 419 | while( blob_token(&line, &a1) ){ |
| 420 | char *zUuid; |
| 421 | if( blob_size(&a1)!=UUID_SIZE ) goto manifest_syntax_error; |
| 422 | zUuid = blob_terminate(&a1); |
| 423 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 424 | if( p->nParent>=p->nParentAlloc ){ |
| 425 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 426 | p->azParent = realloc(p->azParent, p->nParentAlloc*sizeof(char*)); |
| 427 | if( p->azParent==0 ) fossil_panic("out of memory"); |
| 428 | } |
| 429 | i = p->nParent++; |
| 430 | p->azParent[i] = zUuid; |
| 431 | } |
| 432 | break; |
| @@ -437,16 +615,13 @@ | |
| 437 | ** |
| 438 | ** Specify the MD5 checksum over the name and content of all files |
| 439 | ** in the manifest. |
| 440 | */ |
| 441 | case 'R': { |
| 442 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 443 | if( p->zRepoCksum!=0 ) goto manifest_syntax_error; |
| 444 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 445 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 446 | if( blob_size(&a1)!=32 ) goto manifest_syntax_error; |
| 447 | p->zRepoCksum = blob_terminate(&a1); |
| 448 | if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; |
| 449 | break; |
| 450 | } |
| 451 | |
| 452 | /* |
| @@ -463,29 +638,20 @@ | |
| 463 | ** the tag is really a property with the given value. |
| 464 | ** |
| 465 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 466 | */ |
| 467 | case 'T': { |
| 468 | char *zName, *zUuid, *zValue; |
| 469 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 470 | if( blob_token(&line, &a1)==0 ){ |
| 471 | goto manifest_syntax_error; |
| 472 | } |
| 473 | if( blob_token(&line, &a2)==0 ){ |
| 474 | goto manifest_syntax_error; |
| 475 | } |
| 476 | zName = blob_terminate(&a1); |
| 477 | zUuid = blob_terminate(&a2); |
| 478 | if( blob_token(&line, &a3)==0 ){ |
| 479 | zValue = 0; |
| 480 | }else{ |
| 481 | zValue = blob_terminate(&a3); |
| 482 | defossilize(zValue); |
| 483 | } |
| 484 | if( blob_size(&a2)==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ |
| 485 | /* A valid uuid */ |
| 486 | }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){ |
| 487 | zUuid = 0; |
| 488 | }else{ |
| 489 | goto manifest_syntax_error; |
| 490 | } |
| 491 | defossilize(zName); |
| @@ -496,12 +662,11 @@ | |
| 496 | /* Do not allow tags whose names look like UUIDs */ |
| 497 | goto manifest_syntax_error; |
| 498 | } |
| 499 | if( p->nTag>=p->nTagAlloc ){ |
| 500 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 501 | p->aTag = realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
| 502 | if( p->aTag==0 ) fossil_panic("out of memory"); |
| 503 | } |
| 504 | i = p->nTag++; |
| 505 | p->aTag[i].zName = zName; |
| 506 | p->aTag[i].zUuid = zUuid; |
| 507 | p->aTag[i].zValue = zValue; |
| @@ -517,19 +682,17 @@ | |
| 517 | ** Identify the user who created this control file by their |
| 518 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 519 | ** If the user name is omitted, take that to be "anonymous". |
| 520 | */ |
| 521 | case 'U': { |
| 522 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 523 | if( p->zUser!=0 ) goto manifest_syntax_error; |
| 524 | if( blob_token(&line, &a1)==0 ){ |
| 525 | p->zUser = "anonymous"; |
| 526 | }else{ |
| 527 | p->zUser = blob_terminate(&a1); |
| 528 | defossilize(p->zUser); |
| 529 | } |
| 530 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 531 | break; |
| 532 | } |
| 533 | |
| 534 | /* |
| 535 | ** W <size> |
| @@ -537,26 +700,28 @@ | |
| 537 | ** The next <size> bytes of the file contain the text of the wiki |
| 538 | ** page. There is always an extra \n before the start of the next |
| 539 | ** record. |
| 540 | */ |
| 541 | case 'W': { |
| 542 | int size; |
| 543 | Blob wiki; |
| 544 | md5sum_step_text(blob_buffer(&line), blob_size(&line)); |
| 545 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 546 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 547 | if( !blob_is_int(&a1, &size) ) goto manifest_syntax_error; |
| 548 | if( size<0 ) goto manifest_syntax_error; |
| 549 | if( p->zWiki!=0 ) goto manifest_syntax_error; |
| 550 | blob_zero(&wiki); |
| 551 | if( blob_extract(pContent, size+1, &wiki)!=size+1 ){ |
| 552 | goto manifest_syntax_error; |
| 553 | } |
| 554 | p->zWiki = blob_buffer(&wiki); |
| 555 | md5sum_step_text(p->zWiki, size+1); |
| 556 | if( p->zWiki[size]!='\n' ) goto manifest_syntax_error; |
| 557 | p->zWiki[size] = 0; |
| 558 | break; |
| 559 | } |
| 560 | |
| 561 | |
| 562 | /* |
| @@ -569,31 +734,24 @@ | |
| 569 | ** This card is required for all control file types except for |
| 570 | ** Manifest. It is not required for manifest only for historical |
| 571 | ** compatibility reasons. |
| 572 | */ |
| 573 | case 'Z': { |
| 574 | int rc; |
| 575 | Blob hash; |
| 576 | if( blob_token(&line, &a1)==0 ) goto manifest_syntax_error; |
| 577 | if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error; |
| 578 | if( blob_size(&a1)!=32 ) goto manifest_syntax_error; |
| 579 | if( !validate16(blob_buffer(&a1), 32) ) goto manifest_syntax_error; |
| 580 | md5sum_finish(&hash); |
| 581 | rc = blob_compare(&hash, &a1); |
| 582 | blob_reset(&hash); |
| 583 | if( rc!=0 ) goto manifest_syntax_error; |
| 584 | seenZ = 1; |
| 585 | break; |
| 586 | } |
| 587 | default: { |
| 588 | goto manifest_syntax_error; |
| 589 | } |
| 590 | } |
| 591 | } |
| 592 | if( !seenHeader ) goto manifest_syntax_error; |
| 593 | |
| 594 | if( p->nFile>0 || p->zRepoCksum!=0 ){ |
| 595 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 596 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 597 | if( p->nField>0 ) goto manifest_syntax_error; |
| 598 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 599 | if( p->zWiki ) goto manifest_syntax_error; |
| @@ -674,36 +832,194 @@ | |
| 674 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 675 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 676 | p->type = CFTYPE_MANIFEST; |
| 677 | } |
| 678 | md5sum_init(); |
| 679 | return 1; |
| 680 | |
| 681 | manifest_syntax_error: |
| 682 | /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ |
| 683 | md5sum_init(); |
| 684 | manifest_clear(p); |
| 685 | return 0; |
| 686 | } |
| 687 | |
| 688 | /* |
| 689 | ** COMMAND: test-parse-manifest |
| 690 | ** |
| 691 | ** Usage: %fossil test-parse-manifest FILENAME |
| 692 | ** |
| 693 | ** Parse the manifest and discarded. Use for testing only. |
| 694 | */ |
| 695 | void manifest_test_parse_cmd(void){ |
| 696 | Manifest m; |
| 697 | Blob b; |
| 698 | if( g.argc!=3 ){ |
| 699 | usage("FILENAME"); |
| 700 | } |
| 701 | db_must_be_within_tree(); |
| 702 | blob_read_from_file(&b, g.argv[2]); |
| 703 | manifest_parse(&m, &b); |
| 704 | manifest_clear(&m); |
| 705 | } |
| 706 | |
| 707 | /* |
| 708 | ** Translate a filename into a filename-id (fnid). Create a new fnid |
| 709 | ** if no previously exists. |
| @@ -731,14 +1047,14 @@ | |
| 731 | ** Add a single entry to the mlink table. Also add the filename to |
| 732 | ** the filename table if it is not there already. |
| 733 | */ |
| 734 | static void add_one_mlink( |
| 735 | int mid, /* The record ID of the manifest */ |
| 736 | const char *zFromUuid, /* UUID for the mlink.pid field */ |
| 737 | const char *zToUuid, /* UUID for the mlink.fid field */ |
| 738 | const char *zFilename, /* Filename */ |
| 739 | const char *zPrior /* Previous filename. NULL if unchanged */ |
| 740 | ){ |
| 741 | int fnid, pfnid, pid, fid; |
| 742 | static Stmt s1; |
| 743 | |
| 744 | fnid = filename_to_fnid(zFilename); |
| @@ -745,16 +1061,16 @@ | |
| 745 | if( zPrior==0 ){ |
| 746 | pfnid = 0; |
| 747 | }else{ |
| 748 | pfnid = filename_to_fnid(zPrior); |
| 749 | } |
| 750 | if( zFromUuid==0 ){ |
| 751 | pid = 0; |
| 752 | }else{ |
| 753 | pid = uuid_to_rid(zFromUuid, 1); |
| 754 | } |
| 755 | if( zToUuid==0 ){ |
| 756 | fid = 0; |
| 757 | }else{ |
| 758 | fid = uuid_to_rid(zToUuid, 1); |
| 759 | } |
| 760 | db_static_prepare(&s1, |
| @@ -771,33 +1087,83 @@ | |
| 771 | content_deltify(pid, fid, 0); |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | /* |
| 776 | ** Locate a file named zName in the aFile[] array of the given |
| 777 | ** manifest. We assume that filenames are in sorted order. |
| 778 | ** Use a binary search. Return turn the index of the matching |
| 779 | ** entry. Or return -1 if not found. |
| 780 | */ |
| 781 | static int find_file_in_manifest(Manifest *p, const char *zName){ |
| 782 | int lwr, upr; |
| 783 | int c; |
| 784 | int i; |
| 785 | lwr = 0; |
| 786 | upr = p->nFile - 1; |
| 787 | while( lwr<=upr ){ |
| 788 | i = (lwr+upr)/2; |
| 789 | c = strcmp(p->aFile[i].zName, zName); |
| 790 | if( c<0 ){ |
| 791 | lwr = i+1; |
| 792 | }else if( c>0 ){ |
| 793 | upr = i-1; |
| 794 | }else{ |
| 795 | return i; |
| 796 | } |
| 797 | } |
| 798 | return -1; |
| 799 | } |
| 800 | |
| 801 | /* |
| 802 | ** Add mlink table entries associated with manifest cid. The |
| 803 | ** parent manifest is pid. |
| @@ -808,85 +1174,76 @@ | |
| 808 | ** Deleted files have mlink.fid=0. |
| 809 | ** Added files have mlink.pid=0. |
| 810 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 811 | */ |
| 812 | static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ |
| 813 | Manifest other; |
| 814 | Blob otherContent; |
| 815 | int i, j; |
| 816 | |
| 817 | if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", cid) ){ |
| 818 | return; |
| 819 | } |
| 820 | assert( pParent==0 || pChild==0 ); |
| 821 | if( pParent==0 ){ |
| 822 | pParent = &other; |
| 823 | content_get(pid, &otherContent); |
| 824 | }else{ |
| 825 | pChild = &other; |
| 826 | content_get(cid, &otherContent); |
| 827 | } |
| 828 | if( blob_size(&otherContent)==0 ) return; |
| 829 | if( manifest_parse(&other, &otherContent)==0 ) return; |
| 830 | content_deltify(pid, cid, 0); |
| 831 | |
| 832 | /* Use the iRename fields to find the cross-linkage between |
| 833 | ** renamed files. */ |
| 834 | for(j=0; j<pChild->nFile; j++){ |
| 835 | const char *zPrior = pChild->aFile[j].zPrior; |
| 836 | if( zPrior && zPrior[0] ){ |
| 837 | i = find_file_in_manifest(pParent, zPrior); |
| 838 | if( i>=0 ){ |
| 839 | pChild->aFile[j].iRename = i; |
| 840 | pParent->aFile[i].iRename = j; |
| 841 | } |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | /* Construct the mlink entries */ |
| 846 | for(i=j=0; i<pParent->nFile && j<pChild->nFile; ){ |
| 847 | int c; |
| 848 | if( pParent->aFile[i].iRename>=0 ){ |
| 849 | i++; |
| 850 | }else if( (c = strcmp(pParent->aFile[i].zName, pChild->aFile[j].zName))<0 ){ |
| 851 | add_one_mlink(cid, pParent->aFile[i].zUuid,0,pParent->aFile[i].zName,0); |
| 852 | i++; |
| 853 | }else if( c>0 ){ |
| 854 | int rn = pChild->aFile[j].iRename; |
| 855 | if( rn>=0 ){ |
| 856 | add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, |
| 857 | pChild->aFile[j].zName, pParent->aFile[rn].zName); |
| 858 | }else{ |
| 859 | add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0); |
| 860 | } |
| 861 | j++; |
| 862 | }else{ |
| 863 | if( strcmp(pParent->aFile[i].zUuid, pChild->aFile[j].zUuid)!=0 ){ |
| 864 | add_one_mlink(cid, pParent->aFile[i].zUuid, pChild->aFile[j].zUuid, |
| 865 | pChild->aFile[j].zName, 0); |
| 866 | } |
| 867 | i++; |
| 868 | j++; |
| 869 | } |
| 870 | } |
| 871 | while( i<pParent->nFile ){ |
| 872 | if( pParent->aFile[i].iRename<0 ){ |
| 873 | add_one_mlink(cid, pParent->aFile[i].zUuid, 0, pParent->aFile[i].zName,0); |
| 874 | } |
| 875 | i++; |
| 876 | } |
| 877 | while( j<pChild->nFile ){ |
| 878 | int rn = pChild->aFile[j].iRename; |
| 879 | if( rn>=0 ){ |
| 880 | add_one_mlink(cid, pParent->aFile[rn].zUuid, pChild->aFile[j].zUuid, |
| 881 | pChild->aFile[j].zName, pParent->aFile[rn].zName); |
| 882 | }else{ |
| 883 | add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0); |
| 884 | } |
| 885 | j++; |
| 886 | } |
| 887 | manifest_clear(&other); |
| 888 | } |
| 889 | |
| 890 | /* |
| 891 | ** True if manifest_crosslink_begin() has been called but |
| 892 | ** manifest_crosslink_end() is still pending. |
| @@ -1023,38 +1380,44 @@ | |
| 1023 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1024 | ** file, is a legacy of its original use. |
| 1025 | */ |
| 1026 | int manifest_crosslink(int rid, Blob *pContent){ |
| 1027 | int i; |
| 1028 | Manifest m; |
| 1029 | Stmt q; |
| 1030 | int parentid = 0; |
| 1031 | |
| 1032 | if( manifest_parse(&m, pContent)==0 ){ |
| 1033 | return 0; |
| 1034 | } |
| 1035 | if( g.xlinkClusterOnly && m.type!=CFTYPE_CLUSTER ){ |
| 1036 | manifest_clear(&m); |
| 1037 | return 0; |
| 1038 | } |
| 1039 | db_begin_transaction(); |
| 1040 | if( m.type==CFTYPE_MANIFEST ){ |
| 1041 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1042 | char *zCom; |
| 1043 | for(i=0; i<m.nParent; i++){ |
| 1044 | int pid = uuid_to_rid(m.azParent[i], 1); |
| 1045 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| 1046 | "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, m.rDate); |
| 1047 | if( i==0 ){ |
| 1048 | add_mlink(pid, 0, rid, &m); |
| 1049 | parentid = pid; |
| 1050 | } |
| 1051 | } |
| 1052 | db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); |
| 1053 | while( db_step(&q)==SQLITE_ROW ){ |
| 1054 | int cid = db_column_int(&q, 0); |
| 1055 | add_mlink(rid, &m, cid, 0); |
| 1056 | } |
| 1057 | db_finalize(&q); |
| 1058 | db_multi_exec( |
| 1059 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1060 | "bgcolor,euser,ecomment)" |
| @@ -1065,118 +1428,134 @@ | |
| 1065 | " )," |
| 1066 | " %d,%Q,%Q," |
| 1067 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0)," |
| 1068 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1069 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1070 | TAG_DATE, rid, m.rDate, |
| 1071 | rid, m.zUser, m.zComment, |
| 1072 | TAG_BGCOLOR, rid, |
| 1073 | TAG_USER, rid, |
| 1074 | TAG_COMMENT, rid |
| 1075 | ); |
| 1076 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 1077 | " WHERE rowid=last_insert_rowid()"); |
| 1078 | wiki_extract_links(zCom, rid, 0, m.rDate, 1, WIKI_INLINE); |
| 1079 | free(zCom); |
| 1080 | } |
| 1081 | } |
| 1082 | if( m.type==CFTYPE_CLUSTER ){ |
| 1083 | tag_insert("cluster", 1, 0, rid, m.rDate, rid); |
| 1084 | for(i=0; i<m.nCChild; i++){ |
| 1085 | int mid; |
| 1086 | mid = uuid_to_rid(m.azCChild[i], 1); |
| 1087 | if( mid>0 ){ |
| 1088 | db_multi_exec("DELETE FROM unclustered WHERE rid=%d", mid); |
| 1089 | } |
| 1090 | } |
| 1091 | } |
| 1092 | if( m.type==CFTYPE_CONTROL |
| 1093 | || m.type==CFTYPE_MANIFEST |
| 1094 | || m.type==CFTYPE_EVENT |
| 1095 | ){ |
| 1096 | for(i=0; i<m.nTag; i++){ |
| 1097 | int tid; |
| 1098 | int type; |
| 1099 | if( m.aTag[i].zUuid ){ |
| 1100 | tid = uuid_to_rid(m.aTag[i].zUuid, 1); |
| 1101 | }else{ |
| 1102 | tid = rid; |
| 1103 | } |
| 1104 | if( tid ){ |
| 1105 | switch( m.aTag[i].zName[0] ){ |
| 1106 | case '-': type = 0; break; /* Cancel prior occurances */ |
| 1107 | case '+': type = 1; break; /* Apply to target only */ |
| 1108 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1109 | default: |
| 1110 | fossil_fatal("unknown tag type in manifest: %s", m.aTag); |
| 1111 | return 0; |
| 1112 | } |
| 1113 | tag_insert(&m.aTag[i].zName[1], type, m.aTag[i].zValue, |
| 1114 | rid, m.rDate, tid); |
| 1115 | } |
| 1116 | } |
| 1117 | if( parentid ){ |
| 1118 | tag_propagate_all(parentid); |
| 1119 | } |
| 1120 | } |
| 1121 | if( m.type==CFTYPE_WIKI ){ |
| 1122 | char *zTag = mprintf("wiki-%s", m.zWikiTitle); |
| 1123 | int tagid = tag_findid(zTag, 1); |
| 1124 | int prior; |
| 1125 | char *zComment; |
| 1126 | int nWiki; |
| 1127 | char zLength[40]; |
| 1128 | while( isspace(m.zWiki[0]) ) m.zWiki++; |
| 1129 | nWiki = strlen(m.zWiki); |
| 1130 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1131 | tag_insert(zTag, 1, zLength, rid, m.rDate, rid); |
| 1132 | free(zTag); |
| 1133 | prior = db_int(0, |
| 1134 | "SELECT rid FROM tagxref" |
| 1135 | " WHERE tagid=%d AND mtime<%.17g" |
| 1136 | " ORDER BY mtime DESC", |
| 1137 | tagid, m.rDate |
| 1138 | ); |
| 1139 | if( prior ){ |
| 1140 | content_deltify(prior, rid, 0); |
| 1141 | } |
| 1142 | if( nWiki>0 ){ |
| 1143 | zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle); |
| 1144 | }else{ |
| 1145 | zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle); |
| 1146 | } |
| 1147 | db_multi_exec( |
| 1148 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1149 | " bgcolor,euser,ecomment)" |
| 1150 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 1151 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| 1152 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1153 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1154 | m.rDate, rid, m.zUser, zComment, |
| 1155 | TAG_BGCOLOR, rid, |
| 1156 | TAG_BGCOLOR, rid, |
| 1157 | TAG_USER, rid, |
| 1158 | TAG_COMMENT, rid |
| 1159 | ); |
| 1160 | free(zComment); |
| 1161 | } |
| 1162 | if( m.type==CFTYPE_EVENT ){ |
| 1163 | char *zTag = mprintf("event-%s", m.zEventId); |
| 1164 | int tagid = tag_findid(zTag, 1); |
| 1165 | int prior, subsequent; |
| 1166 | int nWiki; |
| 1167 | char zLength[40]; |
| 1168 | while( isspace(m.zWiki[0]) ) m.zWiki++; |
| 1169 | nWiki = strlen(m.zWiki); |
| 1170 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1171 | tag_insert(zTag, 1, zLength, rid, m.rDate, rid); |
| 1172 | free(zTag); |
| 1173 | prior = db_int(0, |
| 1174 | "SELECT rid FROM tagxref" |
| 1175 | " WHERE tagid=%d AND mtime<%.17g" |
| 1176 | " ORDER BY mtime DESC", |
| 1177 | tagid, m.rDate |
| 1178 | ); |
| 1179 | if( prior ){ |
| 1180 | content_deltify(prior, rid, 0); |
| 1181 | db_multi_exec( |
| 1182 | "DELETE FROM event" |
| @@ -1188,104 +1567,87 @@ | |
| 1188 | } |
| 1189 | subsequent = db_int(0, |
| 1190 | "SELECT rid FROM tagxref" |
| 1191 | " WHERE tagid=%d AND mtime>%.17g" |
| 1192 | " ORDER BY mtime", |
| 1193 | tagid, m.rDate |
| 1194 | ); |
| 1195 | if( subsequent ){ |
| 1196 | content_deltify(rid, subsequent, 0); |
| 1197 | }else{ |
| 1198 | db_multi_exec( |
| 1199 | "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" |
| 1200 | "VALUES('e',%.17g,%d,%d,%Q,%Q," |
| 1201 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1202 | m.rEventDate, rid, tagid, m.zUser, m.zComment, |
| 1203 | TAG_BGCOLOR, rid |
| 1204 | ); |
| 1205 | } |
| 1206 | } |
| 1207 | if( m.type==CFTYPE_TICKET ){ |
| 1208 | char *zTag; |
| 1209 | |
| 1210 | assert( manifest_crosslink_busy==1 ); |
| 1211 | zTag = mprintf("tkt-%s", m.zTicketUuid); |
| 1212 | tag_insert(zTag, 1, 0, rid, m.rDate, rid); |
| 1213 | free(zTag); |
| 1214 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| 1215 | m.zTicketUuid); |
| 1216 | } |
| 1217 | if( m.type==CFTYPE_ATTACHMENT ){ |
| 1218 | db_multi_exec( |
| 1219 | "INSERT INTO attachment(attachid, mtime, src, target," |
| 1220 | "filename, comment, user)" |
| 1221 | "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", |
| 1222 | rid, m.rDate, m.zAttachSrc, m.zAttachTarget, m.zAttachName, |
| 1223 | (m.zComment ? m.zComment : ""), m.zUser |
| 1224 | ); |
| 1225 | db_multi_exec( |
| 1226 | "UPDATE attachment SET isLatest = (mtime==" |
| 1227 | "(SELECT max(mtime) FROM attachment" |
| 1228 | " WHERE target=%Q AND filename=%Q))" |
| 1229 | " WHERE target=%Q AND filename=%Q", |
| 1230 | m.zAttachTarget, m.zAttachName, |
| 1231 | m.zAttachTarget, m.zAttachName |
| 1232 | ); |
| 1233 | if( strlen(m.zAttachTarget)!=UUID_SIZE |
| 1234 | || !validate16(m.zAttachTarget, UUID_SIZE) |
| 1235 | ){ |
| 1236 | char *zComment; |
| 1237 | if( m.zAttachSrc && m.zAttachSrc[0] ){ |
| 1238 | zComment = mprintf("Add attachment \"%h\" to wiki page [%h]", |
| 1239 | m.zAttachName, m.zAttachTarget); |
| 1240 | }else{ |
| 1241 | zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", |
| 1242 | m.zAttachName, m.zAttachTarget); |
| 1243 | } |
| 1244 | db_multi_exec( |
| 1245 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1246 | "VALUES('w',%.17g,%d,%Q,%Q)", |
| 1247 | m.rDate, rid, m.zUser, zComment |
| 1248 | ); |
| 1249 | free(zComment); |
| 1250 | }else{ |
| 1251 | char *zComment; |
| 1252 | if( m.zAttachSrc && m.zAttachSrc[0] ){ |
| 1253 | zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]", |
| 1254 | m.zAttachName, m.zAttachTarget); |
| 1255 | }else{ |
| 1256 | zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]", |
| 1257 | m.zAttachName, m.zAttachTarget); |
| 1258 | } |
| 1259 | db_multi_exec( |
| 1260 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1261 | "VALUES('t',%.17g,%d,%Q,%Q)", |
| 1262 | m.rDate, rid, m.zUser, zComment |
| 1263 | ); |
| 1264 | free(zComment); |
| 1265 | } |
| 1266 | } |
| 1267 | db_end_transaction(0); |
| 1268 | manifest_clear(&m); |
| 1269 | return 1; |
| 1270 | } |
| 1271 | |
| 1272 | /* |
| 1273 | ** Given a checkin name, load and parse the manifest for that checkin. |
| 1274 | ** Throw a fatal error if anything goes wrong. |
| 1275 | */ |
| 1276 | void manifest_from_name( |
| 1277 | const char *zName, |
| 1278 | Manifest *pM |
| 1279 | ){ |
| 1280 | int rid; |
| 1281 | Blob content; |
| 1282 | |
| 1283 | rid = name_to_rid(zName); |
| 1284 | if( !is_a_version(rid) ){ |
| 1285 | fossil_fatal("no such checkin: %s", zName); |
| 1286 | } |
| 1287 | content_get(rid, &content); |
| 1288 | if( !manifest_parse(pM, &content) ){ |
| 1289 | fossil_fatal("cannot parse manifest for checkin: %s", zName); |
| 1290 | } |
| 1291 | } |
| 1292 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -26,24 +26,39 @@ | |
| 26 | |
| 27 | #if INTERFACE |
| 28 | /* |
| 29 | ** Types of control files |
| 30 | */ |
| 31 | #define CFTYPE_ANY 0 |
| 32 | #define CFTYPE_MANIFEST 1 |
| 33 | #define CFTYPE_CLUSTER 2 |
| 34 | #define CFTYPE_CONTROL 3 |
| 35 | #define CFTYPE_WIKI 4 |
| 36 | #define CFTYPE_TICKET 5 |
| 37 | #define CFTYPE_ATTACHMENT 6 |
| 38 | #define CFTYPE_EVENT 7 |
| 39 | |
| 40 | /* |
| 41 | ** A single F-card within a manifest |
| 42 | */ |
| 43 | struct ManifestFile { |
| 44 | char *zName; /* Name of a file */ |
| 45 | char *zUuid; /* UUID of the file */ |
| 46 | char *zPerm; /* File permissions */ |
| 47 | char *zPrior; /* Prior name if the name was changed */ |
| 48 | }; |
| 49 | |
| 50 | |
| 51 | /* |
| 52 | ** A parsed manifest or cluster. |
| 53 | */ |
| 54 | struct Manifest { |
| 55 | Blob content; /* The original content blob */ |
| 56 | int type; /* Type of artifact. One of CFTYPE_xxxxx */ |
| 57 | int rid; /* The blob-id for this manifest */ |
| 58 | char *zBaseline; /* Baseline manifest. The B card. */ |
| 59 | Manifest *pBaseline; /* The actual baseline manifest */ |
| 60 | char *zComment; /* Decoded comment. The C card. */ |
| 61 | double rDate; /* Date and time from D card. 0.0 if no D card. */ |
| 62 | char *zUser; /* Name of the user from the U card. */ |
| 63 | char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ |
| 64 | char *zWiki; /* Text of the wiki page. W card. */ |
| @@ -54,17 +69,12 @@ | |
| 69 | char *zAttachName; /* Filename of an attachment. A card. */ |
| 70 | char *zAttachSrc; /* UUID of document being attached. A card. */ |
| 71 | char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ |
| 72 | int nFile; /* Number of F cards */ |
| 73 | int nFileAlloc; /* Slots allocated in aFile[] */ |
| 74 | int iFile; /* Index of current file in iterator */ |
| 75 | ManifestFile *aFile; /* One entry for each F-card */ |
| 76 | int nParent; /* Number of parents. */ |
| 77 | int nParentAlloc; /* Slots allocated in azParent[] */ |
| 78 | char **azParent; /* UUIDs of parents. One for each P card argument */ |
| 79 | int nCChild; /* Number of cluster children */ |
| 80 | int nCChildAlloc; /* Number of closts allocated in azCChild[] */ |
| @@ -83,22 +93,192 @@ | |
| 93 | char *zValue; /* Value of the field */ |
| 94 | } *aField; /* One for each J card */ |
| 95 | }; |
| 96 | #endif |
| 97 | |
| 98 | /* |
| 99 | ** A cache of parsed manifests. This reduces the number of |
| 100 | ** calls to manifest_parse() when doing a rebuild. |
| 101 | */ |
| 102 | #define MX_MANIFEST_CACHE 6 |
| 103 | static struct { |
| 104 | int nxAge; |
| 105 | int aAge[MX_MANIFEST_CACHE]; |
| 106 | Manifest *apManifest[MX_MANIFEST_CACHE]; |
| 107 | } manifestCache; |
| 108 | |
| 109 | |
| 110 | /* |
| 111 | ** Clear the memory allocated in a manifest object |
| 112 | */ |
| 113 | void manifest_destroy(Manifest *p){ |
| 114 | if( p ){ |
| 115 | blob_reset(&p->content); |
| 116 | free(p->aFile); |
| 117 | free(p->azParent); |
| 118 | free(p->azCChild); |
| 119 | free(p->aTag); |
| 120 | free(p->aField); |
| 121 | if( p->pBaseline ) manifest_destroy(p->pBaseline); |
| 122 | fossil_free(p); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /* |
| 127 | ** Add an element to the manifest cache using LRU replacement. |
| 128 | */ |
| 129 | void manifest_cache_insert(Manifest *p){ |
| 130 | while( p ){ |
| 131 | int i; |
| 132 | Manifest *pBaseline = p->pBaseline; |
| 133 | p->pBaseline = 0; |
| 134 | for(i=0; i<MX_MANIFEST_CACHE; i++){ |
| 135 | if( manifestCache.apManifest[i]==0 ) break; |
| 136 | } |
| 137 | if( i>=MX_MANIFEST_CACHE ){ |
| 138 | int oldest = 0; |
| 139 | int oldestAge = manifestCache.aAge[0]; |
| 140 | for(i=1; i<MX_MANIFEST_CACHE; i++){ |
| 141 | if( manifestCache.aAge[i]<oldestAge ){ |
| 142 | oldest = i; |
| 143 | oldestAge = manifestCache.aAge[i]; |
| 144 | } |
| 145 | } |
| 146 | manifest_destroy(manifestCache.apManifest[oldest]); |
| 147 | i = oldest; |
| 148 | } |
| 149 | manifestCache.aAge[i] = ++manifestCache.nxAge; |
| 150 | manifestCache.apManifest[i] = p; |
| 151 | p = pBaseline; |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /* |
| 156 | ** Try to extract a line from the manifest cache. Return 1 if found. |
| 157 | ** Return 0 if not found. |
| 158 | */ |
| 159 | static Manifest *manifest_cache_find(int rid){ |
| 160 | int i; |
| 161 | Manifest *p; |
| 162 | for(i=0; i<MX_MANIFEST_CACHE; i++){ |
| 163 | if( manifestCache.apManifest[i] && manifestCache.apManifest[i]->rid==rid ){ |
| 164 | p = manifestCache.apManifest[i]; |
| 165 | manifestCache.apManifest[i] = 0; |
| 166 | return p; |
| 167 | } |
| 168 | } |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | ** Clear the manifest cache. |
| 174 | */ |
| 175 | void manifest_cache_clear(void){ |
| 176 | int i; |
| 177 | for(i=0; i<MX_MANIFEST_CACHE; i++){ |
| 178 | if( manifestCache.apManifest[i] ){ |
| 179 | manifest_destroy(manifestCache.apManifest[i]); |
| 180 | } |
| 181 | } |
| 182 | memset(&manifestCache, 0, sizeof(manifestCache)); |
| 183 | } |
| 184 | |
| 185 | #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM |
| 186 | # define md5sum_init(X) |
| 187 | # define md5sum_step_text(X,Y) |
| 188 | #endif |
| 189 | |
| 190 | /* |
| 191 | ** Remove the PGP signature from the artifact, if there is one. |
| 192 | */ |
| 193 | static void remove_pgp_signature(char **pz, int *pn){ |
| 194 | char *z = *pz; |
| 195 | int n = *pn; |
| 196 | int i; |
| 197 | if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; |
| 198 | for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){} |
| 199 | if( i>=n ) return; |
| 200 | z += i; |
| 201 | n -= i; |
| 202 | *pz = z; |
| 203 | for(i=n-1; i>=0; i--){ |
| 204 | if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ |
| 205 | n = i+1; |
| 206 | break; |
| 207 | } |
| 208 | } |
| 209 | *pn = n; |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | /* |
| 214 | ** Verify the Z-card checksum on the artifact, if there is such a |
| 215 | ** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card |
| 216 | ** exists and is correct. Return 2 if the Z-card exists and has the wrong |
| 217 | ** value. |
| 218 | ** |
| 219 | ** 0123456789 123456789 123456789 123456789 |
| 220 | ** Z aea84f4f863865a8d59d0384e4d2a41c |
| 221 | */ |
| 222 | static int verify_z_card(const char *z, int n){ |
| 223 | if( n<35 ) return 0; |
| 224 | if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0; |
| 225 | md5sum_init(); |
| 226 | md5sum_step_text(z, n-35); |
| 227 | if( memcmp(&z[n-33], md5sum_finish(0), 32)==0 ){ |
| 228 | return 1; |
| 229 | }else{ |
| 230 | return 2; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | ** A structure used for rapid parsing of the Manifest file |
| 236 | */ |
| 237 | typedef struct ManifestText ManifestText; |
| 238 | struct ManifestText { |
| 239 | char *z; /* The first character of the next token */ |
| 240 | char *zEnd; /* One character beyond the end of the manifest */ |
| 241 | int atEol; /* True if z points to the start of a new line */ |
| 242 | }; |
| 243 | |
| 244 | /* |
| 245 | ** Return a pointer to the next token. The token is zero-terminated. |
| 246 | ** Return NULL if there are no more tokens on the current line. |
| 247 | */ |
| 248 | static char *next_token(ManifestText *p, int *pLen){ |
| 249 | char *z; |
| 250 | char *zStart; |
| 251 | int c; |
| 252 | if( p->atEol ) return 0; |
| 253 | zStart = z = p->z; |
| 254 | while( (c=(*z))!=' ' && c!='\n' ){ z++; } |
| 255 | *z = 0; |
| 256 | p->z = &z[1]; |
| 257 | p->atEol = c=='\n'; |
| 258 | if( pLen ) *pLen = z - zStart; |
| 259 | return zStart; |
| 260 | } |
| 261 | |
| 262 | /* |
| 263 | ** Return the card-type for the next card. Or, return 0 if there are no |
| 264 | ** more cards or if we are not at the end of the current card. |
| 265 | */ |
| 266 | static char next_card(ManifestText *p){ |
| 267 | char c; |
| 268 | if( !p->atEol || p->z>=p->zEnd ) return 0; |
| 269 | c = p->z[0]; |
| 270 | if( p->z[1]==' ' ){ |
| 271 | p->z += 2; |
| 272 | p->atEol = 0; |
| 273 | }else if( p->z[1]=='\n' ){ |
| 274 | p->z += 2; |
| 275 | p->atEol = 1; |
| 276 | }else{ |
| 277 | c = 0; |
| 278 | } |
| 279 | return c; |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** Parse a blob into a Manifest object. The Manifest object |
| 284 | ** takes over the input blob and will free it when the |
| @@ -124,48 +304,66 @@ | |
| 304 | ** Each card is divided into tokens by a single space character. |
| 305 | ** The first token is a single upper-case letter which is the card type. |
| 306 | ** The card type determines the other parameters to the card. |
| 307 | ** Cards must occur in lexicographical order. |
| 308 | */ |
| 309 | static Manifest *manifest_parse(Blob *pContent, int rid){ |
| 310 | Manifest *p; |
| 311 | int seenZ = 0; |
| 312 | int i, lineNo=0; |
| 313 | ManifestText x; |
| 314 | char cPrevType = 0; |
| 315 | char cType; |
| 316 | char *z; |
| 317 | int n; |
| 318 | char *zUuid; |
| 319 | int sz = 0; |
| 320 | |
| 321 | /* Every control artifact ends with a '\n' character. Exit early |
| 322 | ** if that is not the case for this artifact. |
| 323 | */ |
| 324 | z = blob_buffer(pContent); |
| 325 | n = blob_size(pContent); |
| 326 | if( n<=0 || z[n-1]!='\n' ){ |
| 327 | blob_reset(pContent); |
| 328 | return 0; |
| 329 | } |
| 330 | |
| 331 | /* Strip off the PGP signature if there is one. Then verify the |
| 332 | ** Z-card. |
| 333 | */ |
| 334 | remove_pgp_signature(&z, &n); |
| 335 | if( verify_z_card(z, n)==0 ){ |
| 336 | blob_reset(pContent); |
| 337 | return 0; |
| 338 | } |
| 339 | |
| 340 | /* Verify that the first few characters of the artifact look like |
| 341 | ** a control artifact. |
| 342 | */ |
| 343 | if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){ |
| 344 | blob_reset(pContent); |
| 345 | return 0; |
| 346 | } |
| 347 | |
| 348 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 349 | */ |
| 350 | p = fossil_malloc( sizeof(*p) ); |
| 351 | memset(p, 0, sizeof(*p)); |
| 352 | memcpy(&p->content, pContent, sizeof(p->content)); |
| 353 | p->rid = rid; |
| 354 | blob_zero(pContent); |
| 355 | pContent = &p->content; |
| 356 | |
| 357 | /* Begin parsing, card by card. |
| 358 | */ |
| 359 | x.z = z; |
| 360 | x.zEnd = &z[n]; |
| 361 | x.atEol = 1; |
| 362 | while( (cType = next_card(&x))!=0 && cType>=cPrevType ){ |
| 363 | lineNo++; |
| 364 | switch( cType ){ |
| 365 | /* |
| 366 | ** A <filename> <target> ?<source>? |
| 367 | ** |
| 368 | ** Identifies an attachment to either a wiki page or a ticket. |
| 369 | ** <source> is the artifact that is the attachment. <source> |
| @@ -172,50 +370,60 @@ | |
| 370 | ** is omitted to delete an attachment. <target> is the name of |
| 371 | ** a wiki page or ticket to which that attachment is connected. |
| 372 | */ |
| 373 | case 'A': { |
| 374 | char *zName, *zTarget, *zSrc; |
| 375 | int nTarget = 0, nSrc = 0; |
| 376 | zName = next_token(&x, 0); |
| 377 | zTarget = next_token(&x, &nTarget); |
| 378 | zSrc = next_token(&x, &nSrc); |
| 379 | if( zName==0 || zTarget==0 ) goto manifest_syntax_error; |
| 380 | if( p->zAttachName!=0 ) goto manifest_syntax_error; |
| 381 | defossilize(zName); |
| 382 | if( !file_is_simple_pathname(zName) ){ |
| 383 | goto manifest_syntax_error; |
| 384 | } |
| 385 | defossilize(zTarget); |
| 386 | if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
| 387 | && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 388 | goto manifest_syntax_error; |
| 389 | } |
| 390 | if( zSrc && (nSrc!=UUID_SIZE || !validate16(zSrc, UUID_SIZE)) ){ |
| 391 | goto manifest_syntax_error; |
| 392 | } |
| 393 | p->zAttachName = (char*)file_tail(zName); |
| 394 | p->zAttachSrc = zSrc; |
| 395 | p->zAttachTarget = zTarget; |
| 396 | break; |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** B <uuid> |
| 401 | ** |
| 402 | ** A B-line gives the UUID for the baselinen of a delta-manifest. |
| 403 | */ |
| 404 | case 'B': { |
| 405 | if( p->zBaseline ) goto manifest_syntax_error; |
| 406 | p->zBaseline = next_token(&x, &sz); |
| 407 | if( p->zBaseline==0 ) goto manifest_syntax_error; |
| 408 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 409 | if( !validate16(p->zBaseline, UUID_SIZE) ) goto manifest_syntax_error; |
| 410 | break; |
| 411 | } |
| 412 | |
| 413 | |
| 414 | /* |
| 415 | ** C <comment> |
| 416 | ** |
| 417 | ** Comment text is fossil-encoded. There may be no more than |
| 418 | ** one C line. C lines are required for manifests and are |
| 419 | ** disallowed on all other control files. |
| 420 | */ |
| 421 | case 'C': { |
| 422 | if( p->zComment!=0 ) goto manifest_syntax_error; |
| 423 | p->zComment = next_token(&x, 0); |
| 424 | if( p->zComment==0 ) goto manifest_syntax_error; |
| 425 | defossilize(p->zComment); |
| 426 | break; |
| 427 | } |
| 428 | |
| 429 | /* |
| @@ -224,17 +432,13 @@ | |
| 432 | ** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS |
| 433 | ** There can be no more than 1 D line. D lines are required |
| 434 | ** for all control files except for clusters. |
| 435 | */ |
| 436 | case 'D': { |
| 437 | if( p->rDate>0.0 ) goto manifest_syntax_error; |
| 438 | p->rDate = db_double(0.0, "SELECT julianday(%Q)", next_token(&x,0)); |
| 439 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 440 | break; |
| 441 | } |
| 442 | |
| 443 | /* |
| 444 | ** E <timestamp> <uuid> |
| @@ -244,68 +448,57 @@ | |
| 448 | ** The event timestamp is distinct from the D timestamp. The D |
| 449 | ** timestamp is when the artifact was created whereas the E timestamp |
| 450 | ** is when the specific event is said to occur. |
| 451 | */ |
| 452 | case 'E': { |
| 453 | if( p->rEventDate>0.0 ) goto manifest_syntax_error; |
| 454 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 455 | if( p->rEventDate<=0.0 ) goto manifest_syntax_error; |
| 456 | p->zEventId = next_token(&x, &sz); |
| 457 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 458 | if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error; |
| 459 | break; |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? |
| 464 | ** |
| 465 | ** Identifies a file in a manifest. Multiple F lines are |
| 466 | ** allowed in a manifest. F lines are not allowed in any |
| 467 | ** other control file. The filename and old-name are fossil-encoded. |
| 468 | */ |
| 469 | case 'F': { |
| 470 | char *zName, *zPerm, *zPriorName; |
| 471 | zName = next_token(&x,0); |
| 472 | if( zName==0 ) goto manifest_syntax_error; |
| 473 | defossilize(zName); |
| 474 | if( !file_is_simple_pathname(zName) ){ |
| 475 | goto manifest_syntax_error; |
| 476 | } |
| 477 | zUuid = next_token(&x, &sz); |
| 478 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 479 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 480 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 481 | } |
| 482 | zPerm = next_token(&x,0); |
| 483 | zPriorName = next_token(&x,0); |
| 484 | if( zPriorName ){ |
| 485 | defossilize(zPriorName); |
| 486 | if( !file_is_simple_pathname(zPriorName) ){ |
| 487 | goto manifest_syntax_error; |
| 488 | } |
| 489 | } |
| 490 | if( p->nFile>=p->nFileAlloc ){ |
| 491 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 492 | p->aFile = fossil_realloc(p->aFile, |
| 493 | p->nFileAlloc*sizeof(p->aFile[0]) ); |
| 494 | } |
| 495 | i = p->nFile++; |
| 496 | p->aFile[i].zName = zName; |
| 497 | p->aFile[i].zUuid = zUuid; |
| 498 | p->aFile[i].zPerm = zPerm; |
| 499 | p->aFile[i].zPrior = zPriorName; |
| 500 | if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 501 | goto manifest_syntax_error; |
| 502 | } |
| 503 | break; |
| 504 | } |
| @@ -318,22 +511,19 @@ | |
| 511 | ** value. If <value> is omitted then it is understood to be an |
| 512 | ** empty string. |
| 513 | */ |
| 514 | case 'J': { |
| 515 | char *zName, *zValue; |
| 516 | zName = next_token(&x,0); |
| 517 | zValue = next_token(&x,0); |
| 518 | if( zName==0 ) goto manifest_syntax_error; |
| 519 | if( zValue==0 ) zValue = ""; |
| 520 | defossilize(zValue); |
| 521 | if( p->nField>=p->nFieldAlloc ){ |
| 522 | p->nFieldAlloc = p->nFieldAlloc*2 + 10; |
| 523 | p->aField = fossil_realloc(p->aField, |
| 524 | p->nFieldAlloc*sizeof(p->aField[0]) ); |
| 525 | } |
| 526 | i = p->nField++; |
| 527 | p->aField[i].zName = zName; |
| 528 | p->aField[i].zValue = zValue; |
| 529 | if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| @@ -348,18 +538,14 @@ | |
| 538 | ** |
| 539 | ** A K-line gives the UUID for the ticket which this control file |
| 540 | ** is amending. |
| 541 | */ |
| 542 | case 'K': { |
| 543 | if( p->zTicketUuid!=0 ) goto manifest_syntax_error; |
| 544 | p->zTicketUuid = next_token(&x, &sz); |
| 545 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 546 | if( !validate16(p->zTicketUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 547 | break; |
| 548 | } |
| 549 | |
| 550 | /* |
| 551 | ** L <wikititle> |
| @@ -366,15 +552,13 @@ | |
| 552 | ** |
| 553 | ** The wiki page title is fossil-encoded. There may be no more than |
| 554 | ** one L line. |
| 555 | */ |
| 556 | case 'L': { |
| 557 | if( p->zWikiTitle!=0 ) goto manifest_syntax_error; |
| 558 | p->zWikiTitle = next_token(&x,0); |
| 559 | if( p->zWikiTitle==0 ) goto manifest_syntax_error; |
| 560 | defossilize(p->zWikiTitle); |
| 561 | if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ |
| 562 | goto manifest_syntax_error; |
| 563 | } |
| 564 | break; |
| @@ -385,21 +569,18 @@ | |
| 569 | ** |
| 570 | ** An M-line identifies another artifact by its UUID. M-lines |
| 571 | ** occur in clusters only. |
| 572 | */ |
| 573 | case 'M': { |
| 574 | zUuid = next_token(&x, &sz); |
| 575 | if( zUuid==0 ) goto manifest_syntax_error; |
| 576 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 577 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 578 | if( p->nCChild>=p->nCChildAlloc ){ |
| 579 | p->nCChildAlloc = p->nCChildAlloc*2 + 10; |
| 580 | p->azCChild = fossil_realloc(p->azCChild |
| 581 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); |
| 582 | } |
| 583 | i = p->nCChild++; |
| 584 | p->azCChild[i] = zUuid; |
| 585 | if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){ |
| 586 | goto manifest_syntax_error; |
| @@ -413,20 +594,17 @@ | |
| 594 | ** Specify one or more other artifacts where are the parents of |
| 595 | ** this artifact. The first parent is the primary parent. All |
| 596 | ** others are parents by merge. |
| 597 | */ |
| 598 | case 'P': { |
| 599 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| 600 | if( sz!=UUID_SIZE ) goto manifest_syntax_error; |
| 601 | if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error; |
| 602 | if( p->nParent>=p->nParentAlloc ){ |
| 603 | p->nParentAlloc = p->nParentAlloc*2 + 5; |
| 604 | p->azParent = fossil_realloc(p->azParent, |
| 605 | p->nParentAlloc*sizeof(char*)); |
| 606 | } |
| 607 | i = p->nParent++; |
| 608 | p->azParent[i] = zUuid; |
| 609 | } |
| 610 | break; |
| @@ -437,16 +615,13 @@ | |
| 615 | ** |
| 616 | ** Specify the MD5 checksum over the name and content of all files |
| 617 | ** in the manifest. |
| 618 | */ |
| 619 | case 'R': { |
| 620 | if( p->zRepoCksum!=0 ) goto manifest_syntax_error; |
| 621 | p->zRepoCksum = next_token(&x, &sz); |
| 622 | if( sz!=32 ) goto manifest_syntax_error; |
| 623 | if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error; |
| 624 | break; |
| 625 | } |
| 626 | |
| 627 | /* |
| @@ -463,29 +638,20 @@ | |
| 638 | ** the tag is really a property with the given value. |
| 639 | ** |
| 640 | ** Tags are not allowed in clusters. Multiple T lines are allowed. |
| 641 | */ |
| 642 | case 'T': { |
| 643 | char *zName, *zValue; |
| 644 | zName = next_token(&x, 0); |
| 645 | if( zName==0 ) goto manifest_syntax_error; |
| 646 | zUuid = next_token(&x, &sz); |
| 647 | if( zUuid==0 ) goto manifest_syntax_error; |
| 648 | zValue = next_token(&x, 0); |
| 649 | if( zValue ) defossilize(zValue); |
| 650 | if( sz==UUID_SIZE && validate16(zUuid, UUID_SIZE) ){ |
| 651 | /* A valid uuid */ |
| 652 | }else if( sz==1 && zUuid[0]=='*' ){ |
| 653 | zUuid = 0; |
| 654 | }else{ |
| 655 | goto manifest_syntax_error; |
| 656 | } |
| 657 | defossilize(zName); |
| @@ -496,12 +662,11 @@ | |
| 662 | /* Do not allow tags whose names look like UUIDs */ |
| 663 | goto manifest_syntax_error; |
| 664 | } |
| 665 | if( p->nTag>=p->nTagAlloc ){ |
| 666 | p->nTagAlloc = p->nTagAlloc*2 + 10; |
| 667 | p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
| 668 | } |
| 669 | i = p->nTag++; |
| 670 | p->aTag[i].zName = zName; |
| 671 | p->aTag[i].zUuid = zUuid; |
| 672 | p->aTag[i].zValue = zValue; |
| @@ -517,19 +682,17 @@ | |
| 682 | ** Identify the user who created this control file by their |
| 683 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 684 | ** If the user name is omitted, take that to be "anonymous". |
| 685 | */ |
| 686 | case 'U': { |
| 687 | if( p->zUser!=0 ) goto manifest_syntax_error; |
| 688 | p->zUser = next_token(&x, 0); |
| 689 | if( p->zUser==0 ){ |
| 690 | p->zUser = "anonymous"; |
| 691 | }else{ |
| 692 | defossilize(p->zUser); |
| 693 | } |
| 694 | break; |
| 695 | } |
| 696 | |
| 697 | /* |
| 698 | ** W <size> |
| @@ -537,26 +700,28 @@ | |
| 700 | ** The next <size> bytes of the file contain the text of the wiki |
| 701 | ** page. There is always an extra \n before the start of the next |
| 702 | ** record. |
| 703 | */ |
| 704 | case 'W': { |
| 705 | char *zSize; |
| 706 | int size, c; |
| 707 | Blob wiki; |
| 708 | zSize = next_token(&x, 0); |
| 709 | if( zSize==0 ) goto manifest_syntax_error; |
| 710 | if( x.atEol==0 ) goto manifest_syntax_error; |
| 711 | for(size=0; (c = zSize[0])>='0' && c<='9'; zSize++){ |
| 712 | size = size*10 + c - '0'; |
| 713 | } |
| 714 | if( size<0 ) goto manifest_syntax_error; |
| 715 | if( p->zWiki!=0 ) goto manifest_syntax_error; |
| 716 | blob_zero(&wiki); |
| 717 | if( (&x.z[size+1])>=x.zEnd ) goto manifest_syntax_error; |
| 718 | p->zWiki = x.z; |
| 719 | x.z += size; |
| 720 | if( x.z[0]!='\n' ) goto manifest_syntax_error; |
| 721 | x.z[0] = 0; |
| 722 | x.z++; |
| 723 | break; |
| 724 | } |
| 725 | |
| 726 | |
| 727 | /* |
| @@ -569,31 +734,24 @@ | |
| 734 | ** This card is required for all control file types except for |
| 735 | ** Manifest. It is not required for manifest only for historical |
| 736 | ** compatibility reasons. |
| 737 | */ |
| 738 | case 'Z': { |
| 739 | zUuid = next_token(&x, &sz); |
| 740 | if( sz!=32 ) goto manifest_syntax_error; |
| 741 | if( !validate16(zUuid, 32) ) goto manifest_syntax_error; |
| 742 | seenZ = 1; |
| 743 | break; |
| 744 | } |
| 745 | default: { |
| 746 | goto manifest_syntax_error; |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | if( x.z<x.zEnd ) goto manifest_syntax_error; |
| 751 | |
| 752 | if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){ |
| 753 | if( p->nCChild>0 ) goto manifest_syntax_error; |
| 754 | if( p->rDate<=0.0 ) goto manifest_syntax_error; |
| 755 | if( p->nField>0 ) goto manifest_syntax_error; |
| 756 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 757 | if( p->zWiki ) goto manifest_syntax_error; |
| @@ -674,36 +832,194 @@ | |
| 832 | if( p->zWikiTitle ) goto manifest_syntax_error; |
| 833 | if( p->zTicketUuid ) goto manifest_syntax_error; |
| 834 | p->type = CFTYPE_MANIFEST; |
| 835 | } |
| 836 | md5sum_init(); |
| 837 | return p; |
| 838 | |
| 839 | manifest_syntax_error: |
| 840 | /*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/ |
| 841 | md5sum_init(); |
| 842 | manifest_destroy(p); |
| 843 | return 0; |
| 844 | } |
| 845 | |
| 846 | /* |
| 847 | ** Get a manifest given the rid for the control artifact. Return |
| 848 | ** a pointer to the manifest on success or NULL if there is a failure. |
| 849 | */ |
| 850 | Manifest *manifest_get(int rid, int cfType){ |
| 851 | Blob content; |
| 852 | Manifest *p; |
| 853 | p = manifest_cache_find(rid); |
| 854 | if( p ){ |
| 855 | if( cfType!=CFTYPE_ANY && cfType!=p->type ){ |
| 856 | manifest_cache_insert(p); |
| 857 | p = 0; |
| 858 | } |
| 859 | return p; |
| 860 | } |
| 861 | content_get(rid, &content); |
| 862 | p = manifest_parse(&content, rid); |
| 863 | if( p && cfType!=CFTYPE_ANY && cfType!=p->type ){ |
| 864 | manifest_destroy(p); |
| 865 | p = 0; |
| 866 | } |
| 867 | return p; |
| 868 | } |
| 869 | |
| 870 | /* |
| 871 | ** Given a checkin name, load and parse the manifest for that checkin. |
| 872 | ** Throw a fatal error if anything goes wrong. |
| 873 | */ |
| 874 | Manifest *manifest_get_by_name(const char *zName, int *pRid){ |
| 875 | int rid; |
| 876 | Manifest *p; |
| 877 | |
| 878 | rid = name_to_rid(zName); |
| 879 | if( !is_a_version(rid) ){ |
| 880 | fossil_fatal("no such checkin: %s", zName); |
| 881 | } |
| 882 | if( pRid ) *pRid = rid; |
| 883 | p = manifest_get(rid, CFTYPE_MANIFEST); |
| 884 | if( p==0 ){ |
| 885 | fossil_fatal("cannot parse manifest for checkin: %s", zName); |
| 886 | } |
| 887 | return p; |
| 888 | } |
| 889 | |
| 890 | /* |
| 891 | ** COMMAND: test-parse-manifest |
| 892 | ** |
| 893 | ** Usage: %fossil test-parse-manifest FILENAME ?N? |
| 894 | ** |
| 895 | ** Parse the manifest and discarded. Use for testing only. |
| 896 | */ |
| 897 | void manifest_test_parse_cmd(void){ |
| 898 | Manifest *p; |
| 899 | Blob b; |
| 900 | int i; |
| 901 | int n = 1; |
| 902 | sqlite3_open(":memory:", &g.db); |
| 903 | if( g.argc!=3 && g.argc!=4 ){ |
| 904 | usage("FILENAME"); |
| 905 | } |
| 906 | blob_read_from_file(&b, g.argv[2]); |
| 907 | if( g.argc>3 ) n = atoi(g.argv[3]); |
| 908 | for(i=0; i<n; i++){ |
| 909 | Blob b2; |
| 910 | blob_copy(&b2, &b); |
| 911 | p = manifest_parse(&b2, 0); |
| 912 | manifest_destroy(p); |
| 913 | } |
| 914 | } |
| 915 | |
| 916 | /* |
| 917 | ** Fetch the baseline associated with the delta-manifest p. |
| 918 | ** Return 0 on success. If unable to parse the baseline, |
| 919 | ** throw an error. If the baseline is a manifest, throw an |
| 920 | ** error if throwError is true, or record that p is an orphan |
| 921 | ** and return 1 throwError is false. |
| 922 | */ |
| 923 | static int fetch_baseline(Manifest *p, int throwError){ |
| 924 | if( p->zBaseline!=0 && p->pBaseline==0 ){ |
| 925 | int rid = uuid_to_rid(p->zBaseline, 0); |
| 926 | if( rid==0 && !throwError ){ |
| 927 | rid = content_new(p->zBaseline); |
| 928 | db_multi_exec( |
| 929 | "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", |
| 930 | rid, p->rid |
| 931 | ); |
| 932 | return 1; |
| 933 | } |
| 934 | p->pBaseline = manifest_get(rid, CFTYPE_MANIFEST); |
| 935 | if( p->pBaseline==0 ){ |
| 936 | if( !throwError && db_exists("SELECT 1 FROM phantom WHERE rid=%d",rid) ){ |
| 937 | db_multi_exec( |
| 938 | "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", |
| 939 | rid, p->rid |
| 940 | ); |
| 941 | return 1; |
| 942 | } |
| 943 | fossil_fatal("cannot access baseline manifest %S", p->zBaseline); |
| 944 | } |
| 945 | } |
| 946 | return 0; |
| 947 | } |
| 948 | |
| 949 | /* |
| 950 | ** Rewind a manifest-file iterator back to the beginning of the manifest. |
| 951 | */ |
| 952 | void manifest_file_rewind(Manifest *p){ |
| 953 | p->iFile = 0; |
| 954 | fetch_baseline(p, 1); |
| 955 | if( p->pBaseline ){ |
| 956 | p->pBaseline->iFile = 0; |
| 957 | } |
| 958 | } |
| 959 | |
| 960 | /* |
| 961 | ** Advance to the next manifest-file. |
| 962 | ** |
| 963 | ** Return NULL for end-of-records or if there is an error. If an error |
| 964 | ** occurs and pErr!=0 then store 1 in *pErr. |
| 965 | */ |
| 966 | ManifestFile *manifest_file_next( |
| 967 | Manifest *p, |
| 968 | int *pErr |
| 969 | ){ |
| 970 | ManifestFile *pOut = 0; |
| 971 | if( pErr ) *pErr = 0; |
| 972 | if( p->pBaseline==0 ){ |
| 973 | /* Manifest p is a baseline-manifest. Just scan down the list |
| 974 | ** of files. */ |
| 975 | if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; |
| 976 | }else{ |
| 977 | /* Manifest p is a delta-manifest. Scan the baseline but amend the |
| 978 | ** file list in the baseline with changes described by p. |
| 979 | */ |
| 980 | Manifest *pB = p->pBaseline; |
| 981 | int cmp; |
| 982 | while(1){ |
| 983 | if( pB->iFile>=pB->nFile ){ |
| 984 | /* We have used all entries out of the baseline. Return the next |
| 985 | ** entry from the delta. */ |
| 986 | if( p->iFile<p->nFile ) pOut = &p->aFile[p->iFile++]; |
| 987 | break; |
| 988 | }else if( p->iFile>=p->nFile ){ |
| 989 | /* We have used all entries from the delta. Return the next |
| 990 | ** entry from the baseline. */ |
| 991 | if( pB->iFile<pB->nFile ) pOut = &pB->aFile[pB->iFile++]; |
| 992 | break; |
| 993 | }else if( (cmp = strcmp(pB->aFile[pB->iFile].zName, |
| 994 | p->aFile[p->iFile].zName)) < 0 ){ |
| 995 | /* The next baseline entry comes before the next delta entry. |
| 996 | ** So return the baseline entry. */ |
| 997 | pOut = &pB->aFile[pB->iFile++]; |
| 998 | break; |
| 999 | }else if( cmp>0 ){ |
| 1000 | /* The next delta entry comes before the next baseline |
| 1001 | ** entry so return the delta entry */ |
| 1002 | pOut = &p->aFile[p->iFile++]; |
| 1003 | break; |
| 1004 | }else if( p->aFile[p->iFile].zUuid ){ |
| 1005 | /* The next delta entry is a replacement for the next baseline |
| 1006 | ** entry. Skip the baseline entry and return the delta entry */ |
| 1007 | pB->iFile++; |
| 1008 | pOut = &p->aFile[p->iFile++]; |
| 1009 | break; |
| 1010 | }else{ |
| 1011 | /* The next delta entry is a delete of the next baseline |
| 1012 | ** entry. Skip them both. Repeat the loop to find the next |
| 1013 | ** non-delete entry. */ |
| 1014 | pB->iFile++; |
| 1015 | p->iFile++; |
| 1016 | continue; |
| 1017 | } |
| 1018 | } |
| 1019 | } |
| 1020 | return pOut; |
| 1021 | } |
| 1022 | |
| 1023 | /* |
| 1024 | ** Translate a filename into a filename-id (fnid). Create a new fnid |
| 1025 | ** if no previously exists. |
| @@ -731,14 +1047,14 @@ | |
| 1047 | ** Add a single entry to the mlink table. Also add the filename to |
| 1048 | ** the filename table if it is not there already. |
| 1049 | */ |
| 1050 | static void add_one_mlink( |
| 1051 | int mid, /* The record ID of the manifest */ |
| 1052 | const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ |
| 1053 | const char *zToUuid, /* UUID for the mlink.fid. "" to delele */ |
| 1054 | const char *zFilename, /* Filename */ |
| 1055 | const char *zPrior /* Previous filename. NULL if unchanged */ |
| 1056 | ){ |
| 1057 | int fnid, pfnid, pid, fid; |
| 1058 | static Stmt s1; |
| 1059 | |
| 1060 | fnid = filename_to_fnid(zFilename); |
| @@ -745,16 +1061,16 @@ | |
| 1061 | if( zPrior==0 ){ |
| 1062 | pfnid = 0; |
| 1063 | }else{ |
| 1064 | pfnid = filename_to_fnid(zPrior); |
| 1065 | } |
| 1066 | if( zFromUuid==0 || zFromUuid[0]==0 ){ |
| 1067 | pid = 0; |
| 1068 | }else{ |
| 1069 | pid = uuid_to_rid(zFromUuid, 1); |
| 1070 | } |
| 1071 | if( zToUuid==0 || zToUuid[0]==0 ){ |
| 1072 | fid = 0; |
| 1073 | }else{ |
| 1074 | fid = uuid_to_rid(zToUuid, 1); |
| 1075 | } |
| 1076 | db_static_prepare(&s1, |
| @@ -771,33 +1087,83 @@ | |
| 1087 | content_deltify(pid, fid, 0); |
| 1088 | } |
| 1089 | } |
| 1090 | |
| 1091 | /* |
| 1092 | ** Do a binary search to find a file in the p->aFile[] array. |
| 1093 | ** |
| 1094 | ** As an optimization, guess that the file we seek is at index p->iFile. |
| 1095 | ** That will usually be the case. If it is not found there, then do the |
| 1096 | ** actual binary search. |
| 1097 | ** |
| 1098 | ** Update p->iFile to be the index of the file that is found. |
| 1099 | */ |
| 1100 | static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){ |
| 1101 | int lwr, upr; |
| 1102 | int c; |
| 1103 | int i; |
| 1104 | lwr = 0; |
| 1105 | upr = p->nFile - 1; |
| 1106 | if( p->iFile>=lwr && p->iFile<upr ){ |
| 1107 | c = strcmp(p->aFile[p->iFile+1].zName, zName); |
| 1108 | if( c==0 ){ |
| 1109 | return &p->aFile[++p->iFile]; |
| 1110 | }else if( c>0 ){ |
| 1111 | upr = p->iFile; |
| 1112 | }else{ |
| 1113 | lwr = p->iFile+1; |
| 1114 | } |
| 1115 | } |
| 1116 | while( lwr<=upr ){ |
| 1117 | i = (lwr+upr)/2; |
| 1118 | c = strcmp(p->aFile[i].zName, zName); |
| 1119 | if( c<0 ){ |
| 1120 | lwr = i+1; |
| 1121 | }else if( c>0 ){ |
| 1122 | upr = i-1; |
| 1123 | }else{ |
| 1124 | p->iFile = i; |
| 1125 | return &p->aFile[i]; |
| 1126 | } |
| 1127 | } |
| 1128 | return 0; |
| 1129 | } |
| 1130 | |
| 1131 | /* |
| 1132 | ** Locate a file named zName in the aFile[] array of the given manifest. |
| 1133 | ** Return a pointer to the appropriate ManifestFile object. Return NULL |
| 1134 | ** if not found. |
| 1135 | ** |
| 1136 | ** This routine works even if p is a delta-manifest. The pointer |
| 1137 | ** returned might be to the baseline. |
| 1138 | ** |
| 1139 | ** We assume that filenames are in sorted order and use a binary search. |
| 1140 | */ |
| 1141 | ManifestFile *manifest_file_seek(Manifest *p, const char *zName){ |
| 1142 | ManifestFile *pFile; |
| 1143 | |
| 1144 | pFile = manifest_file_seek_base(p, zName); |
| 1145 | if( pFile && pFile->zUuid==0 ) return 0; |
| 1146 | if( pFile==0 && p->zBaseline ){ |
| 1147 | fetch_baseline(p, 1); |
| 1148 | pFile = manifest_file_seek_base(p->pBaseline, zName); |
| 1149 | } |
| 1150 | return pFile; |
| 1151 | } |
| 1152 | |
| 1153 | /* |
| 1154 | ** This strcmp() function handles NULL arguments. NULLs sort first. |
| 1155 | */ |
| 1156 | static int strcmp_null(const char *zOne, const char *zTwo){ |
| 1157 | if( zOne==0 ){ |
| 1158 | if( zTwo==0 ) return 0; |
| 1159 | return -1; |
| 1160 | }else if( zTwo==0 ){ |
| 1161 | return +1; |
| 1162 | }else{ |
| 1163 | return strcmp(zOne, zTwo); |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | /* |
| 1168 | ** Add mlink table entries associated with manifest cid. The |
| 1169 | ** parent manifest is pid. |
| @@ -808,85 +1174,76 @@ | |
| 1174 | ** Deleted files have mlink.fid=0. |
| 1175 | ** Added files have mlink.pid=0. |
| 1176 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 1177 | */ |
| 1178 | static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ |
| 1179 | Blob otherContent; |
| 1180 | int otherRid; |
| 1181 | int i, rc; |
| 1182 | ManifestFile *pChildFile, *pParentFile; |
| 1183 | Manifest **ppOther; |
| 1184 | static Stmt eq; |
| 1185 | |
| 1186 | db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid"); |
| 1187 | db_bind_int(&eq, ":mid", cid); |
| 1188 | rc = db_step(&eq); |
| 1189 | db_reset(&eq); |
| 1190 | if( rc==SQLITE_ROW ) return; |
| 1191 | |
| 1192 | assert( pParent==0 || pChild==0 ); |
| 1193 | if( pParent==0 ){ |
| 1194 | ppOther = &pParent; |
| 1195 | otherRid = pid; |
| 1196 | }else{ |
| 1197 | ppOther = &pChild; |
| 1198 | otherRid = cid; |
| 1199 | } |
| 1200 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1201 | content_get(otherRid, &otherContent); |
| 1202 | if( blob_size(&otherContent)==0 ) return; |
| 1203 | *ppOther = manifest_parse(&otherContent, otherRid); |
| 1204 | if( *ppOther==0 ) return; |
| 1205 | } |
| 1206 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1207 | manifest_destroy(*ppOther); |
| 1208 | return; |
| 1209 | } |
| 1210 | if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ |
| 1211 | content_deltify(pid, cid, 0); |
| 1212 | }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ |
| 1213 | content_deltify(pParent->pBaseline->rid, cid, 0); |
| 1214 | } |
| 1215 | |
| 1216 | for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){ |
| 1217 | if( pChildFile->zPrior ){ |
| 1218 | pParentFile = manifest_file_seek(pParent, pChildFile->zPrior); |
| 1219 | if( pParentFile ){ |
| 1220 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1221 | pChildFile->zName, pChildFile->zPrior); |
| 1222 | } |
| 1223 | }else{ |
| 1224 | pParentFile = manifest_file_seek(pParent, pChildFile->zName); |
| 1225 | if( pParentFile==0 ){ |
| 1226 | if( pChildFile->zUuid ){ |
| 1227 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); |
| 1228 | } |
| 1229 | }else if( strcmp_null(pChildFile->zUuid, pParentFile->zUuid)!=0 ){ |
| 1230 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1231 | pChildFile->zName, 0); |
| 1232 | } |
| 1233 | } |
| 1234 | } |
| 1235 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1236 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ |
| 1237 | if( pParentFile->zUuid ) continue; |
| 1238 | pChildFile = manifest_file_seek(pChild, pParentFile->zName); |
| 1239 | if( pChildFile ){ |
| 1240 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); |
| 1241 | } |
| 1242 | } |
| 1243 | } |
| 1244 | manifest_cache_insert(*ppOther); |
| 1245 | } |
| 1246 | |
| 1247 | /* |
| 1248 | ** True if manifest_crosslink_begin() has been called but |
| 1249 | ** manifest_crosslink_end() is still pending. |
| @@ -1023,38 +1380,44 @@ | |
| 1380 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1381 | ** file, is a legacy of its original use. |
| 1382 | */ |
| 1383 | int manifest_crosslink(int rid, Blob *pContent){ |
| 1384 | int i; |
| 1385 | Manifest *p; |
| 1386 | Stmt q; |
| 1387 | int parentid = 0; |
| 1388 | |
| 1389 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1390 | blob_reset(pContent); |
| 1391 | }else if( (p = manifest_parse(pContent, rid))==0 ){ |
| 1392 | return 0; |
| 1393 | } |
| 1394 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1395 | manifest_destroy(p); |
| 1396 | return 0; |
| 1397 | } |
| 1398 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1399 | manifest_destroy(p); |
| 1400 | return 0; |
| 1401 | } |
| 1402 | db_begin_transaction(); |
| 1403 | if( p->type==CFTYPE_MANIFEST ){ |
| 1404 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1405 | char *zCom; |
| 1406 | for(i=0; i<p->nParent; i++){ |
| 1407 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1408 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| 1409 | "VALUES(%d, %d, %d, %.17g)", pid, rid, i==0, p->rDate); |
| 1410 | if( i==0 ){ |
| 1411 | add_mlink(pid, 0, rid, p); |
| 1412 | parentid = pid; |
| 1413 | } |
| 1414 | } |
| 1415 | db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); |
| 1416 | while( db_step(&q)==SQLITE_ROW ){ |
| 1417 | int cid = db_column_int(&q, 0); |
| 1418 | add_mlink(rid, p, cid, 0); |
| 1419 | } |
| 1420 | db_finalize(&q); |
| 1421 | db_multi_exec( |
| 1422 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1423 | "bgcolor,euser,ecomment)" |
| @@ -1065,118 +1428,134 @@ | |
| 1428 | " )," |
| 1429 | " %d,%Q,%Q," |
| 1430 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0)," |
| 1431 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1432 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1433 | TAG_DATE, rid, p->rDate, |
| 1434 | rid, p->zUser, p->zComment, |
| 1435 | TAG_BGCOLOR, rid, |
| 1436 | TAG_USER, rid, |
| 1437 | TAG_COMMENT, rid |
| 1438 | ); |
| 1439 | zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" |
| 1440 | " WHERE rowid=last_insert_rowid()"); |
| 1441 | wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); |
| 1442 | free(zCom); |
| 1443 | |
| 1444 | /* If this is a delta-manifest, record the fact that this repository |
| 1445 | ** contains delta manifests, to free the "commit" logic to generate |
| 1446 | ** new delta manifests. |
| 1447 | */ |
| 1448 | if( p->zBaseline!=0 ){ |
| 1449 | static int once = 0; |
| 1450 | if( !once ){ |
| 1451 | db_set_int("seen-delta-manifest", 1, 0); |
| 1452 | once = 0; |
| 1453 | } |
| 1454 | } |
| 1455 | } |
| 1456 | } |
| 1457 | if( p->type==CFTYPE_CLUSTER ){ |
| 1458 | static Stmt del1; |
| 1459 | tag_insert("cluster", 1, 0, rid, p->rDate, rid); |
| 1460 | db_static_prepare(&del1, "DELETE FROM unclustered WHERE rid=:rid"); |
| 1461 | for(i=0; i<p->nCChild; i++){ |
| 1462 | int mid; |
| 1463 | mid = uuid_to_rid(p->azCChild[i], 1); |
| 1464 | if( mid>0 ){ |
| 1465 | db_bind_int(&del1, ":rid", mid); |
| 1466 | db_step(&del1); |
| 1467 | db_reset(&del1); |
| 1468 | } |
| 1469 | } |
| 1470 | } |
| 1471 | if( p->type==CFTYPE_CONTROL |
| 1472 | || p->type==CFTYPE_MANIFEST |
| 1473 | || p->type==CFTYPE_EVENT |
| 1474 | ){ |
| 1475 | for(i=0; i<p->nTag; i++){ |
| 1476 | int tid; |
| 1477 | int type; |
| 1478 | if( p->aTag[i].zUuid ){ |
| 1479 | tid = uuid_to_rid(p->aTag[i].zUuid, 1); |
| 1480 | }else{ |
| 1481 | tid = rid; |
| 1482 | } |
| 1483 | if( tid ){ |
| 1484 | switch( p->aTag[i].zName[0] ){ |
| 1485 | case '-': type = 0; break; /* Cancel prior occurances */ |
| 1486 | case '+': type = 1; break; /* Apply to target only */ |
| 1487 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1488 | default: |
| 1489 | fossil_fatal("unknown tag type in manifest: %s", p->aTag); |
| 1490 | return 0; |
| 1491 | } |
| 1492 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1493 | rid, p->rDate, tid); |
| 1494 | } |
| 1495 | } |
| 1496 | if( parentid ){ |
| 1497 | tag_propagate_all(parentid); |
| 1498 | } |
| 1499 | } |
| 1500 | if( p->type==CFTYPE_WIKI ){ |
| 1501 | char *zTag = mprintf("wiki-%s", p->zWikiTitle); |
| 1502 | int tagid = tag_findid(zTag, 1); |
| 1503 | int prior; |
| 1504 | char *zComment; |
| 1505 | int nWiki; |
| 1506 | char zLength[40]; |
| 1507 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 1508 | nWiki = strlen(p->zWiki); |
| 1509 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1510 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 1511 | free(zTag); |
| 1512 | prior = db_int(0, |
| 1513 | "SELECT rid FROM tagxref" |
| 1514 | " WHERE tagid=%d AND mtime<%.17g" |
| 1515 | " ORDER BY mtime DESC", |
| 1516 | tagid, p->rDate |
| 1517 | ); |
| 1518 | if( prior ){ |
| 1519 | content_deltify(prior, rid, 0); |
| 1520 | } |
| 1521 | if( nWiki>0 ){ |
| 1522 | zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); |
| 1523 | }else{ |
| 1524 | zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); |
| 1525 | } |
| 1526 | db_multi_exec( |
| 1527 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1528 | " bgcolor,euser,ecomment)" |
| 1529 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 1530 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| 1531 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," |
| 1532 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1533 | p->rDate, rid, p->zUser, zComment, |
| 1534 | TAG_BGCOLOR, rid, |
| 1535 | TAG_BGCOLOR, rid, |
| 1536 | TAG_USER, rid, |
| 1537 | TAG_COMMENT, rid |
| 1538 | ); |
| 1539 | free(zComment); |
| 1540 | } |
| 1541 | if( p->type==CFTYPE_EVENT ){ |
| 1542 | char *zTag = mprintf("event-%s", p->zEventId); |
| 1543 | int tagid = tag_findid(zTag, 1); |
| 1544 | int prior, subsequent; |
| 1545 | int nWiki; |
| 1546 | char zLength[40]; |
| 1547 | while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; |
| 1548 | nWiki = strlen(p->zWiki); |
| 1549 | sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); |
| 1550 | tag_insert(zTag, 1, zLength, rid, p->rDate, rid); |
| 1551 | free(zTag); |
| 1552 | prior = db_int(0, |
| 1553 | "SELECT rid FROM tagxref" |
| 1554 | " WHERE tagid=%d AND mtime<%.17g" |
| 1555 | " ORDER BY mtime DESC", |
| 1556 | tagid, p->rDate |
| 1557 | ); |
| 1558 | if( prior ){ |
| 1559 | content_deltify(prior, rid, 0); |
| 1560 | db_multi_exec( |
| 1561 | "DELETE FROM event" |
| @@ -1188,104 +1567,87 @@ | |
| 1567 | } |
| 1568 | subsequent = db_int(0, |
| 1569 | "SELECT rid FROM tagxref" |
| 1570 | " WHERE tagid=%d AND mtime>%.17g" |
| 1571 | " ORDER BY mtime", |
| 1572 | tagid, p->rDate |
| 1573 | ); |
| 1574 | if( subsequent ){ |
| 1575 | content_deltify(rid, subsequent, 0); |
| 1576 | }else{ |
| 1577 | db_multi_exec( |
| 1578 | "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" |
| 1579 | "VALUES('e',%.17g,%d,%d,%Q,%Q," |
| 1580 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1581 | p->rEventDate, rid, tagid, p->zUser, p->zComment, |
| 1582 | TAG_BGCOLOR, rid |
| 1583 | ); |
| 1584 | } |
| 1585 | } |
| 1586 | if( p->type==CFTYPE_TICKET ){ |
| 1587 | char *zTag; |
| 1588 | |
| 1589 | assert( manifest_crosslink_busy==1 ); |
| 1590 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1591 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1592 | free(zTag); |
| 1593 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| 1594 | p->zTicketUuid); |
| 1595 | } |
| 1596 | if( p->type==CFTYPE_ATTACHMENT ){ |
| 1597 | db_multi_exec( |
| 1598 | "INSERT INTO attachment(attachid, mtime, src, target," |
| 1599 | "filename, comment, user)" |
| 1600 | "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", |
| 1601 | rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName, |
| 1602 | (p->zComment ? p->zComment : ""), p->zUser |
| 1603 | ); |
| 1604 | db_multi_exec( |
| 1605 | "UPDATE attachment SET isLatest = (mtime==" |
| 1606 | "(SELECT max(mtime) FROM attachment" |
| 1607 | " WHERE target=%Q AND filename=%Q))" |
| 1608 | " WHERE target=%Q AND filename=%Q", |
| 1609 | p->zAttachTarget, p->zAttachName, |
| 1610 | p->zAttachTarget, p->zAttachName |
| 1611 | ); |
| 1612 | if( strlen(p->zAttachTarget)!=UUID_SIZE |
| 1613 | || !validate16(p->zAttachTarget, UUID_SIZE) |
| 1614 | ){ |
| 1615 | char *zComment; |
| 1616 | if( p->zAttachSrc && p->zAttachSrc[0] ){ |
| 1617 | zComment = mprintf("Add attachment \"%h\" to wiki page [%h]", |
| 1618 | p->zAttachName, p->zAttachTarget); |
| 1619 | }else{ |
| 1620 | zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", |
| 1621 | p->zAttachName, p->zAttachTarget); |
| 1622 | } |
| 1623 | db_multi_exec( |
| 1624 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1625 | "VALUES('w',%.17g,%d,%Q,%Q)", |
| 1626 | p->rDate, rid, p->zUser, zComment |
| 1627 | ); |
| 1628 | free(zComment); |
| 1629 | }else{ |
| 1630 | char *zComment; |
| 1631 | if( p->zAttachSrc && p->zAttachSrc[0] ){ |
| 1632 | zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]", |
| 1633 | p->zAttachName, p->zAttachTarget); |
| 1634 | }else{ |
| 1635 | zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]", |
| 1636 | p->zAttachName, p->zAttachTarget); |
| 1637 | } |
| 1638 | db_multi_exec( |
| 1639 | "REPLACE INTO event(type,mtime,objid,user,comment)" |
| 1640 | "VALUES('t',%.17g,%d,%Q,%Q)", |
| 1641 | p->rDate, rid, p->zUser, zComment |
| 1642 | ); |
| 1643 | free(zComment); |
| 1644 | } |
| 1645 | } |
| 1646 | db_end_transaction(0); |
| 1647 | if( p->type==CFTYPE_MANIFEST ){ |
| 1648 | manifest_cache_insert(p); |
| 1649 | }else{ |
| 1650 | manifest_destroy(p); |
| 1651 | } |
| 1652 | return 1; |
| 1653 | } |
| 1654 |
+27
-1
| --- src/md5.c | ||
| +++ src/md5.c | ||
| @@ -41,12 +41,16 @@ | ||
| 41 | 41 | uint32 bits[2]; |
| 42 | 42 | unsigned char in[64]; |
| 43 | 43 | }; |
| 44 | 44 | typedef struct Context MD5Context; |
| 45 | 45 | |
| 46 | +#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) | |
| 47 | +# define byteReverse(A,B) | |
| 48 | +#else | |
| 46 | 49 | /* |
| 47 | - * Note: this code is harmless on little-endian machines. | |
| 50 | + * Convert an array of integers to little-endian. | |
| 51 | + * Note: this code is a no-op on little-endian machines. | |
| 48 | 52 | */ |
| 49 | 53 | static void byteReverse (unsigned char *buf, unsigned longs){ |
| 50 | 54 | uint32 t; |
| 51 | 55 | do { |
| 52 | 56 | t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | |
| @@ -53,10 +57,12 @@ | ||
| 53 | 57 | ((unsigned)buf[1]<<8 | buf[0]); |
| 54 | 58 | *(uint32 *)buf = t; |
| 55 | 59 | buf += 4; |
| 56 | 60 | } while (--longs); |
| 57 | 61 | } |
| 62 | +#endif | |
| 63 | + | |
| 58 | 64 | /* The four core functions - F1 is optimized somewhat */ |
| 59 | 65 | |
| 60 | 66 | /* #define F1(x, y, z) (x & y | ~x & z) */ |
| 61 | 67 | #define F1(x, y, z) (z ^ (x & (y ^ z))) |
| 62 | 68 | #define F2(x, y, z) F1(z, x, y) |
| @@ -314,10 +320,30 @@ | ||
| 314 | 320 | ** Add the content of a blob to the incremental MD5 checksum. |
| 315 | 321 | */ |
| 316 | 322 | void md5sum_step_blob(Blob *p){ |
| 317 | 323 | md5sum_step_text(blob_buffer(p), blob_size(p)); |
| 318 | 324 | } |
| 325 | + | |
| 326 | +/* | |
| 327 | +** For trouble-shooting only: | |
| 328 | +** | |
| 329 | +** Report the current state of the incremental checksum. | |
| 330 | +*/ | |
| 331 | +const char *md5sum_current_state(void){ | |
| 332 | + unsigned int cksum = 0; | |
| 333 | + unsigned int *pFirst, *pLast; | |
| 334 | + static char zResult[12]; | |
| 335 | + | |
| 336 | + pFirst = (unsigned int*)&incrCtx; | |
| 337 | + pLast = (unsigned int*)((&incrCtx)+1); | |
| 338 | + while( pFirst<pLast ){ | |
| 339 | + cksum += *pFirst; | |
| 340 | + pFirst++; | |
| 341 | + } | |
| 342 | + sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum); | |
| 343 | + return zResult; | |
| 344 | +} | |
| 319 | 345 | |
| 320 | 346 | /* |
| 321 | 347 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 322 | 348 | ** if pOut!=0. Also return a pointer to the result. |
| 323 | 349 | ** |
| 324 | 350 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -41,12 +41,16 @@ | |
| 41 | uint32 bits[2]; |
| 42 | unsigned char in[64]; |
| 43 | }; |
| 44 | typedef struct Context MD5Context; |
| 45 | |
| 46 | /* |
| 47 | * Note: this code is harmless on little-endian machines. |
| 48 | */ |
| 49 | static void byteReverse (unsigned char *buf, unsigned longs){ |
| 50 | uint32 t; |
| 51 | do { |
| 52 | t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | |
| @@ -53,10 +57,12 @@ | |
| 53 | ((unsigned)buf[1]<<8 | buf[0]); |
| 54 | *(uint32 *)buf = t; |
| 55 | buf += 4; |
| 56 | } while (--longs); |
| 57 | } |
| 58 | /* The four core functions - F1 is optimized somewhat */ |
| 59 | |
| 60 | /* #define F1(x, y, z) (x & y | ~x & z) */ |
| 61 | #define F1(x, y, z) (z ^ (x & (y ^ z))) |
| 62 | #define F2(x, y, z) F1(z, x, y) |
| @@ -314,10 +320,30 @@ | |
| 314 | ** Add the content of a blob to the incremental MD5 checksum. |
| 315 | */ |
| 316 | void md5sum_step_blob(Blob *p){ |
| 317 | md5sum_step_text(blob_buffer(p), blob_size(p)); |
| 318 | } |
| 319 | |
| 320 | /* |
| 321 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 322 | ** if pOut!=0. Also return a pointer to the result. |
| 323 | ** |
| 324 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -41,12 +41,16 @@ | |
| 41 | uint32 bits[2]; |
| 42 | unsigned char in[64]; |
| 43 | }; |
| 44 | typedef struct Context MD5Context; |
| 45 | |
| 46 | #if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) |
| 47 | # define byteReverse(A,B) |
| 48 | #else |
| 49 | /* |
| 50 | * Convert an array of integers to little-endian. |
| 51 | * Note: this code is a no-op on little-endian machines. |
| 52 | */ |
| 53 | static void byteReverse (unsigned char *buf, unsigned longs){ |
| 54 | uint32 t; |
| 55 | do { |
| 56 | t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | |
| @@ -53,10 +57,12 @@ | |
| 57 | ((unsigned)buf[1]<<8 | buf[0]); |
| 58 | *(uint32 *)buf = t; |
| 59 | buf += 4; |
| 60 | } while (--longs); |
| 61 | } |
| 62 | #endif |
| 63 | |
| 64 | /* The four core functions - F1 is optimized somewhat */ |
| 65 | |
| 66 | /* #define F1(x, y, z) (x & y | ~x & z) */ |
| 67 | #define F1(x, y, z) (z ^ (x & (y ^ z))) |
| 68 | #define F2(x, y, z) F1(z, x, y) |
| @@ -314,10 +320,30 @@ | |
| 320 | ** Add the content of a blob to the incremental MD5 checksum. |
| 321 | */ |
| 322 | void md5sum_step_blob(Blob *p){ |
| 323 | md5sum_step_text(blob_buffer(p), blob_size(p)); |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | ** For trouble-shooting only: |
| 328 | ** |
| 329 | ** Report the current state of the incremental checksum. |
| 330 | */ |
| 331 | const char *md5sum_current_state(void){ |
| 332 | unsigned int cksum = 0; |
| 333 | unsigned int *pFirst, *pLast; |
| 334 | static char zResult[12]; |
| 335 | |
| 336 | pFirst = (unsigned int*)&incrCtx; |
| 337 | pLast = (unsigned int*)((&incrCtx)+1); |
| 338 | while( pFirst<pLast ){ |
| 339 | cksum += *pFirst; |
| 340 | pFirst++; |
| 341 | } |
| 342 | sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum); |
| 343 | return zResult; |
| 344 | } |
| 345 | |
| 346 | /* |
| 347 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 348 | ** if pOut!=0. Also return a pointer to the result. |
| 349 | ** |
| 350 |
+2
-2
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -151,13 +151,13 @@ | ||
| 151 | 151 | int *aC2; /* Changes from pPivot to pV2 */ |
| 152 | 152 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 153 | 153 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 154 | 154 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 155 | 155 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 156 | - static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; | |
| 156 | + static const char zBegin[] = "<<<<<<< BEGIN MERGE CONFLICT\n"; | |
| 157 | 157 | static const char zMid[] = "============================\n"; |
| 158 | - static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; | |
| 158 | + static const char zEnd[] = ">>>>>>> END MERGE CONFLICT\n"; | |
| 159 | 159 | |
| 160 | 160 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 161 | 161 | |
| 162 | 162 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 163 | 163 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 164 | 164 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -151,13 +151,13 @@ | |
| 151 | int *aC2; /* Changes from pPivot to pV2 */ |
| 152 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 153 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 154 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 155 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 156 | static const char zBegin[] = ">>>>>>> BEGIN MERGE CONFLICT\n"; |
| 157 | static const char zMid[] = "============================\n"; |
| 158 | static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n"; |
| 159 | |
| 160 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 161 | |
| 162 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 163 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 164 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -151,13 +151,13 @@ | |
| 151 | int *aC2; /* Changes from pPivot to pV2 */ |
| 152 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 153 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 154 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 155 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 156 | static const char zBegin[] = "<<<<<<< BEGIN MERGE CONFLICT\n"; |
| 157 | static const char zMid[] = "============================\n"; |
| 158 | static const char zEnd[] = ">>>>>>> END MERGE CONFLICT\n"; |
| 159 | |
| 160 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 161 | |
| 162 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 163 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| 164 |
+10
-10
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -111,20 +111,20 @@ | ||
| 111 | 111 | |
| 112 | 112 | /* |
| 113 | 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | 114 | */ |
| 115 | 115 | static int is_date(const char *z){ |
| 116 | - if( !isdigit(z[0]) ) return 0; | |
| 117 | - if( !isdigit(z[1]) ) return 0; | |
| 118 | - if( !isdigit(z[2]) ) return 0; | |
| 119 | - if( !isdigit(z[3]) ) return 0; | |
| 116 | + if( !fossil_isdigit(z[0]) ) return 0; | |
| 117 | + if( !fossil_isdigit(z[1]) ) return 0; | |
| 118 | + if( !fossil_isdigit(z[2]) ) return 0; | |
| 119 | + if( !fossil_isdigit(z[3]) ) return 0; | |
| 120 | 120 | if( z[4]!='-') return 0; |
| 121 | - if( !isdigit(z[5]) ) return 0; | |
| 122 | - if( !isdigit(z[6]) ) return 0; | |
| 121 | + if( !fossil_isdigit(z[5]) ) return 0; | |
| 122 | + if( !fossil_isdigit(z[6]) ) return 0; | |
| 123 | 123 | if( z[7]!='-') return 0; |
| 124 | - if( !isdigit(z[8]) ) return 0; | |
| 125 | - if( !isdigit(z[9]) ) return 0; | |
| 124 | + if( !fossil_isdigit(z[8]) ) return 0; | |
| 125 | + if( !fossil_isdigit(z[9]) ) return 0; | |
| 126 | 126 | return 1; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | 129 | /* |
| 130 | 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| @@ -280,11 +280,11 @@ | ||
| 280 | 280 | |
| 281 | 281 | if( zName==0 || zName[0]==0 ) return 0; |
| 282 | 282 | blob_init(&name, zName, -1); |
| 283 | 283 | if( name_to_uuid(&name, -1) ){ |
| 284 | 284 | blob_reset(&name); |
| 285 | - for(i=0; zName[i] && isdigit(zName[i]); i++){} | |
| 285 | + for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 286 | 286 | if( zName[i]==0 ){ |
| 287 | 287 | rid = atoi(zName); |
| 288 | 288 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 289 | 289 | return rid; |
| 290 | 290 | } |
| @@ -348,11 +348,11 @@ | ||
| 348 | 348 | if( zName==0 || zName[0]==0 ) return 0; |
| 349 | 349 | blob_init(&name, zName, -1); |
| 350 | 350 | rc = name_to_uuid(&name, -1); |
| 351 | 351 | if( rc==1 ){ |
| 352 | 352 | blob_reset(&name); |
| 353 | - for(i=0; zName[i] && isdigit(zName[i]); i++){} | |
| 353 | + for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 354 | 354 | if( zName[i]==0 ){ |
| 355 | 355 | rid = atoi(zName); |
| 356 | 356 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 357 | 357 | return rid; |
| 358 | 358 | } |
| 359 | 359 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -111,20 +111,20 @@ | |
| 111 | |
| 112 | /* |
| 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | */ |
| 115 | static int is_date(const char *z){ |
| 116 | if( !isdigit(z[0]) ) return 0; |
| 117 | if( !isdigit(z[1]) ) return 0; |
| 118 | if( !isdigit(z[2]) ) return 0; |
| 119 | if( !isdigit(z[3]) ) return 0; |
| 120 | if( z[4]!='-') return 0; |
| 121 | if( !isdigit(z[5]) ) return 0; |
| 122 | if( !isdigit(z[6]) ) return 0; |
| 123 | if( z[7]!='-') return 0; |
| 124 | if( !isdigit(z[8]) ) return 0; |
| 125 | if( !isdigit(z[9]) ) return 0; |
| 126 | return 1; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| @@ -280,11 +280,11 @@ | |
| 280 | |
| 281 | if( zName==0 || zName[0]==0 ) return 0; |
| 282 | blob_init(&name, zName, -1); |
| 283 | if( name_to_uuid(&name, -1) ){ |
| 284 | blob_reset(&name); |
| 285 | for(i=0; zName[i] && isdigit(zName[i]); i++){} |
| 286 | if( zName[i]==0 ){ |
| 287 | rid = atoi(zName); |
| 288 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 289 | return rid; |
| 290 | } |
| @@ -348,11 +348,11 @@ | |
| 348 | if( zName==0 || zName[0]==0 ) return 0; |
| 349 | blob_init(&name, zName, -1); |
| 350 | rc = name_to_uuid(&name, -1); |
| 351 | if( rc==1 ){ |
| 352 | blob_reset(&name); |
| 353 | for(i=0; zName[i] && isdigit(zName[i]); i++){} |
| 354 | if( zName[i]==0 ){ |
| 355 | rid = atoi(zName); |
| 356 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 357 | return rid; |
| 358 | } |
| 359 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -111,20 +111,20 @@ | |
| 111 | |
| 112 | /* |
| 113 | ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. |
| 114 | */ |
| 115 | static int is_date(const char *z){ |
| 116 | if( !fossil_isdigit(z[0]) ) return 0; |
| 117 | if( !fossil_isdigit(z[1]) ) return 0; |
| 118 | if( !fossil_isdigit(z[2]) ) return 0; |
| 119 | if( !fossil_isdigit(z[3]) ) return 0; |
| 120 | if( z[4]!='-') return 0; |
| 121 | if( !fossil_isdigit(z[5]) ) return 0; |
| 122 | if( !fossil_isdigit(z[6]) ) return 0; |
| 123 | if( z[7]!='-') return 0; |
| 124 | if( !fossil_isdigit(z[8]) ) return 0; |
| 125 | if( !fossil_isdigit(z[9]) ) return 0; |
| 126 | return 1; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | ** Convert a symbolic tag name into the UUID of a check-in that contains |
| @@ -280,11 +280,11 @@ | |
| 280 | |
| 281 | if( zName==0 || zName[0]==0 ) return 0; |
| 282 | blob_init(&name, zName, -1); |
| 283 | if( name_to_uuid(&name, -1) ){ |
| 284 | blob_reset(&name); |
| 285 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 286 | if( zName[i]==0 ){ |
| 287 | rid = atoi(zName); |
| 288 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 289 | return rid; |
| 290 | } |
| @@ -348,11 +348,11 @@ | |
| 348 | if( zName==0 || zName[0]==0 ) return 0; |
| 349 | blob_init(&name, zName, -1); |
| 350 | rc = name_to_uuid(&name, -1); |
| 351 | if( rc==1 ){ |
| 352 | blob_reset(&name); |
| 353 | for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} |
| 354 | if( zName[i]==0 ){ |
| 355 | rid = atoi(zName); |
| 356 | if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ |
| 357 | return rid; |
| 358 | } |
| 359 |
+1
-1
| --- src/pqueue.c | ||
| +++ src/pqueue.c | ||
| @@ -62,11 +62,11 @@ | ||
| 62 | 62 | |
| 63 | 63 | /* |
| 64 | 64 | ** Change the size of the queue so that it contains N slots |
| 65 | 65 | */ |
| 66 | 66 | static void pqueue_resize(PQueue *p, int N){ |
| 67 | - p->a = realloc(p->a, sizeof(p->a[0])*N); | |
| 67 | + p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); | |
| 68 | 68 | p->sz = N; |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | 71 | /* |
| 72 | 72 | ** Insert element e into the queue. |
| 73 | 73 |
| --- src/pqueue.c | |
| +++ src/pqueue.c | |
| @@ -62,11 +62,11 @@ | |
| 62 | |
| 63 | /* |
| 64 | ** Change the size of the queue so that it contains N slots |
| 65 | */ |
| 66 | static void pqueue_resize(PQueue *p, int N){ |
| 67 | p->a = realloc(p->a, sizeof(p->a[0])*N); |
| 68 | p->sz = N; |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | ** Insert element e into the queue. |
| 73 |
| --- src/pqueue.c | |
| +++ src/pqueue.c | |
| @@ -62,11 +62,11 @@ | |
| 62 | |
| 63 | /* |
| 64 | ** Change the size of the queue so that it contains N slots |
| 65 | */ |
| 66 | static void pqueue_resize(PQueue *p, int N){ |
| 67 | p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); |
| 68 | p->sz = N; |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | ** Insert element e into the queue. |
| 73 |
+3
-4
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -560,11 +560,11 @@ | ||
| 560 | 560 | int i; |
| 561 | 561 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 562 | 562 | char *e = va_arg(ap,char*); |
| 563 | 563 | if( e==0 ){e="";} |
| 564 | 564 | length = StrNLen32(e, limit); |
| 565 | - zExtra = bufpt = malloc(length+1); | |
| 565 | + zExtra = bufpt = fossil_malloc(length+1); | |
| 566 | 566 | for( i=0; i<length; i++ ){ |
| 567 | 567 | if( e[i]=='\\' ){ |
| 568 | 568 | bufpt[i]='/'; |
| 569 | 569 | }else{ |
| 570 | 570 | bufpt[i]=e[i]; |
| @@ -605,11 +605,11 @@ | ||
| 605 | 605 | int i, j, n, cnt; |
| 606 | 606 | n = blob_size(pBlob); |
| 607 | 607 | if( limit>=0 && limit<n ) n = limit; |
| 608 | 608 | for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } |
| 609 | 609 | if( n+cnt+2 > etBUFSIZE ){ |
| 610 | - bufpt = zExtra = malloc( n + cnt + 2 ); | |
| 610 | + bufpt = zExtra = fossil_malloc( n + cnt + 2 ); | |
| 611 | 611 | }else{ |
| 612 | 612 | bufpt = buf; |
| 613 | 613 | } |
| 614 | 614 | bufpt[0] = '\''; |
| 615 | 615 | for(i=0, j=1; i<n; i++, j++){ |
| @@ -634,12 +634,11 @@ | ||
| 634 | 634 | if( escarg[i]=='\'' ) n++; |
| 635 | 635 | } |
| 636 | 636 | needQuote = !isnull && xtype==etSQLESCAPE2; |
| 637 | 637 | n += i + 1 + needQuote*2; |
| 638 | 638 | if( n>etBUFSIZE ){ |
| 639 | - bufpt = zExtra = malloc( n ); | |
| 640 | - if( bufpt==0 ) return -1; | |
| 639 | + bufpt = zExtra = fossil_malloc( n ); | |
| 641 | 640 | }else{ |
| 642 | 641 | bufpt = buf; |
| 643 | 642 | } |
| 644 | 643 | j = 0; |
| 645 | 644 | if( needQuote ) bufpt[j++] = '\''; |
| 646 | 645 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -560,11 +560,11 @@ | |
| 560 | int i; |
| 561 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 562 | char *e = va_arg(ap,char*); |
| 563 | if( e==0 ){e="";} |
| 564 | length = StrNLen32(e, limit); |
| 565 | zExtra = bufpt = malloc(length+1); |
| 566 | for( i=0; i<length; i++ ){ |
| 567 | if( e[i]=='\\' ){ |
| 568 | bufpt[i]='/'; |
| 569 | }else{ |
| 570 | bufpt[i]=e[i]; |
| @@ -605,11 +605,11 @@ | |
| 605 | int i, j, n, cnt; |
| 606 | n = blob_size(pBlob); |
| 607 | if( limit>=0 && limit<n ) n = limit; |
| 608 | for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } |
| 609 | if( n+cnt+2 > etBUFSIZE ){ |
| 610 | bufpt = zExtra = malloc( n + cnt + 2 ); |
| 611 | }else{ |
| 612 | bufpt = buf; |
| 613 | } |
| 614 | bufpt[0] = '\''; |
| 615 | for(i=0, j=1; i<n; i++, j++){ |
| @@ -634,12 +634,11 @@ | |
| 634 | if( escarg[i]=='\'' ) n++; |
| 635 | } |
| 636 | needQuote = !isnull && xtype==etSQLESCAPE2; |
| 637 | n += i + 1 + needQuote*2; |
| 638 | if( n>etBUFSIZE ){ |
| 639 | bufpt = zExtra = malloc( n ); |
| 640 | if( bufpt==0 ) return -1; |
| 641 | }else{ |
| 642 | bufpt = buf; |
| 643 | } |
| 644 | j = 0; |
| 645 | if( needQuote ) bufpt[j++] = '\''; |
| 646 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -560,11 +560,11 @@ | |
| 560 | int i; |
| 561 | int limit = flag_alternateform ? va_arg(ap,int) : -1; |
| 562 | char *e = va_arg(ap,char*); |
| 563 | if( e==0 ){e="";} |
| 564 | length = StrNLen32(e, limit); |
| 565 | zExtra = bufpt = fossil_malloc(length+1); |
| 566 | for( i=0; i<length; i++ ){ |
| 567 | if( e[i]=='\\' ){ |
| 568 | bufpt[i]='/'; |
| 569 | }else{ |
| 570 | bufpt[i]=e[i]; |
| @@ -605,11 +605,11 @@ | |
| 605 | int i, j, n, cnt; |
| 606 | n = blob_size(pBlob); |
| 607 | if( limit>=0 && limit<n ) n = limit; |
| 608 | for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; } |
| 609 | if( n+cnt+2 > etBUFSIZE ){ |
| 610 | bufpt = zExtra = fossil_malloc( n + cnt + 2 ); |
| 611 | }else{ |
| 612 | bufpt = buf; |
| 613 | } |
| 614 | bufpt[0] = '\''; |
| 615 | for(i=0, j=1; i<n; i++, j++){ |
| @@ -634,12 +634,11 @@ | |
| 634 | if( escarg[i]=='\'' ) n++; |
| 635 | } |
| 636 | needQuote = !isnull && xtype==etSQLESCAPE2; |
| 637 | n += i + 1 + needQuote*2; |
| 638 | if( n>etBUFSIZE ){ |
| 639 | bufpt = zExtra = fossil_malloc( n ); |
| 640 | }else{ |
| 641 | bufpt = buf; |
| 642 | } |
| 643 | j = 0; |
| 644 | if( needQuote ) bufpt[j++] = '\''; |
| 645 |
+60
-9
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -82,22 +82,36 @@ | ||
| 82 | 82 | static int ttyOutput; /* Do progress output */ |
| 83 | 83 | static Bag bagDone; /* Bag of records rebuilt */ |
| 84 | 84 | |
| 85 | 85 | static char *zFNameFormat; /* Format string for filenames on deconstruct */ |
| 86 | 86 | static int prefixLength; /* Length of directory prefix for deconstruct */ |
| 87 | + | |
| 88 | + | |
| 89 | +/* | |
| 90 | +** Draw the percent-complete message. | |
| 91 | +** The input is actually the permill complete. | |
| 92 | +*/ | |
| 93 | +static void percent_complete(int permill){ | |
| 94 | + static int lastOutput = -1; | |
| 95 | + if( permill>lastOutput ){ | |
| 96 | + printf(" %d.%d%% complete...\r", permill/10, permill%10); | |
| 97 | + fflush(stdout); | |
| 98 | + lastOutput = permill; | |
| 99 | + } | |
| 100 | +} | |
| 101 | + | |
| 87 | 102 | |
| 88 | 103 | /* |
| 89 | 104 | ** Called after each artifact is processed |
| 90 | 105 | */ |
| 91 | 106 | static void rebuild_step_done(rid){ |
| 92 | 107 | /* assert( bag_find(&bagDone, rid)==0 ); */ |
| 93 | 108 | bag_insert(&bagDone, rid); |
| 94 | 109 | if( ttyOutput ){ |
| 95 | 110 | processCnt++; |
| 96 | - if (!g.fQuiet) { | |
| 97 | - printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize)); | |
| 98 | - fflush(stdout); | |
| 111 | + if (!g.fQuiet && totalSize>0) { | |
| 112 | + percent_complete((processCnt*1000)/totalSize); | |
| 99 | 113 | } |
| 100 | 114 | } |
| 101 | 115 | } |
| 102 | 116 | |
| 103 | 117 | /* |
| @@ -167,20 +181,21 @@ | ||
| 167 | 181 | rebuild_step_done(rid); |
| 168 | 182 | |
| 169 | 183 | /* Call all children recursively */ |
| 170 | 184 | rid = 0; |
| 171 | 185 | for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ |
| 172 | - Stmt q2; | |
| 186 | + static Stmt q2; | |
| 173 | 187 | int sz; |
| 174 | - db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); | |
| 188 | + db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid"); | |
| 189 | + db_bind_int(&q2, ":rid", cid); | |
| 175 | 190 | if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ |
| 176 | 191 | Blob delta, next; |
| 177 | 192 | db_ephemeral_blob(&q2, 0, &delta); |
| 178 | 193 | blob_uncompress(&delta, &delta); |
| 179 | 194 | blob_delta_apply(pBase, &delta, &next); |
| 180 | 195 | blob_reset(&delta); |
| 181 | - db_finalize(&q2); | |
| 196 | + db_reset(&q2); | |
| 182 | 197 | if( i<nChild ){ |
| 183 | 198 | rebuild_step(cid, sz, &next); |
| 184 | 199 | }else{ |
| 185 | 200 | /* Tail recursion */ |
| 186 | 201 | rid = cid; |
| @@ -187,11 +202,11 @@ | ||
| 187 | 202 | size = sz; |
| 188 | 203 | blob_reset(pBase); |
| 189 | 204 | *pBase = next; |
| 190 | 205 | } |
| 191 | 206 | }else{ |
| 192 | - db_finalize(&q2); | |
| 207 | + db_reset(&q2); | |
| 193 | 208 | blob_reset(pBase); |
| 194 | 209 | } |
| 195 | 210 | } |
| 196 | 211 | bag_clear(&children); |
| 197 | 212 | } |
| @@ -232,17 +247,17 @@ | ||
| 232 | 247 | */ |
| 233 | 248 | int rebuild_db(int randomize, int doOut){ |
| 234 | 249 | Stmt s; |
| 235 | 250 | int errCnt = 0; |
| 236 | 251 | char *zTable; |
| 252 | + int incrSize; | |
| 237 | 253 | |
| 238 | 254 | bag_init(&bagDone); |
| 239 | 255 | ttyOutput = doOut; |
| 240 | 256 | processCnt = 0; |
| 241 | 257 | if (!g.fQuiet) { |
| 242 | - printf("0 (0%%)...\r"); | |
| 243 | - fflush(stdout); | |
| 258 | + percent_complete(0); | |
| 244 | 259 | } |
| 245 | 260 | db_multi_exec(zSchemaUpdates); |
| 246 | 261 | for(;;){ |
| 247 | 262 | zTable = db_text(0, |
| 248 | 263 | "SELECT name FROM sqlite_master /*scan*/" |
| @@ -270,10 +285,12 @@ | ||
| 270 | 285 | ); |
| 271 | 286 | db_multi_exec( |
| 272 | 287 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 273 | 288 | ); |
| 274 | 289 | totalSize = db_int(0, "SELECT count(*) FROM blob"); |
| 290 | + incrSize = totalSize/100; | |
| 291 | + totalSize += incrSize*2; | |
| 275 | 292 | db_prepare(&s, |
| 276 | 293 | "SELECT rid, size FROM blob /*scan*/" |
| 277 | 294 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 278 | 295 | " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" |
| 279 | 296 | ); |
| @@ -307,10 +324,19 @@ | ||
| 307 | 324 | } |
| 308 | 325 | } |
| 309 | 326 | db_finalize(&s); |
| 310 | 327 | manifest_crosslink_end(); |
| 311 | 328 | rebuild_tag_trunk(); |
| 329 | + if( !g.fQuiet && totalSize>0 ){ | |
| 330 | + processCnt += incrSize; | |
| 331 | + percent_complete((processCnt*1000)/totalSize); | |
| 332 | + } | |
| 333 | + create_cluster(); | |
| 334 | + if( !g.fQuiet && totalSize>0 ){ | |
| 335 | + processCnt += incrSize; | |
| 336 | + percent_complete((processCnt*1000)/totalSize); | |
| 337 | + } | |
| 312 | 338 | if(!g.fQuiet && ttyOutput ){ |
| 313 | 339 | printf("\n"); |
| 314 | 340 | } |
| 315 | 341 | return errCnt; |
| 316 | 342 | } |
| @@ -326,11 +352,13 @@ | ||
| 326 | 352 | */ |
| 327 | 353 | void rebuild_database(void){ |
| 328 | 354 | int forceFlag; |
| 329 | 355 | int randomizeFlag; |
| 330 | 356 | int errCnt; |
| 357 | + int omitVerify; | |
| 331 | 358 | |
| 359 | + omitVerify = find_option("noverify",0,0)!=0; | |
| 332 | 360 | forceFlag = find_option("force","f",0)!=0; |
| 333 | 361 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 334 | 362 | if( g.argc==3 ){ |
| 335 | 363 | db_open_repository(g.argv[2]); |
| 336 | 364 | }else{ |
| @@ -347,10 +375,11 @@ | ||
| 347 | 375 | if( errCnt && !forceFlag ){ |
| 348 | 376 | printf("%d errors. Rolling back changes. Use --force to force a commit.\n", |
| 349 | 377 | errCnt); |
| 350 | 378 | db_end_transaction(1); |
| 351 | 379 | }else{ |
| 380 | + if( omitVerify ) verify_cancel(); | |
| 352 | 381 | db_end_transaction(0); |
| 353 | 382 | } |
| 354 | 383 | } |
| 355 | 384 | |
| 356 | 385 | /* |
| @@ -371,10 +400,32 @@ | ||
| 371 | 400 | "UPDATE config SET value='detached-' || value" |
| 372 | 401 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 373 | 402 | ); |
| 374 | 403 | db_end_transaction(0); |
| 375 | 404 | } |
| 405 | + | |
| 406 | +/* | |
| 407 | +** COMMAND: test-create-clusters | |
| 408 | +** | |
| 409 | +** Create clusters for all unclustered artifacts if the number of unclustered | |
| 410 | +** artifacts exceeds the current clustering threshold. | |
| 411 | +*/ | |
| 412 | +void test_createcluster_cmd(void){ | |
| 413 | + if( g.argc==3 ){ | |
| 414 | + db_open_repository(g.argv[2]); | |
| 415 | + }else{ | |
| 416 | + db_find_and_open_repository(1); | |
| 417 | + if( g.argc!=2 ){ | |
| 418 | + usage("?REPOSITORY-FILENAME?"); | |
| 419 | + } | |
| 420 | + db_close(); | |
| 421 | + db_open_repository(g.zRepositoryName); | |
| 422 | + } | |
| 423 | + db_begin_transaction(); | |
| 424 | + create_cluster(); | |
| 425 | + db_end_transaction(0); | |
| 426 | +} | |
| 376 | 427 | |
| 377 | 428 | /* |
| 378 | 429 | ** COMMAND: scrub |
| 379 | 430 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 380 | 431 | ** |
| 381 | 432 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -82,22 +82,36 @@ | |
| 82 | static int ttyOutput; /* Do progress output */ |
| 83 | static Bag bagDone; /* Bag of records rebuilt */ |
| 84 | |
| 85 | static char *zFNameFormat; /* Format string for filenames on deconstruct */ |
| 86 | static int prefixLength; /* Length of directory prefix for deconstruct */ |
| 87 | |
| 88 | /* |
| 89 | ** Called after each artifact is processed |
| 90 | */ |
| 91 | static void rebuild_step_done(rid){ |
| 92 | /* assert( bag_find(&bagDone, rid)==0 ); */ |
| 93 | bag_insert(&bagDone, rid); |
| 94 | if( ttyOutput ){ |
| 95 | processCnt++; |
| 96 | if (!g.fQuiet) { |
| 97 | printf("%d (%d%%)...\r", processCnt, (processCnt*100/totalSize)); |
| 98 | fflush(stdout); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /* |
| @@ -167,20 +181,21 @@ | |
| 167 | rebuild_step_done(rid); |
| 168 | |
| 169 | /* Call all children recursively */ |
| 170 | rid = 0; |
| 171 | for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ |
| 172 | Stmt q2; |
| 173 | int sz; |
| 174 | db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid); |
| 175 | if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ |
| 176 | Blob delta, next; |
| 177 | db_ephemeral_blob(&q2, 0, &delta); |
| 178 | blob_uncompress(&delta, &delta); |
| 179 | blob_delta_apply(pBase, &delta, &next); |
| 180 | blob_reset(&delta); |
| 181 | db_finalize(&q2); |
| 182 | if( i<nChild ){ |
| 183 | rebuild_step(cid, sz, &next); |
| 184 | }else{ |
| 185 | /* Tail recursion */ |
| 186 | rid = cid; |
| @@ -187,11 +202,11 @@ | |
| 187 | size = sz; |
| 188 | blob_reset(pBase); |
| 189 | *pBase = next; |
| 190 | } |
| 191 | }else{ |
| 192 | db_finalize(&q2); |
| 193 | blob_reset(pBase); |
| 194 | } |
| 195 | } |
| 196 | bag_clear(&children); |
| 197 | } |
| @@ -232,17 +247,17 @@ | |
| 232 | */ |
| 233 | int rebuild_db(int randomize, int doOut){ |
| 234 | Stmt s; |
| 235 | int errCnt = 0; |
| 236 | char *zTable; |
| 237 | |
| 238 | bag_init(&bagDone); |
| 239 | ttyOutput = doOut; |
| 240 | processCnt = 0; |
| 241 | if (!g.fQuiet) { |
| 242 | printf("0 (0%%)...\r"); |
| 243 | fflush(stdout); |
| 244 | } |
| 245 | db_multi_exec(zSchemaUpdates); |
| 246 | for(;;){ |
| 247 | zTable = db_text(0, |
| 248 | "SELECT name FROM sqlite_master /*scan*/" |
| @@ -270,10 +285,12 @@ | |
| 270 | ); |
| 271 | db_multi_exec( |
| 272 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 273 | ); |
| 274 | totalSize = db_int(0, "SELECT count(*) FROM blob"); |
| 275 | db_prepare(&s, |
| 276 | "SELECT rid, size FROM blob /*scan*/" |
| 277 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 278 | " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" |
| 279 | ); |
| @@ -307,10 +324,19 @@ | |
| 307 | } |
| 308 | } |
| 309 | db_finalize(&s); |
| 310 | manifest_crosslink_end(); |
| 311 | rebuild_tag_trunk(); |
| 312 | if(!g.fQuiet && ttyOutput ){ |
| 313 | printf("\n"); |
| 314 | } |
| 315 | return errCnt; |
| 316 | } |
| @@ -326,11 +352,13 @@ | |
| 326 | */ |
| 327 | void rebuild_database(void){ |
| 328 | int forceFlag; |
| 329 | int randomizeFlag; |
| 330 | int errCnt; |
| 331 | |
| 332 | forceFlag = find_option("force","f",0)!=0; |
| 333 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 334 | if( g.argc==3 ){ |
| 335 | db_open_repository(g.argv[2]); |
| 336 | }else{ |
| @@ -347,10 +375,11 @@ | |
| 347 | if( errCnt && !forceFlag ){ |
| 348 | printf("%d errors. Rolling back changes. Use --force to force a commit.\n", |
| 349 | errCnt); |
| 350 | db_end_transaction(1); |
| 351 | }else{ |
| 352 | db_end_transaction(0); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* |
| @@ -371,10 +400,32 @@ | |
| 371 | "UPDATE config SET value='detached-' || value" |
| 372 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 373 | ); |
| 374 | db_end_transaction(0); |
| 375 | } |
| 376 | |
| 377 | /* |
| 378 | ** COMMAND: scrub |
| 379 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 380 | ** |
| 381 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -82,22 +82,36 @@ | |
| 82 | static int ttyOutput; /* Do progress output */ |
| 83 | static Bag bagDone; /* Bag of records rebuilt */ |
| 84 | |
| 85 | static char *zFNameFormat; /* Format string for filenames on deconstruct */ |
| 86 | static int prefixLength; /* Length of directory prefix for deconstruct */ |
| 87 | |
| 88 | |
| 89 | /* |
| 90 | ** Draw the percent-complete message. |
| 91 | ** The input is actually the permill complete. |
| 92 | */ |
| 93 | static void percent_complete(int permill){ |
| 94 | static int lastOutput = -1; |
| 95 | if( permill>lastOutput ){ |
| 96 | printf(" %d.%d%% complete...\r", permill/10, permill%10); |
| 97 | fflush(stdout); |
| 98 | lastOutput = permill; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | |
| 103 | /* |
| 104 | ** Called after each artifact is processed |
| 105 | */ |
| 106 | static void rebuild_step_done(rid){ |
| 107 | /* assert( bag_find(&bagDone, rid)==0 ); */ |
| 108 | bag_insert(&bagDone, rid); |
| 109 | if( ttyOutput ){ |
| 110 | processCnt++; |
| 111 | if (!g.fQuiet && totalSize>0) { |
| 112 | percent_complete((processCnt*1000)/totalSize); |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /* |
| @@ -167,20 +181,21 @@ | |
| 181 | rebuild_step_done(rid); |
| 182 | |
| 183 | /* Call all children recursively */ |
| 184 | rid = 0; |
| 185 | for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ |
| 186 | static Stmt q2; |
| 187 | int sz; |
| 188 | db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid"); |
| 189 | db_bind_int(&q2, ":rid", cid); |
| 190 | if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){ |
| 191 | Blob delta, next; |
| 192 | db_ephemeral_blob(&q2, 0, &delta); |
| 193 | blob_uncompress(&delta, &delta); |
| 194 | blob_delta_apply(pBase, &delta, &next); |
| 195 | blob_reset(&delta); |
| 196 | db_reset(&q2); |
| 197 | if( i<nChild ){ |
| 198 | rebuild_step(cid, sz, &next); |
| 199 | }else{ |
| 200 | /* Tail recursion */ |
| 201 | rid = cid; |
| @@ -187,11 +202,11 @@ | |
| 202 | size = sz; |
| 203 | blob_reset(pBase); |
| 204 | *pBase = next; |
| 205 | } |
| 206 | }else{ |
| 207 | db_reset(&q2); |
| 208 | blob_reset(pBase); |
| 209 | } |
| 210 | } |
| 211 | bag_clear(&children); |
| 212 | } |
| @@ -232,17 +247,17 @@ | |
| 247 | */ |
| 248 | int rebuild_db(int randomize, int doOut){ |
| 249 | Stmt s; |
| 250 | int errCnt = 0; |
| 251 | char *zTable; |
| 252 | int incrSize; |
| 253 | |
| 254 | bag_init(&bagDone); |
| 255 | ttyOutput = doOut; |
| 256 | processCnt = 0; |
| 257 | if (!g.fQuiet) { |
| 258 | percent_complete(0); |
| 259 | } |
| 260 | db_multi_exec(zSchemaUpdates); |
| 261 | for(;;){ |
| 262 | zTable = db_text(0, |
| 263 | "SELECT name FROM sqlite_master /*scan*/" |
| @@ -270,10 +285,12 @@ | |
| 285 | ); |
| 286 | db_multi_exec( |
| 287 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 288 | ); |
| 289 | totalSize = db_int(0, "SELECT count(*) FROM blob"); |
| 290 | incrSize = totalSize/100; |
| 291 | totalSize += incrSize*2; |
| 292 | db_prepare(&s, |
| 293 | "SELECT rid, size FROM blob /*scan*/" |
| 294 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 295 | " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" |
| 296 | ); |
| @@ -307,10 +324,19 @@ | |
| 324 | } |
| 325 | } |
| 326 | db_finalize(&s); |
| 327 | manifest_crosslink_end(); |
| 328 | rebuild_tag_trunk(); |
| 329 | if( !g.fQuiet && totalSize>0 ){ |
| 330 | processCnt += incrSize; |
| 331 | percent_complete((processCnt*1000)/totalSize); |
| 332 | } |
| 333 | create_cluster(); |
| 334 | if( !g.fQuiet && totalSize>0 ){ |
| 335 | processCnt += incrSize; |
| 336 | percent_complete((processCnt*1000)/totalSize); |
| 337 | } |
| 338 | if(!g.fQuiet && ttyOutput ){ |
| 339 | printf("\n"); |
| 340 | } |
| 341 | return errCnt; |
| 342 | } |
| @@ -326,11 +352,13 @@ | |
| 352 | */ |
| 353 | void rebuild_database(void){ |
| 354 | int forceFlag; |
| 355 | int randomizeFlag; |
| 356 | int errCnt; |
| 357 | int omitVerify; |
| 358 | |
| 359 | omitVerify = find_option("noverify",0,0)!=0; |
| 360 | forceFlag = find_option("force","f",0)!=0; |
| 361 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 362 | if( g.argc==3 ){ |
| 363 | db_open_repository(g.argv[2]); |
| 364 | }else{ |
| @@ -347,10 +375,11 @@ | |
| 375 | if( errCnt && !forceFlag ){ |
| 376 | printf("%d errors. Rolling back changes. Use --force to force a commit.\n", |
| 377 | errCnt); |
| 378 | db_end_transaction(1); |
| 379 | }else{ |
| 380 | if( omitVerify ) verify_cancel(); |
| 381 | db_end_transaction(0); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | /* |
| @@ -371,10 +400,32 @@ | |
| 400 | "UPDATE config SET value='detached-' || value" |
| 401 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 402 | ); |
| 403 | db_end_transaction(0); |
| 404 | } |
| 405 | |
| 406 | /* |
| 407 | ** COMMAND: test-create-clusters |
| 408 | ** |
| 409 | ** Create clusters for all unclustered artifacts if the number of unclustered |
| 410 | ** artifacts exceeds the current clustering threshold. |
| 411 | */ |
| 412 | void test_createcluster_cmd(void){ |
| 413 | if( g.argc==3 ){ |
| 414 | db_open_repository(g.argv[2]); |
| 415 | }else{ |
| 416 | db_find_and_open_repository(1); |
| 417 | if( g.argc!=2 ){ |
| 418 | usage("?REPOSITORY-FILENAME?"); |
| 419 | } |
| 420 | db_close(); |
| 421 | db_open_repository(g.zRepositoryName); |
| 422 | } |
| 423 | db_begin_transaction(); |
| 424 | create_cluster(); |
| 425 | db_end_transaction(0); |
| 426 | } |
| 427 | |
| 428 | /* |
| 429 | ** COMMAND: scrub |
| 430 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 431 | ** |
| 432 |
+180
-46
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -88,22 +88,22 @@ | ||
| 88 | 88 | /* |
| 89 | 89 | ** Remove whitespace from both ends of a string. |
| 90 | 90 | */ |
| 91 | 91 | char *trim_string(const char *zOrig){ |
| 92 | 92 | int i; |
| 93 | - while( isspace(*zOrig) ){ zOrig++; } | |
| 93 | + while( fossil_isspace(*zOrig) ){ zOrig++; } | |
| 94 | 94 | i = strlen(zOrig); |
| 95 | - while( i>0 && isspace(zOrig[i-1]) ){ i--; } | |
| 95 | + while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; } | |
| 96 | 96 | return mprintf("%.*s", i, zOrig); |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | 99 | /* |
| 100 | 100 | ** Extract a numeric (integer) value from a string. |
| 101 | 101 | */ |
| 102 | 102 | char *extract_integer(const char *zOrig){ |
| 103 | 103 | if( zOrig == NULL || zOrig[0] == 0 ) return ""; |
| 104 | - while( *zOrig && !isdigit(*zOrig) ){ zOrig++; } | |
| 104 | + while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; } | |
| 105 | 105 | if( *zOrig ){ |
| 106 | 106 | /* we have a digit. atoi() will get as much of the number as it |
| 107 | 107 | ** can. We'll run it through mprintf() to get a string. Not |
| 108 | 108 | ** an efficient way to do it, but effective. |
| 109 | 109 | */ |
| @@ -118,18 +118,18 @@ | ||
| 118 | 118 | ** which also converts any CRNL sequence into a single NL. |
| 119 | 119 | */ |
| 120 | 120 | char *remove_blank_lines(const char *zOrig){ |
| 121 | 121 | int i, j, n; |
| 122 | 122 | char *z; |
| 123 | - for(i=j=0; isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } | |
| 123 | + for(i=j=0; fossil_isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } | |
| 124 | 124 | n = strlen(&zOrig[j]); |
| 125 | - while( n>0 && isspace(zOrig[j+n-1]) ){ n--; } | |
| 125 | + while( n>0 && fossil_isspace(zOrig[j+n-1]) ){ n--; } | |
| 126 | 126 | z = mprintf("%.*s", n, &zOrig[j]); |
| 127 | 127 | for(i=j=0; z[i]; i++){ |
| 128 | - if( z[i+1]=='\n' && z[i]!='\n' && isspace(z[i]) ){ | |
| 128 | + if( z[i+1]=='\n' && z[i]!='\n' && fossil_isspace(z[i]) ){ | |
| 129 | 129 | z[j] = z[i]; |
| 130 | - while(isspace(z[j]) && z[j] != '\n' ){ j--; } | |
| 130 | + while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; } | |
| 131 | 131 | j++; |
| 132 | 132 | continue; |
| 133 | 133 | } |
| 134 | 134 | |
| 135 | 135 | z[j++] = z[i]; |
| @@ -145,11 +145,11 @@ | ||
| 145 | 145 | ** This is the SQLite authorizer callback used to make sure that the |
| 146 | 146 | ** SQL statements entered by users do not try to do anything untoward. |
| 147 | 147 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 148 | 148 | ** message obtained from malloc. |
| 149 | 149 | */ |
| 150 | -static int report_query_authorizer( | |
| 150 | +int report_query_authorizer( | |
| 151 | 151 | void *pError, |
| 152 | 152 | int code, |
| 153 | 153 | const char *zArg1, |
| 154 | 154 | const char *zArg2, |
| 155 | 155 | const char *zArg3, |
| @@ -212,11 +212,11 @@ | ||
| 212 | 212 | int rc; |
| 213 | 213 | |
| 214 | 214 | /* First make sure the SQL is a single query command by verifying that |
| 215 | 215 | ** the first token is "SELECT" and that there are no unquoted semicolons. |
| 216 | 216 | */ |
| 217 | - for(i=0; isspace(zSql[i]); i++){} | |
| 217 | + for(i=0; fossil_isspace(zSql[i]); i++){} | |
| 218 | 218 | if( strncasecmp(&zSql[i],"select",6)!=0 ){ |
| 219 | 219 | return mprintf("The SQL must be a SELECT statement"); |
| 220 | 220 | } |
| 221 | 221 | for(i=0; zSql[i]; i++){ |
| 222 | 222 | if( zSql[i]==';' ){ |
| @@ -583,39 +583,10 @@ | ||
| 583 | 583 | @ FROM ticket |
| 584 | 584 | @ </pre></blockquote> |
| 585 | 585 | @ |
| 586 | 586 | } |
| 587 | 587 | |
| 588 | -#if 0 /* NOT USED */ | |
| 589 | -static void column_header(int rn,const char *zCol, int nCol, int nSorted, | |
| 590 | - const char *zDirection, const char *zExtra | |
| 591 | -){ | |
| 592 | - int set = (nCol==nSorted); | |
| 593 | - int desc = !strcmp(zDirection,"DESC"); | |
| 594 | - | |
| 595 | - /* | |
| 596 | - ** Clicking same column header 3 times in a row resets any sorting. | |
| 597 | - ** Note that we link to rptview, which means embedded reports will get | |
| 598 | - ** sent to the actual report view page as soon as a user tries to do | |
| 599 | - ** any sorting. I don't see that as a Bad Thing. | |
| 600 | - */ | |
| 601 | - if(set && desc){ | |
| 602 | - @ <th bgcolor="%s(BG1)" class="bkgnd1"> | |
| 603 | - @ <a href="rptview?rn=%d(rn)%s(zExtra)">%h(zCol)</a></th> | |
| 604 | - }else{ | |
| 605 | - if(set){ | |
| 606 | - @ <th bgcolor="%s(BG1)" class="bkgnd1"><a | |
| 607 | - }else{ | |
| 608 | - @ <th><a | |
| 609 | - } | |
| 610 | - @ href="rptview?rn=%d(rn)&order_by=%d(nCol)&\ | |
| 611 | - @ order_dir=%s(desc?"ASC":"DESC")\ | |
| 612 | - @ %s(zExtra)">%h(zCol)</a></th> | |
| 613 | - } | |
| 614 | -} | |
| 615 | -#endif | |
| 616 | - | |
| 617 | 588 | /* |
| 618 | 589 | ** The state of the report generation. |
| 619 | 590 | */ |
| 620 | 591 | struct GenerateHTML { |
| 621 | 592 | int rn; /* Report number */ |
| @@ -723,11 +694,11 @@ | ||
| 723 | 694 | |
| 724 | 695 | /* Output the data for this entry from the database |
| 725 | 696 | */ |
| 726 | 697 | zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; |
| 727 | 698 | if( zBg==0 ) zBg = "white"; |
| 728 | - @ <tr bgcolor="%h(zBg)"> | |
| 699 | + @ <tr style="background-color:%h(zBg)"> | |
| 729 | 700 | zTid = 0; |
| 730 | 701 | zPage[0] = 0; |
| 731 | 702 | for(i=0; i<nArg; i++){ |
| 732 | 703 | char *zData; |
| 733 | 704 | if( i==pState->iBg ) continue; |
| @@ -738,11 +709,11 @@ | ||
| 738 | 709 | @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td> |
| 739 | 710 | zTid = 0; |
| 740 | 711 | } |
| 741 | 712 | if( zData[0] ){ |
| 742 | 713 | Blob content; |
| 743 | - @ </tr><tr bgcolor="%h(zBg)"><td colspan=%d(pState->nCol)> | |
| 714 | + @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)> | |
| 744 | 715 | blob_init(&content, zData, -1); |
| 745 | 716 | wiki_convert(&content, 0, 0); |
| 746 | 717 | blob_reset(&content); |
| 747 | 718 | } |
| 748 | 719 | }else if( azName[i][0]=='#' ){ |
| @@ -772,15 +743,15 @@ | ||
| 772 | 743 | ** spaces. |
| 773 | 744 | */ |
| 774 | 745 | static void output_no_tabs(const char *z){ |
| 775 | 746 | while( z && z[0] ){ |
| 776 | 747 | int i, j; |
| 777 | - for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){} | |
| 748 | + for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} | |
| 778 | 749 | if( i>0 ){ |
| 779 | 750 | cgi_printf("%.*s", i, z); |
| 780 | 751 | } |
| 781 | - for(j=i; isspace(z[j]); j++){} | |
| 752 | + for(j=i; fossil_isspace(z[j]); j++){} | |
| 782 | 753 | if( j>i ){ |
| 783 | 754 | cgi_printf("%*s", j-i, ""); |
| 784 | 755 | } |
| 785 | 756 | z += j; |
| 786 | 757 | } |
| @@ -816,21 +787,21 @@ | ||
| 816 | 787 | ** Generate HTML that describes a color key. |
| 817 | 788 | */ |
| 818 | 789 | void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ |
| 819 | 790 | int i, j, k; |
| 820 | 791 | char *zSafeKey, *zToFree; |
| 821 | - while( isspace(*zClrKey) ) zClrKey++; | |
| 792 | + while( fossil_isspace(*zClrKey) ) zClrKey++; | |
| 822 | 793 | if( zClrKey[0]==0 ) return; |
| 823 | 794 | @ <table %s(zTabArgs)> |
| 824 | 795 | if( horiz ){ |
| 825 | 796 | @ <tr> |
| 826 | 797 | } |
| 827 | 798 | zToFree = zSafeKey = mprintf("%h", zClrKey); |
| 828 | 799 | while( zSafeKey[0] ){ |
| 829 | - while( isspace(*zSafeKey) ) zSafeKey++; | |
| 830 | - for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){} | |
| 831 | - for(j=i; isspace(zSafeKey[j]); j++){} | |
| 800 | + while( fossil_isspace(*zSafeKey) ) zSafeKey++; | |
| 801 | + for(i=0; zSafeKey[i] && !fossil_isspace(zSafeKey[i]); i++){} | |
| 802 | + for(j=i; fossil_isspace(zSafeKey[j]); j++){} | |
| 832 | 803 | for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){} |
| 833 | 804 | if( !horiz ){ |
| 834 | 805 | cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n", |
| 835 | 806 | i, zSafeKey, k-j, &zSafeKey[j]); |
| 836 | 807 | }else{ |
| @@ -942,5 +913,168 @@ | ||
| 942 | 913 | sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 943 | 914 | sqlite3_set_authorizer(g.db, 0, 0); |
| 944 | 915 | cgi_set_content_type("text/plain"); |
| 945 | 916 | } |
| 946 | 917 | } |
| 918 | + | |
| 919 | +/* | |
| 920 | +** report number for full table ticket export | |
| 921 | +*/ | |
| 922 | +static const char zFullTicketRptRn[] = "0"; | |
| 923 | + | |
| 924 | +/* | |
| 925 | +** report title for full table ticket export | |
| 926 | +*/ | |
| 927 | +static const char zFullTicketRptTitle[] = "full ticket export"; | |
| 928 | + | |
| 929 | +/* | |
| 930 | +** show all reports, which can be used for ticket show. | |
| 931 | +** Output is written to stdout as tab delimited table | |
| 932 | +*/ | |
| 933 | +void rpt_list_reports(void){ | |
| 934 | + Stmt q; | |
| 935 | + char const aRptOutFrmt[] = "%s\t%s\n"; | |
| 936 | + | |
| 937 | + printf("Available reports:\n"); | |
| 938 | + printf(aRptOutFrmt,"report number","report title"); | |
| 939 | + printf(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle); | |
| 940 | + db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn"); | |
| 941 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 942 | + const char *zRn = db_column_text(&q, 0); | |
| 943 | + const char *zTitle = db_column_text(&q, 1); | |
| 944 | + | |
| 945 | + printf(aRptOutFrmt,zRn,zTitle); | |
| 946 | + } | |
| 947 | + db_finalize(&q); | |
| 948 | +} | |
| 949 | + | |
| 950 | +/* | |
| 951 | +** user defined separator used by ticket show command | |
| 952 | +*/ | |
| 953 | +static const char *zSep = 0; | |
| 954 | + | |
| 955 | +/* | |
| 956 | +** select the quoting algorithm for "ticket show" | |
| 957 | +*/ | |
| 958 | +#if INTERFACE | |
| 959 | +typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding; | |
| 960 | +#endif | |
| 961 | +static tTktShowEncoding tktEncode = tktNoTab; | |
| 962 | + | |
| 963 | +/* | |
| 964 | +** Output the text given in the argument. Convert tabs and newlines into | |
| 965 | +** spaces. | |
| 966 | +*/ | |
| 967 | +static void output_no_tabs_file(const char *z){ | |
| 968 | + switch( tktEncode ){ | |
| 969 | + case tktFossilize: | |
| 970 | + { char *zFosZ; | |
| 971 | + | |
| 972 | + if( z && *z ){ | |
| 973 | + zFosZ = fossilize(z,-1); | |
| 974 | + printf("%s",zFosZ); | |
| 975 | + free(zFosZ); | |
| 976 | + } | |
| 977 | + break; | |
| 978 | + } | |
| 979 | + default: | |
| 980 | + while( z && z[0] ){ | |
| 981 | + int i, j; | |
| 982 | + for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} | |
| 983 | + if( i>0 ){ | |
| 984 | + printf("%.*s", i, z); | |
| 985 | + } | |
| 986 | + for(j=i; fossil_isspace(z[j]); j++){} | |
| 987 | + if( j>i ){ | |
| 988 | + printf("%*s", j-i, ""); | |
| 989 | + } | |
| 990 | + z += j; | |
| 991 | + } | |
| 992 | + break; | |
| 993 | + } | |
| 994 | +} | |
| 995 | + | |
| 996 | +/* | |
| 997 | +** Output a row as a tab-separated line of text. | |
| 998 | +*/ | |
| 999 | +int output_separated_file( | |
| 1000 | + void *pUser, /* Pointer to row-count integer */ | |
| 1001 | + int nArg, /* Number of columns in this result row */ | |
| 1002 | + char **azArg, /* Text of data in all columns */ | |
| 1003 | + char **azName /* Names of the columns */ | |
| 1004 | +){ | |
| 1005 | + int *pCount = (int*)pUser; | |
| 1006 | + int i; | |
| 1007 | + | |
| 1008 | + if( *pCount==0 ){ | |
| 1009 | + for(i=0; i<nArg; i++){ | |
| 1010 | + output_no_tabs_file(azName[i]); | |
| 1011 | + printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); | |
| 1012 | + } | |
| 1013 | + } | |
| 1014 | + ++*pCount; | |
| 1015 | + for(i=0; i<nArg; i++){ | |
| 1016 | + output_no_tabs_file(azArg[i]); | |
| 1017 | + printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); | |
| 1018 | + } | |
| 1019 | + return 0; | |
| 1020 | +} | |
| 1021 | + | |
| 1022 | +/* | |
| 1023 | +** Generate a report. The rn query parameter is the report number. | |
| 1024 | +** The output is written to stdout as flat file. The zFilter paramater | |
| 1025 | +** is a full WHERE-condition. | |
| 1026 | +*/ | |
| 1027 | +void rptshow( | |
| 1028 | + const char *zRep, | |
| 1029 | + const char *zSepIn, | |
| 1030 | + const char *zFilter, | |
| 1031 | + tTktShowEncoding enc | |
| 1032 | +){ | |
| 1033 | + Stmt q; | |
| 1034 | + char *zSql; | |
| 1035 | + const char *zTitle; | |
| 1036 | + const char *zOwner; | |
| 1037 | + const char *zClrKey; | |
| 1038 | + char *zErr1 = 0; | |
| 1039 | + char *zErr2 = 0; | |
| 1040 | + int count = 0; | |
| 1041 | + int rn; | |
| 1042 | + | |
| 1043 | + if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){ | |
| 1044 | + zTitle = zFullTicketRptTitle; | |
| 1045 | + zSql = "SELECT * FROM ticket"; | |
| 1046 | + zOwner = g.zLogin; | |
| 1047 | + zClrKey = ""; | |
| 1048 | + }else{ | |
| 1049 | + rn = atoi(zRep); | |
| 1050 | + if( rn ){ | |
| 1051 | + db_prepare(&q, | |
| 1052 | + "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn); | |
| 1053 | + }else{ | |
| 1054 | + db_prepare(&q, | |
| 1055 | + "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep); | |
| 1056 | + } | |
| 1057 | + if( db_step(&q)!=SQLITE_ROW ){ | |
| 1058 | + db_finalize(&q); | |
| 1059 | + rpt_list_reports(); | |
| 1060 | + fossil_fatal("unkown report format(%s)!",zRep); | |
| 1061 | + } | |
| 1062 | + zTitle = db_column_malloc(&q, 0); | |
| 1063 | + zSql = db_column_malloc(&q, 1); | |
| 1064 | + zOwner = db_column_malloc(&q, 2); | |
| 1065 | + zClrKey = db_column_malloc(&q, 3); | |
| 1066 | + db_finalize(&q); | |
| 1067 | + } | |
| 1068 | + if( zFilter ){ | |
| 1069 | + zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); | |
| 1070 | + } | |
| 1071 | + count = 0; | |
| 1072 | + tktEncode = enc; | |
| 1073 | + zSep = zSepIn; | |
| 1074 | + sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); | |
| 1075 | + sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2); | |
| 1076 | + sqlite3_set_authorizer(g.db, 0, 0); | |
| 1077 | + if( zFilter ){ | |
| 1078 | + free(zSql); | |
| 1079 | + } | |
| 1080 | +} | |
| 947 | 1081 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -88,22 +88,22 @@ | |
| 88 | /* |
| 89 | ** Remove whitespace from both ends of a string. |
| 90 | */ |
| 91 | char *trim_string(const char *zOrig){ |
| 92 | int i; |
| 93 | while( isspace(*zOrig) ){ zOrig++; } |
| 94 | i = strlen(zOrig); |
| 95 | while( i>0 && isspace(zOrig[i-1]) ){ i--; } |
| 96 | return mprintf("%.*s", i, zOrig); |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | ** Extract a numeric (integer) value from a string. |
| 101 | */ |
| 102 | char *extract_integer(const char *zOrig){ |
| 103 | if( zOrig == NULL || zOrig[0] == 0 ) return ""; |
| 104 | while( *zOrig && !isdigit(*zOrig) ){ zOrig++; } |
| 105 | if( *zOrig ){ |
| 106 | /* we have a digit. atoi() will get as much of the number as it |
| 107 | ** can. We'll run it through mprintf() to get a string. Not |
| 108 | ** an efficient way to do it, but effective. |
| 109 | */ |
| @@ -118,18 +118,18 @@ | |
| 118 | ** which also converts any CRNL sequence into a single NL. |
| 119 | */ |
| 120 | char *remove_blank_lines(const char *zOrig){ |
| 121 | int i, j, n; |
| 122 | char *z; |
| 123 | for(i=j=0; isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } |
| 124 | n = strlen(&zOrig[j]); |
| 125 | while( n>0 && isspace(zOrig[j+n-1]) ){ n--; } |
| 126 | z = mprintf("%.*s", n, &zOrig[j]); |
| 127 | for(i=j=0; z[i]; i++){ |
| 128 | if( z[i+1]=='\n' && z[i]!='\n' && isspace(z[i]) ){ |
| 129 | z[j] = z[i]; |
| 130 | while(isspace(z[j]) && z[j] != '\n' ){ j--; } |
| 131 | j++; |
| 132 | continue; |
| 133 | } |
| 134 | |
| 135 | z[j++] = z[i]; |
| @@ -145,11 +145,11 @@ | |
| 145 | ** This is the SQLite authorizer callback used to make sure that the |
| 146 | ** SQL statements entered by users do not try to do anything untoward. |
| 147 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 148 | ** message obtained from malloc. |
| 149 | */ |
| 150 | static int report_query_authorizer( |
| 151 | void *pError, |
| 152 | int code, |
| 153 | const char *zArg1, |
| 154 | const char *zArg2, |
| 155 | const char *zArg3, |
| @@ -212,11 +212,11 @@ | |
| 212 | int rc; |
| 213 | |
| 214 | /* First make sure the SQL is a single query command by verifying that |
| 215 | ** the first token is "SELECT" and that there are no unquoted semicolons. |
| 216 | */ |
| 217 | for(i=0; isspace(zSql[i]); i++){} |
| 218 | if( strncasecmp(&zSql[i],"select",6)!=0 ){ |
| 219 | return mprintf("The SQL must be a SELECT statement"); |
| 220 | } |
| 221 | for(i=0; zSql[i]; i++){ |
| 222 | if( zSql[i]==';' ){ |
| @@ -583,39 +583,10 @@ | |
| 583 | @ FROM ticket |
| 584 | @ </pre></blockquote> |
| 585 | @ |
| 586 | } |
| 587 | |
| 588 | #if 0 /* NOT USED */ |
| 589 | static void column_header(int rn,const char *zCol, int nCol, int nSorted, |
| 590 | const char *zDirection, const char *zExtra |
| 591 | ){ |
| 592 | int set = (nCol==nSorted); |
| 593 | int desc = !strcmp(zDirection,"DESC"); |
| 594 | |
| 595 | /* |
| 596 | ** Clicking same column header 3 times in a row resets any sorting. |
| 597 | ** Note that we link to rptview, which means embedded reports will get |
| 598 | ** sent to the actual report view page as soon as a user tries to do |
| 599 | ** any sorting. I don't see that as a Bad Thing. |
| 600 | */ |
| 601 | if(set && desc){ |
| 602 | @ <th bgcolor="%s(BG1)" class="bkgnd1"> |
| 603 | @ <a href="rptview?rn=%d(rn)%s(zExtra)">%h(zCol)</a></th> |
| 604 | }else{ |
| 605 | if(set){ |
| 606 | @ <th bgcolor="%s(BG1)" class="bkgnd1"><a |
| 607 | }else{ |
| 608 | @ <th><a |
| 609 | } |
| 610 | @ href="rptview?rn=%d(rn)&order_by=%d(nCol)&\ |
| 611 | @ order_dir=%s(desc?"ASC":"DESC")\ |
| 612 | @ %s(zExtra)">%h(zCol)</a></th> |
| 613 | } |
| 614 | } |
| 615 | #endif |
| 616 | |
| 617 | /* |
| 618 | ** The state of the report generation. |
| 619 | */ |
| 620 | struct GenerateHTML { |
| 621 | int rn; /* Report number */ |
| @@ -723,11 +694,11 @@ | |
| 723 | |
| 724 | /* Output the data for this entry from the database |
| 725 | */ |
| 726 | zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; |
| 727 | if( zBg==0 ) zBg = "white"; |
| 728 | @ <tr bgcolor="%h(zBg)"> |
| 729 | zTid = 0; |
| 730 | zPage[0] = 0; |
| 731 | for(i=0; i<nArg; i++){ |
| 732 | char *zData; |
| 733 | if( i==pState->iBg ) continue; |
| @@ -738,11 +709,11 @@ | |
| 738 | @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td> |
| 739 | zTid = 0; |
| 740 | } |
| 741 | if( zData[0] ){ |
| 742 | Blob content; |
| 743 | @ </tr><tr bgcolor="%h(zBg)"><td colspan=%d(pState->nCol)> |
| 744 | blob_init(&content, zData, -1); |
| 745 | wiki_convert(&content, 0, 0); |
| 746 | blob_reset(&content); |
| 747 | } |
| 748 | }else if( azName[i][0]=='#' ){ |
| @@ -772,15 +743,15 @@ | |
| 772 | ** spaces. |
| 773 | */ |
| 774 | static void output_no_tabs(const char *z){ |
| 775 | while( z && z[0] ){ |
| 776 | int i, j; |
| 777 | for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){} |
| 778 | if( i>0 ){ |
| 779 | cgi_printf("%.*s", i, z); |
| 780 | } |
| 781 | for(j=i; isspace(z[j]); j++){} |
| 782 | if( j>i ){ |
| 783 | cgi_printf("%*s", j-i, ""); |
| 784 | } |
| 785 | z += j; |
| 786 | } |
| @@ -816,21 +787,21 @@ | |
| 816 | ** Generate HTML that describes a color key. |
| 817 | */ |
| 818 | void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ |
| 819 | int i, j, k; |
| 820 | char *zSafeKey, *zToFree; |
| 821 | while( isspace(*zClrKey) ) zClrKey++; |
| 822 | if( zClrKey[0]==0 ) return; |
| 823 | @ <table %s(zTabArgs)> |
| 824 | if( horiz ){ |
| 825 | @ <tr> |
| 826 | } |
| 827 | zToFree = zSafeKey = mprintf("%h", zClrKey); |
| 828 | while( zSafeKey[0] ){ |
| 829 | while( isspace(*zSafeKey) ) zSafeKey++; |
| 830 | for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){} |
| 831 | for(j=i; isspace(zSafeKey[j]); j++){} |
| 832 | for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){} |
| 833 | if( !horiz ){ |
| 834 | cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n", |
| 835 | i, zSafeKey, k-j, &zSafeKey[j]); |
| 836 | }else{ |
| @@ -942,5 +913,168 @@ | |
| 942 | sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 943 | sqlite3_set_authorizer(g.db, 0, 0); |
| 944 | cgi_set_content_type("text/plain"); |
| 945 | } |
| 946 | } |
| 947 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -88,22 +88,22 @@ | |
| 88 | /* |
| 89 | ** Remove whitespace from both ends of a string. |
| 90 | */ |
| 91 | char *trim_string(const char *zOrig){ |
| 92 | int i; |
| 93 | while( fossil_isspace(*zOrig) ){ zOrig++; } |
| 94 | i = strlen(zOrig); |
| 95 | while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; } |
| 96 | return mprintf("%.*s", i, zOrig); |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | ** Extract a numeric (integer) value from a string. |
| 101 | */ |
| 102 | char *extract_integer(const char *zOrig){ |
| 103 | if( zOrig == NULL || zOrig[0] == 0 ) return ""; |
| 104 | while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; } |
| 105 | if( *zOrig ){ |
| 106 | /* we have a digit. atoi() will get as much of the number as it |
| 107 | ** can. We'll run it through mprintf() to get a string. Not |
| 108 | ** an efficient way to do it, but effective. |
| 109 | */ |
| @@ -118,18 +118,18 @@ | |
| 118 | ** which also converts any CRNL sequence into a single NL. |
| 119 | */ |
| 120 | char *remove_blank_lines(const char *zOrig){ |
| 121 | int i, j, n; |
| 122 | char *z; |
| 123 | for(i=j=0; fossil_isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; } |
| 124 | n = strlen(&zOrig[j]); |
| 125 | while( n>0 && fossil_isspace(zOrig[j+n-1]) ){ n--; } |
| 126 | z = mprintf("%.*s", n, &zOrig[j]); |
| 127 | for(i=j=0; z[i]; i++){ |
| 128 | if( z[i+1]=='\n' && z[i]!='\n' && fossil_isspace(z[i]) ){ |
| 129 | z[j] = z[i]; |
| 130 | while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; } |
| 131 | j++; |
| 132 | continue; |
| 133 | } |
| 134 | |
| 135 | z[j++] = z[i]; |
| @@ -145,11 +145,11 @@ | |
| 145 | ** This is the SQLite authorizer callback used to make sure that the |
| 146 | ** SQL statements entered by users do not try to do anything untoward. |
| 147 | ** If anything suspicious is tried, set *(char**)pError to an error |
| 148 | ** message obtained from malloc. |
| 149 | */ |
| 150 | int report_query_authorizer( |
| 151 | void *pError, |
| 152 | int code, |
| 153 | const char *zArg1, |
| 154 | const char *zArg2, |
| 155 | const char *zArg3, |
| @@ -212,11 +212,11 @@ | |
| 212 | int rc; |
| 213 | |
| 214 | /* First make sure the SQL is a single query command by verifying that |
| 215 | ** the first token is "SELECT" and that there are no unquoted semicolons. |
| 216 | */ |
| 217 | for(i=0; fossil_isspace(zSql[i]); i++){} |
| 218 | if( strncasecmp(&zSql[i],"select",6)!=0 ){ |
| 219 | return mprintf("The SQL must be a SELECT statement"); |
| 220 | } |
| 221 | for(i=0; zSql[i]; i++){ |
| 222 | if( zSql[i]==';' ){ |
| @@ -583,39 +583,10 @@ | |
| 583 | @ FROM ticket |
| 584 | @ </pre></blockquote> |
| 585 | @ |
| 586 | } |
| 587 | |
| 588 | /* |
| 589 | ** The state of the report generation. |
| 590 | */ |
| 591 | struct GenerateHTML { |
| 592 | int rn; /* Report number */ |
| @@ -723,11 +694,11 @@ | |
| 694 | |
| 695 | /* Output the data for this entry from the database |
| 696 | */ |
| 697 | zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; |
| 698 | if( zBg==0 ) zBg = "white"; |
| 699 | @ <tr style="background-color:%h(zBg)"> |
| 700 | zTid = 0; |
| 701 | zPage[0] = 0; |
| 702 | for(i=0; i<nArg; i++){ |
| 703 | char *zData; |
| 704 | if( i==pState->iBg ) continue; |
| @@ -738,11 +709,11 @@ | |
| 709 | @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td> |
| 710 | zTid = 0; |
| 711 | } |
| 712 | if( zData[0] ){ |
| 713 | Blob content; |
| 714 | @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)> |
| 715 | blob_init(&content, zData, -1); |
| 716 | wiki_convert(&content, 0, 0); |
| 717 | blob_reset(&content); |
| 718 | } |
| 719 | }else if( azName[i][0]=='#' ){ |
| @@ -772,15 +743,15 @@ | |
| 743 | ** spaces. |
| 744 | */ |
| 745 | static void output_no_tabs(const char *z){ |
| 746 | while( z && z[0] ){ |
| 747 | int i, j; |
| 748 | for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} |
| 749 | if( i>0 ){ |
| 750 | cgi_printf("%.*s", i, z); |
| 751 | } |
| 752 | for(j=i; fossil_isspace(z[j]); j++){} |
| 753 | if( j>i ){ |
| 754 | cgi_printf("%*s", j-i, ""); |
| 755 | } |
| 756 | z += j; |
| 757 | } |
| @@ -816,21 +787,21 @@ | |
| 787 | ** Generate HTML that describes a color key. |
| 788 | */ |
| 789 | void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ |
| 790 | int i, j, k; |
| 791 | char *zSafeKey, *zToFree; |
| 792 | while( fossil_isspace(*zClrKey) ) zClrKey++; |
| 793 | if( zClrKey[0]==0 ) return; |
| 794 | @ <table %s(zTabArgs)> |
| 795 | if( horiz ){ |
| 796 | @ <tr> |
| 797 | } |
| 798 | zToFree = zSafeKey = mprintf("%h", zClrKey); |
| 799 | while( zSafeKey[0] ){ |
| 800 | while( fossil_isspace(*zSafeKey) ) zSafeKey++; |
| 801 | for(i=0; zSafeKey[i] && !fossil_isspace(zSafeKey[i]); i++){} |
| 802 | for(j=i; fossil_isspace(zSafeKey[j]); j++){} |
| 803 | for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){} |
| 804 | if( !horiz ){ |
| 805 | cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n", |
| 806 | i, zSafeKey, k-j, &zSafeKey[j]); |
| 807 | }else{ |
| @@ -942,5 +913,168 @@ | |
| 913 | sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 914 | sqlite3_set_authorizer(g.db, 0, 0); |
| 915 | cgi_set_content_type("text/plain"); |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | ** report number for full table ticket export |
| 921 | */ |
| 922 | static const char zFullTicketRptRn[] = "0"; |
| 923 | |
| 924 | /* |
| 925 | ** report title for full table ticket export |
| 926 | */ |
| 927 | static const char zFullTicketRptTitle[] = "full ticket export"; |
| 928 | |
| 929 | /* |
| 930 | ** show all reports, which can be used for ticket show. |
| 931 | ** Output is written to stdout as tab delimited table |
| 932 | */ |
| 933 | void rpt_list_reports(void){ |
| 934 | Stmt q; |
| 935 | char const aRptOutFrmt[] = "%s\t%s\n"; |
| 936 | |
| 937 | printf("Available reports:\n"); |
| 938 | printf(aRptOutFrmt,"report number","report title"); |
| 939 | printf(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle); |
| 940 | db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn"); |
| 941 | while( db_step(&q)==SQLITE_ROW ){ |
| 942 | const char *zRn = db_column_text(&q, 0); |
| 943 | const char *zTitle = db_column_text(&q, 1); |
| 944 | |
| 945 | printf(aRptOutFrmt,zRn,zTitle); |
| 946 | } |
| 947 | db_finalize(&q); |
| 948 | } |
| 949 | |
| 950 | /* |
| 951 | ** user defined separator used by ticket show command |
| 952 | */ |
| 953 | static const char *zSep = 0; |
| 954 | |
| 955 | /* |
| 956 | ** select the quoting algorithm for "ticket show" |
| 957 | */ |
| 958 | #if INTERFACE |
| 959 | typedef enum eTktShowEnc { tktNoTab=0, tktFossilize=1 } tTktShowEncoding; |
| 960 | #endif |
| 961 | static tTktShowEncoding tktEncode = tktNoTab; |
| 962 | |
| 963 | /* |
| 964 | ** Output the text given in the argument. Convert tabs and newlines into |
| 965 | ** spaces. |
| 966 | */ |
| 967 | static void output_no_tabs_file(const char *z){ |
| 968 | switch( tktEncode ){ |
| 969 | case tktFossilize: |
| 970 | { char *zFosZ; |
| 971 | |
| 972 | if( z && *z ){ |
| 973 | zFosZ = fossilize(z,-1); |
| 974 | printf("%s",zFosZ); |
| 975 | free(zFosZ); |
| 976 | } |
| 977 | break; |
| 978 | } |
| 979 | default: |
| 980 | while( z && z[0] ){ |
| 981 | int i, j; |
| 982 | for(i=0; z[i] && (!fossil_isspace(z[i]) || z[i]==' '); i++){} |
| 983 | if( i>0 ){ |
| 984 | printf("%.*s", i, z); |
| 985 | } |
| 986 | for(j=i; fossil_isspace(z[j]); j++){} |
| 987 | if( j>i ){ |
| 988 | printf("%*s", j-i, ""); |
| 989 | } |
| 990 | z += j; |
| 991 | } |
| 992 | break; |
| 993 | } |
| 994 | } |
| 995 | |
| 996 | /* |
| 997 | ** Output a row as a tab-separated line of text. |
| 998 | */ |
| 999 | int output_separated_file( |
| 1000 | void *pUser, /* Pointer to row-count integer */ |
| 1001 | int nArg, /* Number of columns in this result row */ |
| 1002 | char **azArg, /* Text of data in all columns */ |
| 1003 | char **azName /* Names of the columns */ |
| 1004 | ){ |
| 1005 | int *pCount = (int*)pUser; |
| 1006 | int i; |
| 1007 | |
| 1008 | if( *pCount==0 ){ |
| 1009 | for(i=0; i<nArg; i++){ |
| 1010 | output_no_tabs_file(azName[i]); |
| 1011 | printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); |
| 1012 | } |
| 1013 | } |
| 1014 | ++*pCount; |
| 1015 | for(i=0; i<nArg; i++){ |
| 1016 | output_no_tabs_file(azArg[i]); |
| 1017 | printf("%s", i<nArg-1 ? (zSep?zSep:"\t") : "\n"); |
| 1018 | } |
| 1019 | return 0; |
| 1020 | } |
| 1021 | |
| 1022 | /* |
| 1023 | ** Generate a report. The rn query parameter is the report number. |
| 1024 | ** The output is written to stdout as flat file. The zFilter paramater |
| 1025 | ** is a full WHERE-condition. |
| 1026 | */ |
| 1027 | void rptshow( |
| 1028 | const char *zRep, |
| 1029 | const char *zSepIn, |
| 1030 | const char *zFilter, |
| 1031 | tTktShowEncoding enc |
| 1032 | ){ |
| 1033 | Stmt q; |
| 1034 | char *zSql; |
| 1035 | const char *zTitle; |
| 1036 | const char *zOwner; |
| 1037 | const char *zClrKey; |
| 1038 | char *zErr1 = 0; |
| 1039 | char *zErr2 = 0; |
| 1040 | int count = 0; |
| 1041 | int rn; |
| 1042 | |
| 1043 | if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){ |
| 1044 | zTitle = zFullTicketRptTitle; |
| 1045 | zSql = "SELECT * FROM ticket"; |
| 1046 | zOwner = g.zLogin; |
| 1047 | zClrKey = ""; |
| 1048 | }else{ |
| 1049 | rn = atoi(zRep); |
| 1050 | if( rn ){ |
| 1051 | db_prepare(&q, |
| 1052 | "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn); |
| 1053 | }else{ |
| 1054 | db_prepare(&q, |
| 1055 | "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep); |
| 1056 | } |
| 1057 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1058 | db_finalize(&q); |
| 1059 | rpt_list_reports(); |
| 1060 | fossil_fatal("unkown report format(%s)!",zRep); |
| 1061 | } |
| 1062 | zTitle = db_column_malloc(&q, 0); |
| 1063 | zSql = db_column_malloc(&q, 1); |
| 1064 | zOwner = db_column_malloc(&q, 2); |
| 1065 | zClrKey = db_column_malloc(&q, 3); |
| 1066 | db_finalize(&q); |
| 1067 | } |
| 1068 | if( zFilter ){ |
| 1069 | zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); |
| 1070 | } |
| 1071 | count = 0; |
| 1072 | tktEncode = enc; |
| 1073 | zSep = zSepIn; |
| 1074 | sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); |
| 1075 | sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2); |
| 1076 | sqlite3_set_authorizer(g.db, 0, 0); |
| 1077 | if( zFilter ){ |
| 1078 | free(zSql); |
| 1079 | } |
| 1080 | } |
| 1081 |
+10
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -241,10 +241,20 @@ | ||
| 241 | 241 | @ -- UUID but we do not (yet) know the file content. |
| 242 | 242 | @ -- |
| 243 | 243 | @ CREATE TABLE phantom( |
| 244 | 244 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 245 | 245 | @ ); |
| 246 | +@ | |
| 247 | +@ -- A record of orphaned delta-manifests. An orphan is a delta-manifest | |
| 248 | +@ -- for which we have content, but its baseline-manifest is a phantom. | |
| 249 | +@ -- We have to track all orphan maniftests so that when the baseline arrives, | |
| 250 | +@ -- we know to process the orphaned deltas. | |
| 251 | +@ CREATE TABLE orphan( | |
| 252 | +@ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline | |
| 253 | +@ baseline INTEGER -- Phantom baseline of this orphan | |
| 254 | +@ ); | |
| 255 | +@ CREATE INDEX orphan_baseline ON orphan(baseline); | |
| 246 | 256 | @ |
| 247 | 257 | @ -- Unclustered records. An unclustered record is a record (including |
| 248 | 258 | @ -- a cluster records themselves) that is not mentioned by some other |
| 249 | 259 | @ -- cluster. |
| 250 | 260 | @ -- |
| 251 | 261 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -241,10 +241,20 @@ | |
| 241 | @ -- UUID but we do not (yet) know the file content. |
| 242 | @ -- |
| 243 | @ CREATE TABLE phantom( |
| 244 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 245 | @ ); |
| 246 | @ |
| 247 | @ -- Unclustered records. An unclustered record is a record (including |
| 248 | @ -- a cluster records themselves) that is not mentioned by some other |
| 249 | @ -- cluster. |
| 250 | @ -- |
| 251 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -241,10 +241,20 @@ | |
| 241 | @ -- UUID but we do not (yet) know the file content. |
| 242 | @ -- |
| 243 | @ CREATE TABLE phantom( |
| 244 | @ rid INTEGER PRIMARY KEY -- Record ID of the phantom |
| 245 | @ ); |
| 246 | @ |
| 247 | @ -- A record of orphaned delta-manifests. An orphan is a delta-manifest |
| 248 | @ -- for which we have content, but its baseline-manifest is a phantom. |
| 249 | @ -- We have to track all orphan maniftests so that when the baseline arrives, |
| 250 | @ -- we know to process the orphaned deltas. |
| 251 | @ CREATE TABLE orphan( |
| 252 | @ rid INTEGER PRIMARY KEY, -- Delta manifest with a phantom baseline |
| 253 | @ baseline INTEGER -- Phantom baseline of this orphan |
| 254 | @ ); |
| 255 | @ CREATE INDEX orphan_baseline ON orphan(baseline); |
| 256 | @ |
| 257 | @ -- Unclustered records. An unclustered record is a record (including |
| 258 | @ -- a cluster records themselves) that is not mentioned by some other |
| 259 | @ -- cluster. |
| 260 | @ -- |
| 261 |
+3
-4
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -42,20 +42,19 @@ | ||
| 42 | 42 | int nPattern = strlen(zPattern); |
| 43 | 43 | Search *p; |
| 44 | 44 | char *z; |
| 45 | 45 | int i; |
| 46 | 46 | |
| 47 | - p = malloc( nPattern + sizeof(*p) + 1); | |
| 48 | - if( p==0 ) fossil_panic("out of memory"); | |
| 47 | + p = fossil_malloc( nPattern + sizeof(*p) + 1); | |
| 49 | 48 | z = (char*)&p[1]; |
| 50 | 49 | strcpy(z, zPattern); |
| 51 | 50 | memset(p, 0, sizeof(*p)); |
| 52 | 51 | while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ |
| 53 | - while( !isalnum(*z) && *z ){ z++; } | |
| 52 | + while( !fossil_isalnum(*z) && *z ){ z++; } | |
| 54 | 53 | if( *z==0 ) break; |
| 55 | 54 | p->a[p->nTerm].z = z; |
| 56 | - for(i=1; isalnum(z[i]) || z[i]=='_'; i++){} | |
| 55 | + for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){} | |
| 57 | 56 | p->a[p->nTerm].n = i; |
| 58 | 57 | z += i; |
| 59 | 58 | p->nTerm++; |
| 60 | 59 | } |
| 61 | 60 | return p; |
| 62 | 61 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -42,20 +42,19 @@ | |
| 42 | int nPattern = strlen(zPattern); |
| 43 | Search *p; |
| 44 | char *z; |
| 45 | int i; |
| 46 | |
| 47 | p = malloc( nPattern + sizeof(*p) + 1); |
| 48 | if( p==0 ) fossil_panic("out of memory"); |
| 49 | z = (char*)&p[1]; |
| 50 | strcpy(z, zPattern); |
| 51 | memset(p, 0, sizeof(*p)); |
| 52 | while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ |
| 53 | while( !isalnum(*z) && *z ){ z++; } |
| 54 | if( *z==0 ) break; |
| 55 | p->a[p->nTerm].z = z; |
| 56 | for(i=1; isalnum(z[i]) || z[i]=='_'; i++){} |
| 57 | p->a[p->nTerm].n = i; |
| 58 | z += i; |
| 59 | p->nTerm++; |
| 60 | } |
| 61 | return p; |
| 62 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -42,20 +42,19 @@ | |
| 42 | int nPattern = strlen(zPattern); |
| 43 | Search *p; |
| 44 | char *z; |
| 45 | int i; |
| 46 | |
| 47 | p = fossil_malloc( nPattern + sizeof(*p) + 1); |
| 48 | z = (char*)&p[1]; |
| 49 | strcpy(z, zPattern); |
| 50 | memset(p, 0, sizeof(*p)); |
| 51 | while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ |
| 52 | while( !fossil_isalnum(*z) && *z ){ z++; } |
| 53 | if( *z==0 ) break; |
| 54 | p->a[p->nTerm].z = z; |
| 55 | for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){} |
| 56 | p->a[p->nTerm].n = i; |
| 57 | z += i; |
| 58 | p->nTerm++; |
| 59 | } |
| 60 | return p; |
| 61 |
+2
-2
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -694,11 +694,11 @@ | ||
| 694 | 694 | } |
| 695 | 695 | if( iVal ){ |
| 696 | 696 | @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> |
| 697 | 697 | @ <b>%s(zLabel)</b> |
| 698 | 698 | }else{ |
| 699 | - @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b> | |
| 699 | + @ <input type="checkbox" name="%s(zQParm)" /> <b>%s(zLabel)</b> | |
| 700 | 700 | } |
| 701 | 701 | } |
| 702 | 702 | |
| 703 | 703 | /* |
| 704 | 704 | ** Generate an entry box for an attribute. |
| @@ -877,11 +877,11 @@ | ||
| 877 | 877 | login_insert_csrf_secret(); |
| 878 | 878 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 879 | 879 | if( pSet->width==0 ){ |
| 880 | 880 | onoff_attribute(pSet->name, pSet->name, |
| 881 | 881 | pSet->var!=0 ? pSet->var : pSet->name, |
| 882 | - pSet->def[0]=='1'); | |
| 882 | + is_truth(pSet->def)); | |
| 883 | 883 | @ <br /> |
| 884 | 884 | } |
| 885 | 885 | } |
| 886 | 886 | @ </td><td style="width: 30;"></td><td valign="top"> |
| 887 | 887 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 888 | 888 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -694,11 +694,11 @@ | |
| 694 | } |
| 695 | if( iVal ){ |
| 696 | @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> |
| 697 | @ <b>%s(zLabel)</b> |
| 698 | }else{ |
| 699 | @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b> |
| 700 | } |
| 701 | } |
| 702 | |
| 703 | /* |
| 704 | ** Generate an entry box for an attribute. |
| @@ -877,11 +877,11 @@ | |
| 877 | login_insert_csrf_secret(); |
| 878 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 879 | if( pSet->width==0 ){ |
| 880 | onoff_attribute(pSet->name, pSet->name, |
| 881 | pSet->var!=0 ? pSet->var : pSet->name, |
| 882 | pSet->def[0]=='1'); |
| 883 | @ <br /> |
| 884 | } |
| 885 | } |
| 886 | @ </td><td style="width: 30;"></td><td valign="top"> |
| 887 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 888 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -694,11 +694,11 @@ | |
| 694 | } |
| 695 | if( iVal ){ |
| 696 | @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> |
| 697 | @ <b>%s(zLabel)</b> |
| 698 | }else{ |
| 699 | @ <input type="checkbox" name="%s(zQParm)" /> <b>%s(zLabel)</b> |
| 700 | } |
| 701 | } |
| 702 | |
| 703 | /* |
| 704 | ** Generate an entry box for an attribute. |
| @@ -877,11 +877,11 @@ | |
| 877 | login_insert_csrf_secret(); |
| 878 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 879 | if( pSet->width==0 ){ |
| 880 | onoff_attribute(pSet->name, pSet->name, |
| 881 | pSet->var!=0 ? pSet->var : pSet->name, |
| 882 | is_truth(pSet->def)); |
| 883 | @ <br /> |
| 884 | } |
| 885 | } |
| 886 | @ </td><td style="width: 30;"></td><td valign="top"> |
| 887 | for(pSet=ctrlSettings; pSet->name!=0; pSet++){ |
| 888 |
+198
-425
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -1,417 +1,190 @@ | ||
| 1 | 1 | /* |
| 2 | -** This implementation of SHA1 is adapted from the example implementation | |
| 3 | -** contained in RFC-3174. | |
| 2 | +** This implementation of SHA1. | |
| 4 | 3 | */ |
| 5 | -/* | |
| 6 | - * If you do not have the ISO standard stdint.h header file, then you | |
| 7 | - * must typdef the following: | |
| 8 | - * name meaning | |
| 9 | - * */ | |
| 10 | -#if defined(__DMC__) || defined(_MSC_VER) | |
| 11 | - typedef unsigned long uint32_t; //unsigned 32 bit integer | |
| 12 | - typedef unsigned char uint8_t; //unsigned 8 bit integer (i.e., unsigned char) | |
| 13 | -#else | |
| 14 | -# include <stdint.h> | |
| 15 | -#endif | |
| 16 | 4 | #include <sys/types.h> |
| 17 | 5 | #include "config.h" |
| 18 | 6 | #include "sha1.h" |
| 19 | 7 | |
| 20 | -#define SHA1HashSize 20 | |
| 21 | -#define shaSuccess 0 | |
| 22 | -#define shaInputTooLong 1 | |
| 23 | -#define shaStateError 2 | |
| 24 | - | |
| 25 | -/* | |
| 26 | - * This structure will hold context information for the SHA-1 | |
| 27 | - * hashing operation | |
| 28 | - */ | |
| 29 | -typedef struct SHA1Context SHA1Context; | |
| 30 | -struct SHA1Context { | |
| 31 | - uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ | |
| 32 | - | |
| 33 | - uint32_t Length_Low; /* Message length in bits */ | |
| 34 | - uint32_t Length_High; /* Message length in bits */ | |
| 35 | - | |
| 36 | - int Message_Block_Index; /* Index into message block array */ | |
| 37 | - uint8_t Message_Block[64]; /* 512-bit message blocks */ | |
| 38 | - | |
| 39 | - int Computed; /* Is the digest computed? */ | |
| 40 | - int Corrupted; /* Is the message digest corrupted? */ | |
| 41 | -}; | |
| 42 | - | |
| 43 | -/* | |
| 44 | - * sha1.c | |
| 45 | - * | |
| 46 | - * Description: | |
| 47 | - * This file implements the Secure Hashing Algorithm 1 as | |
| 48 | - * defined in FIPS PUB 180-1 published April 17, 1995. | |
| 49 | - * | |
| 50 | - * The SHA-1, produces a 160-bit message digest for a given | |
| 51 | - * data stream. It should take about 2**n steps to find a | |
| 52 | - * message with the same digest as a given message and | |
| 53 | - * 2**(n/2) to find any two messages with the same digest, | |
| 54 | - * when n is the digest size in bits. Therefore, this | |
| 55 | - * algorithm can serve as a means of providing a | |
| 56 | - * "fingerprint" for a message. | |
| 57 | - * | |
| 58 | - * Portability Issues: | |
| 59 | - * SHA-1 is defined in terms of 32-bit "words". This code | |
| 60 | - * uses <stdint.h> (included via "sha1.h" to define 32 and 8 | |
| 61 | - * bit unsigned integer types. If your C compiler does not | |
| 62 | - * support 32 bit unsigned integers, this code is not | |
| 63 | - * appropriate. | |
| 64 | - * | |
| 65 | - * Caveats: | |
| 66 | - * SHA-1 is designed to work with messages less than 2^64 bits | |
| 67 | - * long. Although SHA-1 allows a message digest to be generated | |
| 68 | - * for messages of any number of bits less than 2^64, this | |
| 69 | - * implementation only works with messages with a length that is | |
| 70 | - * a multiple of the size of an 8-bit character. | |
| 71 | - * | |
| 72 | - */ | |
| 73 | - | |
| 74 | -/* | |
| 75 | - * Define the SHA1 circular left shift macro | |
| 76 | - */ | |
| 77 | -#define SHA1CircularShift(bits,word) \ | |
| 78 | - (((word) << (bits)) | ((word) >> (32-(bits)))) | |
| 79 | - | |
| 80 | -/* Local Function Prototyptes */ | |
| 81 | -static void SHA1PadMessage(SHA1Context *); | |
| 82 | -static void SHA1ProcessMessageBlock(SHA1Context *); | |
| 83 | - | |
| 84 | -/* | |
| 85 | - * SHA1Reset | |
| 86 | - * | |
| 87 | - * Description: | |
| 88 | - * This function will initialize the SHA1Context in preparation | |
| 89 | - * for computing a new SHA1 message digest. | |
| 90 | - * | |
| 91 | - * Parameters: | |
| 92 | - * context: [in/out] | |
| 93 | - * The context to reset. | |
| 94 | - * | |
| 95 | - * Returns: | |
| 96 | - * sha Error Code. | |
| 97 | - * | |
| 98 | - */ | |
| 99 | -static int SHA1Reset(SHA1Context *context) | |
| 100 | -{ | |
| 101 | - context->Length_Low = 0; | |
| 102 | - context->Length_High = 0; | |
| 103 | - context->Message_Block_Index = 0; | |
| 104 | - | |
| 105 | - context->Intermediate_Hash[0] = 0x67452301; | |
| 106 | - context->Intermediate_Hash[1] = 0xEFCDAB89; | |
| 107 | - context->Intermediate_Hash[2] = 0x98BADCFE; | |
| 108 | - context->Intermediate_Hash[3] = 0x10325476; | |
| 109 | - context->Intermediate_Hash[4] = 0xC3D2E1F0; | |
| 110 | - | |
| 111 | - context->Computed = 0; | |
| 112 | - context->Corrupted = 0; | |
| 113 | - | |
| 114 | - return shaSuccess; | |
| 115 | -} | |
| 116 | - | |
| 117 | -/* | |
| 118 | - * SHA1Result | |
| 119 | - * | |
| 120 | - * Description: | |
| 121 | - * This function will return the 160-bit message digest into the | |
| 122 | - * Message_Digest array provided by the caller. | |
| 123 | - * NOTE: The first octet of hash is stored in the 0th element, | |
| 124 | - * the last octet of hash in the 19th element. | |
| 125 | - * | |
| 126 | - * Parameters: | |
| 127 | - * context: [in/out] | |
| 128 | - * The context to use to calculate the SHA-1 hash. | |
| 129 | - * Message_Digest: [out] | |
| 130 | - * Where the digest is returned. | |
| 131 | - * | |
| 132 | - * Returns: | |
| 133 | - * sha Error Code. | |
| 134 | - * | |
| 135 | - */ | |
| 136 | -static int SHA1Result( SHA1Context *context, | |
| 137 | - uint8_t Message_Digest[SHA1HashSize]) | |
| 138 | -{ | |
| 139 | - int i; | |
| 140 | - | |
| 141 | - if (context->Corrupted) | |
| 142 | - { | |
| 143 | - return context->Corrupted; | |
| 144 | - } | |
| 145 | - | |
| 146 | - if (!context->Computed) | |
| 147 | - { | |
| 148 | - SHA1PadMessage(context); | |
| 149 | - for(i=0; i<64; ++i) | |
| 150 | - { | |
| 151 | - /* message may be sensitive, clear it out */ | |
| 152 | - context->Message_Block[i] = 0; | |
| 153 | - } | |
| 154 | - context->Length_Low = 0; /* and clear length */ | |
| 155 | - context->Length_High = 0; | |
| 156 | - context->Computed = 1; | |
| 157 | - | |
| 158 | - } | |
| 159 | - | |
| 160 | - for(i = 0; i < SHA1HashSize; ++i) | |
| 161 | - { | |
| 162 | - Message_Digest[i] = context->Intermediate_Hash[i>>2] | |
| 163 | - >> 8 * ( 3 - ( i & 0x03 ) ); | |
| 164 | - } | |
| 165 | - | |
| 166 | - return shaSuccess; | |
| 167 | -} | |
| 168 | - | |
| 169 | -/* | |
| 170 | - * SHA1Input | |
| 171 | - * | |
| 172 | - * Description: | |
| 173 | - * This function accepts an array of octets as the next portion | |
| 174 | - * of the message. | |
| 175 | - * | |
| 176 | - * Parameters: | |
| 177 | - * context: [in/out] | |
| 178 | - * The SHA context to update | |
| 179 | - * message_array: [in] | |
| 180 | - * An array of characters representing the next portion of | |
| 181 | - * the message. | |
| 182 | - * length: [in] | |
| 183 | - * The length of the message in message_array | |
| 184 | - * | |
| 185 | - * Returns: | |
| 186 | - * sha Error Code. | |
| 187 | - * | |
| 188 | - */ | |
| 189 | -static | |
| 190 | -int SHA1Input( SHA1Context *context, | |
| 191 | - const uint8_t *message_array, | |
| 192 | - unsigned length) | |
| 193 | -{ | |
| 194 | - if (!length) | |
| 195 | - { | |
| 196 | - return shaSuccess; | |
| 197 | - } | |
| 198 | - | |
| 199 | - if (context->Computed) | |
| 200 | - { | |
| 201 | - context->Corrupted = shaStateError; | |
| 202 | - | |
| 203 | - return shaStateError; | |
| 204 | - } | |
| 205 | - | |
| 206 | - if (context->Corrupted) | |
| 207 | - { | |
| 208 | - return context->Corrupted; | |
| 209 | - } | |
| 210 | - while(length-- && !context->Corrupted) | |
| 211 | - { | |
| 212 | - context->Message_Block[context->Message_Block_Index++] = | |
| 213 | - (*message_array & 0xFF); | |
| 214 | - | |
| 215 | - context->Length_Low += 8; | |
| 216 | - if (context->Length_Low == 0) | |
| 217 | - { | |
| 218 | - context->Length_High++; | |
| 219 | - if (context->Length_High == 0) | |
| 220 | - { | |
| 221 | - /* Message is too long */ | |
| 222 | - context->Corrupted = 1; | |
| 223 | - } | |
| 224 | - } | |
| 225 | - | |
| 226 | - if (context->Message_Block_Index == 64) | |
| 227 | - { | |
| 228 | - SHA1ProcessMessageBlock(context); | |
| 229 | - } | |
| 230 | - | |
| 231 | - message_array++; | |
| 232 | - } | |
| 233 | - | |
| 234 | - return shaSuccess; | |
| 235 | -} | |
| 236 | - | |
| 237 | -/* | |
| 238 | - * SHA1ProcessMessageBlock | |
| 239 | - * | |
| 240 | - * Description: | |
| 241 | - * This function will process the next 512 bits of the message | |
| 242 | - * stored in the Message_Block array. | |
| 243 | - * | |
| 244 | - * Parameters: | |
| 245 | - * None. | |
| 246 | - * | |
| 247 | - * Returns: | |
| 248 | - * Nothing. | |
| 249 | - * | |
| 250 | - * Comments: | |
| 251 | - * Many of the variable names in this code, especially the | |
| 252 | - * single character names, were used because those were the | |
| 253 | - * names used in the publication. | |
| 254 | - * | |
| 255 | - * | |
| 256 | - */ | |
| 257 | -static void SHA1ProcessMessageBlock(SHA1Context *context) | |
| 258 | -{ | |
| 259 | - const uint32_t K[] = { /* Constants defined in SHA-1 */ | |
| 260 | - 0x5A827999, | |
| 261 | - 0x6ED9EBA1, | |
| 262 | - 0x8F1BBCDC, | |
| 263 | - 0xCA62C1D6 | |
| 264 | - }; | |
| 265 | - int t; /* Loop counter */ | |
| 266 | - uint32_t temp; /* Temporary word value */ | |
| 267 | - uint32_t W[80]; /* Word sequence */ | |
| 268 | - uint32_t A, B, C, D, E; /* Word buffers */ | |
| 269 | - | |
| 270 | - /* | |
| 271 | - * Initialize the first 16 words in the array W | |
| 272 | - */ | |
| 273 | - for(t = 0; t < 16; t++) | |
| 274 | - { | |
| 275 | - W[t] = context->Message_Block[t * 4] << 24; | |
| 276 | - W[t] |= context->Message_Block[t * 4 + 1] << 16; | |
| 277 | - W[t] |= context->Message_Block[t * 4 + 2] << 8; | |
| 278 | - W[t] |= context->Message_Block[t * 4 + 3]; | |
| 279 | - } | |
| 280 | - | |
| 281 | - for(t = 16; t < 80; t++) | |
| 282 | - { | |
| 283 | - W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); | |
| 284 | - } | |
| 285 | - | |
| 286 | - A = context->Intermediate_Hash[0]; | |
| 287 | - B = context->Intermediate_Hash[1]; | |
| 288 | - C = context->Intermediate_Hash[2]; | |
| 289 | - D = context->Intermediate_Hash[3]; | |
| 290 | - E = context->Intermediate_Hash[4]; | |
| 291 | - | |
| 292 | - for(t = 0; t < 20; t++) | |
| 293 | - { | |
| 294 | - temp = SHA1CircularShift(5,A) + | |
| 295 | - ((B & C) | ((~B) & D)) + E + W[t] + K[0]; | |
| 296 | - E = D; | |
| 297 | - D = C; | |
| 298 | - C = SHA1CircularShift(30,B); | |
| 299 | - | |
| 300 | - B = A; | |
| 301 | - A = temp; | |
| 302 | - } | |
| 303 | - | |
| 304 | - for(t = 20; t < 40; t++) | |
| 305 | - { | |
| 306 | - temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; | |
| 307 | - E = D; | |
| 308 | - D = C; | |
| 309 | - C = SHA1CircularShift(30,B); | |
| 310 | - B = A; | |
| 311 | - A = temp; | |
| 312 | - } | |
| 313 | - | |
| 314 | - for(t = 40; t < 60; t++) | |
| 315 | - { | |
| 316 | - temp = SHA1CircularShift(5,A) + | |
| 317 | - ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; | |
| 318 | - E = D; | |
| 319 | - D = C; | |
| 320 | - C = SHA1CircularShift(30,B); | |
| 321 | - B = A; | |
| 322 | - A = temp; | |
| 323 | - } | |
| 324 | - | |
| 325 | - for(t = 60; t < 80; t++) | |
| 326 | - { | |
| 327 | - temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; | |
| 328 | - E = D; | |
| 329 | - D = C; | |
| 330 | - C = SHA1CircularShift(30,B); | |
| 331 | - B = A; | |
| 332 | - A = temp; | |
| 333 | - } | |
| 334 | - | |
| 335 | - context->Intermediate_Hash[0] += A; | |
| 336 | - context->Intermediate_Hash[1] += B; | |
| 337 | - context->Intermediate_Hash[2] += C; | |
| 338 | - context->Intermediate_Hash[3] += D; | |
| 339 | - context->Intermediate_Hash[4] += E; | |
| 340 | - | |
| 341 | - context->Message_Block_Index = 0; | |
| 342 | -} | |
| 343 | - | |
| 344 | -/* | |
| 345 | - * SHA1PadMessage | |
| 346 | - * | |
| 347 | - | |
| 348 | - * Description: | |
| 349 | - * According to the standard, the message must be padded to an even | |
| 350 | - * 512 bits. The first padding bit must be a '1'. The last 64 | |
| 351 | - * bits represent the length of the original message. All bits in | |
| 352 | - * between should be 0. This function will pad the message | |
| 353 | - * according to those rules by filling the Message_Block array | |
| 354 | - * accordingly. It will also call the ProcessMessageBlock function | |
| 355 | - * provided appropriately. When it returns, it can be assumed that | |
| 356 | - * the message digest has been computed. | |
| 357 | - * | |
| 358 | - * Parameters: | |
| 359 | - * context: [in/out] | |
| 360 | - * The context to pad | |
| 361 | - * ProcessMessageBlock: [in] | |
| 362 | - * The appropriate SHA*ProcessMessageBlock function | |
| 363 | - * Returns: | |
| 364 | - * Nothing. | |
| 365 | - * | |
| 366 | - */ | |
| 367 | -static void SHA1PadMessage(SHA1Context *context) | |
| 368 | -{ | |
| 369 | - /* | |
| 370 | - * Check to see if the current message block is too small to hold | |
| 371 | - * the initial padding bits and length. If so, we will pad the | |
| 372 | - * block, process it, and then continue padding into a second | |
| 373 | - * block. | |
| 374 | - */ | |
| 375 | - if (context->Message_Block_Index > 55) | |
| 376 | - { | |
| 377 | - context->Message_Block[context->Message_Block_Index++] = 0x80; | |
| 378 | - while(context->Message_Block_Index < 64) | |
| 379 | - { | |
| 380 | - context->Message_Block[context->Message_Block_Index++] = 0; | |
| 381 | - } | |
| 382 | - | |
| 383 | - SHA1ProcessMessageBlock(context); | |
| 384 | - | |
| 385 | - while(context->Message_Block_Index < 56) | |
| 386 | - { | |
| 387 | - context->Message_Block[context->Message_Block_Index++] = 0; | |
| 388 | - } | |
| 389 | - } | |
| 390 | - else | |
| 391 | - { | |
| 392 | - context->Message_Block[context->Message_Block_Index++] = 0x80; | |
| 393 | - while(context->Message_Block_Index < 56) | |
| 394 | - { | |
| 395 | - | |
| 396 | - context->Message_Block[context->Message_Block_Index++] = 0; | |
| 397 | - } | |
| 398 | - } | |
| 399 | - | |
| 400 | - /* | |
| 401 | - * Store the message length as the last 8 octets | |
| 402 | - */ | |
| 403 | - context->Message_Block[56] = context->Length_High >> 24; | |
| 404 | - context->Message_Block[57] = context->Length_High >> 16; | |
| 405 | - context->Message_Block[58] = context->Length_High >> 8; | |
| 406 | - context->Message_Block[59] = context->Length_High; | |
| 407 | - context->Message_Block[60] = context->Length_Low >> 24; | |
| 408 | - context->Message_Block[61] = context->Length_Low >> 16; | |
| 409 | - context->Message_Block[62] = context->Length_Low >> 8; | |
| 410 | - context->Message_Block[63] = context->Length_Low; | |
| 411 | - | |
| 412 | - SHA1ProcessMessageBlock(context); | |
| 8 | + | |
| 9 | +/* | |
| 10 | +** The SHA1 implementation below is adapted from: | |
| 11 | +** | |
| 12 | +** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ | |
| 13 | +** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ | |
| 14 | +** | |
| 15 | +** SHA-1 in C | |
| 16 | +** By Steve Reid <[email protected]> | |
| 17 | +** 100% Public Domain | |
| 18 | +*/ | |
| 19 | +typedef struct SHA1Context SHA1Context; | |
| 20 | +struct SHA1Context { | |
| 21 | + unsigned int state[5]; | |
| 22 | + unsigned int count[2]; | |
| 23 | + unsigned char buffer[64]; | |
| 24 | +}; | |
| 25 | + | |
| 26 | +/* | |
| 27 | + * blk0() and blk() perform the initial expand. | |
| 28 | + * I got the idea of expanding during the round function from SSLeay | |
| 29 | + * | |
| 30 | + * blk0le() for little-endian and blk0be() for big-endian. | |
| 31 | + */ | |
| 32 | +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) | |
| 33 | +#define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ | |
| 34 | + |(rol(block->l[i],8)&0x00FF00FF)) | |
| 35 | +#define blk0be(i) block->l[i] | |
| 36 | +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ | |
| 37 | + ^block->l[(i+2)&15]^block->l[i&15],1)) | |
| 38 | + | |
| 39 | +/* | |
| 40 | + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 | |
| 41 | + * | |
| 42 | + * Rl0() for little-endian and Rb0() for big-endian. Endianness is | |
| 43 | + * determined at run-time. | |
| 44 | + */ | |
| 45 | +#define Rl0(v,w,x,y,z,i) \ | |
| 46 | + z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30); | |
| 47 | +#define Rb0(v,w,x,y,z,i) \ | |
| 48 | + z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30); | |
| 49 | +#define R1(v,w,x,y,z,i) \ | |
| 50 | + z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); | |
| 51 | +#define R2(v,w,x,y,z,i) \ | |
| 52 | + z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); | |
| 53 | +#define R3(v,w,x,y,z,i) \ | |
| 54 | + z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); | |
| 55 | +#define R4(v,w,x,y,z,i) \ | |
| 56 | + z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); | |
| 57 | + | |
| 58 | +typedef union { | |
| 59 | + unsigned char c[64]; | |
| 60 | + unsigned int l[16]; | |
| 61 | +} CHAR64LONG16; | |
| 62 | + | |
| 63 | +/* | |
| 64 | + * Hash a single 512-bit block. This is the core of the algorithm. | |
| 65 | + */ | |
| 66 | +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) | |
| 67 | +{ | |
| 68 | + unsigned int a, b, c, d, e; | |
| 69 | + CHAR64LONG16 *block; | |
| 70 | + static int one = 1; | |
| 71 | + CHAR64LONG16 workspace; | |
| 72 | + | |
| 73 | + block = &workspace; | |
| 74 | + (void)memcpy(block, buffer, 64); | |
| 75 | + | |
| 76 | + /* Copy context->state[] to working vars */ | |
| 77 | + a = state[0]; | |
| 78 | + b = state[1]; | |
| 79 | + c = state[2]; | |
| 80 | + d = state[3]; | |
| 81 | + e = state[4]; | |
| 82 | + | |
| 83 | + /* 4 rounds of 20 operations each. Loop unrolled. */ | |
| 84 | + if( 1 == *(unsigned char*)&one ){ | |
| 85 | + Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); | |
| 86 | + Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); | |
| 87 | + Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); | |
| 88 | + Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); | |
| 89 | + }else{ | |
| 90 | + Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); | |
| 91 | + Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); | |
| 92 | + Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); | |
| 93 | + Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); | |
| 94 | + } | |
| 95 | + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); | |
| 96 | + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); | |
| 97 | + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); | |
| 98 | + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); | |
| 99 | + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); | |
| 100 | + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); | |
| 101 | + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); | |
| 102 | + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); | |
| 103 | + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); | |
| 104 | + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); | |
| 105 | + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); | |
| 106 | + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); | |
| 107 | + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); | |
| 108 | + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); | |
| 109 | + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); | |
| 110 | + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); | |
| 111 | + | |
| 112 | + /* Add the working vars back into context.state[] */ | |
| 113 | + state[0] += a; | |
| 114 | + state[1] += b; | |
| 115 | + state[2] += c; | |
| 116 | + state[3] += d; | |
| 117 | + state[4] += e; | |
| 118 | + | |
| 119 | + /* Wipe variables */ | |
| 120 | + a = b = c = d = e = 0; | |
| 121 | +} | |
| 122 | + | |
| 123 | + | |
| 124 | +/* | |
| 125 | + * SHA1Init - Initialize new context | |
| 126 | + */ | |
| 127 | +static void SHA1Init(SHA1Context *context){ | |
| 128 | + /* SHA1 initialization constants */ | |
| 129 | + context->state[0] = 0x67452301; | |
| 130 | + context->state[1] = 0xEFCDAB89; | |
| 131 | + context->state[2] = 0x98BADCFE; | |
| 132 | + context->state[3] = 0x10325476; | |
| 133 | + context->state[4] = 0xC3D2E1F0; | |
| 134 | + context->count[0] = context->count[1] = 0; | |
| 135 | +} | |
| 136 | + | |
| 137 | + | |
| 138 | +/* | |
| 139 | + * Run your data through this. | |
| 140 | + */ | |
| 141 | +static void SHA1Update( | |
| 142 | + SHA1Context *context, | |
| 143 | + const unsigned char *data, | |
| 144 | + unsigned int len | |
| 145 | +){ | |
| 146 | + unsigned int i, j; | |
| 147 | + | |
| 148 | + j = context->count[0]; | |
| 149 | + if ((context->count[0] += len << 3) < j) | |
| 150 | + context->count[1] += (len>>29)+1; | |
| 151 | + j = (j >> 3) & 63; | |
| 152 | + if ((j + len) > 63) { | |
| 153 | + (void)memcpy(&context->buffer[j], data, (i = 64-j)); | |
| 154 | + SHA1Transform(context->state, context->buffer); | |
| 155 | + for ( ; i + 63 < len; i += 64) | |
| 156 | + SHA1Transform(context->state, &data[i]); | |
| 157 | + j = 0; | |
| 158 | + } else { | |
| 159 | + i = 0; | |
| 160 | + } | |
| 161 | + (void)memcpy(&context->buffer[j], &data[i], len - i); | |
| 162 | +} | |
| 163 | + | |
| 164 | + | |
| 165 | +/* | |
| 166 | + * Add padding and return the message digest. | |
| 167 | + */ | |
| 168 | +static void SHA1Final(SHA1Context *context, unsigned char digest[20]){ | |
| 169 | + unsigned int i; | |
| 170 | + unsigned char finalcount[8]; | |
| 171 | + | |
| 172 | + for (i = 0; i < 8; i++) { | |
| 173 | + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] | |
| 174 | + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ | |
| 175 | + } | |
| 176 | + SHA1Update(context, (const unsigned char *)"\200", 1); | |
| 177 | + while ((context->count[0] & 504) != 448) | |
| 178 | + SHA1Update(context, (const unsigned char *)"\0", 1); | |
| 179 | + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ | |
| 180 | + | |
| 181 | + if (digest) { | |
| 182 | + for (i = 0; i < 20; i++) | |
| 183 | + digest[i] = (unsigned char) | |
| 184 | + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); | |
| 185 | + } | |
| 413 | 186 | } |
| 414 | 187 | |
| 415 | 188 | |
| 416 | 189 | /* |
| 417 | 190 | ** Convert a digest into base-16. digest should be declared as |
| @@ -441,18 +214,18 @@ | ||
| 441 | 214 | /* |
| 442 | 215 | ** Add more text to the incremental SHA1 checksum. |
| 443 | 216 | */ |
| 444 | 217 | void sha1sum_step_text(const char *zText, int nBytes){ |
| 445 | 218 | if( !incrInit ){ |
| 446 | - SHA1Reset(&incrCtx); | |
| 219 | + SHA1Init(&incrCtx); | |
| 447 | 220 | incrInit = 1; |
| 448 | 221 | } |
| 449 | 222 | if( nBytes<=0 ){ |
| 450 | 223 | if( nBytes==0 ) return; |
| 451 | 224 | nBytes = strlen(zText); |
| 452 | 225 | } |
| 453 | - SHA1Input(&incrCtx, (unsigned char*)zText, nBytes); | |
| 226 | + SHA1Update(&incrCtx, (unsigned char*)zText, nBytes); | |
| 454 | 227 | } |
| 455 | 228 | |
| 456 | 229 | /* |
| 457 | 230 | ** Add the content of a blob to the incremental SHA1 checksum. |
| 458 | 231 | */ |
| @@ -470,11 +243,11 @@ | ||
| 470 | 243 | */ |
| 471 | 244 | char *sha1sum_finish(Blob *pOut){ |
| 472 | 245 | unsigned char zResult[20]; |
| 473 | 246 | static char zOut[41]; |
| 474 | 247 | sha1sum_step_text(0,0); |
| 475 | - SHA1Result(&incrCtx, zResult); | |
| 248 | + SHA1Final(&incrCtx, zResult); | |
| 476 | 249 | incrInit = 0; |
| 477 | 250 | DigestToBase16(zResult, zOut); |
| 478 | 251 | if( pOut ){ |
| 479 | 252 | blob_zero(pOut); |
| 480 | 253 | blob_append(pOut, zOut, 40); |
| @@ -497,21 +270,21 @@ | ||
| 497 | 270 | |
| 498 | 271 | in = fopen(zFilename,"rb"); |
| 499 | 272 | if( in==0 ){ |
| 500 | 273 | return 1; |
| 501 | 274 | } |
| 502 | - SHA1Reset(&ctx); | |
| 275 | + SHA1Init(&ctx); | |
| 503 | 276 | for(;;){ |
| 504 | 277 | int n; |
| 505 | 278 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 506 | 279 | if( n<=0 ) break; |
| 507 | - SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n); | |
| 280 | + SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); | |
| 508 | 281 | } |
| 509 | 282 | fclose(in); |
| 510 | 283 | blob_zero(pCksum); |
| 511 | 284 | blob_resize(pCksum, 40); |
| 512 | - SHA1Result(&ctx, zResult); | |
| 285 | + SHA1Final(&ctx, zResult); | |
| 513 | 286 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 514 | 287 | return 0; |
| 515 | 288 | } |
| 516 | 289 | |
| 517 | 290 | /* |
| @@ -523,19 +296,19 @@ | ||
| 523 | 296 | */ |
| 524 | 297 | int sha1sum_blob(const Blob *pIn, Blob *pCksum){ |
| 525 | 298 | SHA1Context ctx; |
| 526 | 299 | unsigned char zResult[20]; |
| 527 | 300 | |
| 528 | - SHA1Reset(&ctx); | |
| 529 | - SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); | |
| 301 | + SHA1Init(&ctx); | |
| 302 | + SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); | |
| 530 | 303 | if( pIn==pCksum ){ |
| 531 | 304 | blob_reset(pCksum); |
| 532 | 305 | }else{ |
| 533 | 306 | blob_zero(pCksum); |
| 534 | 307 | } |
| 535 | 308 | blob_resize(pCksum, 40); |
| 536 | - SHA1Result(&ctx, zResult); | |
| 309 | + SHA1Final(&ctx, zResult); | |
| 537 | 310 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 538 | 311 | return 0; |
| 539 | 312 | } |
| 540 | 313 | |
| 541 | 314 | /* |
| @@ -545,13 +318,13 @@ | ||
| 545 | 318 | char *sha1sum(const char *zIn){ |
| 546 | 319 | SHA1Context ctx; |
| 547 | 320 | unsigned char zResult[20]; |
| 548 | 321 | char zDigest[41]; |
| 549 | 322 | |
| 550 | - SHA1Reset(&ctx); | |
| 551 | - SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn)); | |
| 552 | - SHA1Result(&ctx, zResult); | |
| 323 | + SHA1Init(&ctx); | |
| 324 | + SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); | |
| 325 | + SHA1Final(&ctx, zResult); | |
| 553 | 326 | DigestToBase16(zResult, zDigest); |
| 554 | 327 | return mprintf("%s", zDigest); |
| 555 | 328 | } |
| 556 | 329 | |
| 557 | 330 | /* |
| @@ -575,11 +348,11 @@ | ||
| 575 | 348 | static char *zProjectId = 0; |
| 576 | 349 | SHA1Context ctx; |
| 577 | 350 | unsigned char zResult[20]; |
| 578 | 351 | char zDigest[41]; |
| 579 | 352 | |
| 580 | - SHA1Reset(&ctx); | |
| 353 | + SHA1Init(&ctx); | |
| 581 | 354 | if( zProjectId==0 ){ |
| 582 | 355 | zProjectId = db_get("project-code", 0); |
| 583 | 356 | |
| 584 | 357 | /* On the first xfer request of a clone, the project-code is not yet |
| 585 | 358 | ** known. Use the cleartext password, since that is all we have. |
| @@ -586,16 +359,16 @@ | ||
| 586 | 359 | */ |
| 587 | 360 | if( zProjectId==0 ){ |
| 588 | 361 | return mprintf("%s", zPw); |
| 589 | 362 | } |
| 590 | 363 | } |
| 591 | - SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId)); | |
| 592 | - SHA1Input(&ctx, (unsigned char*)"/", 1); | |
| 593 | - SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin)); | |
| 594 | - SHA1Input(&ctx, (unsigned char*)"/", 1); | |
| 595 | - SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw)); | |
| 596 | - SHA1Result(&ctx, zResult); | |
| 364 | + SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId)); | |
| 365 | + SHA1Update(&ctx, (unsigned char*)"/", 1); | |
| 366 | + SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); | |
| 367 | + SHA1Update(&ctx, (unsigned char*)"/", 1); | |
| 368 | + SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); | |
| 369 | + SHA1Final(&ctx, zResult); | |
| 597 | 370 | DigestToBase16(zResult, zDigest); |
| 598 | 371 | return mprintf("%s", zDigest); |
| 599 | 372 | } |
| 600 | 373 | |
| 601 | 374 | /* |
| 602 | 375 | |
| 603 | 376 | ADDED src/shell.c |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -1,417 +1,190 @@ | |
| 1 | /* |
| 2 | ** This implementation of SHA1 is adapted from the example implementation |
| 3 | ** contained in RFC-3174. |
| 4 | */ |
| 5 | /* |
| 6 | * If you do not have the ISO standard stdint.h header file, then you |
| 7 | * must typdef the following: |
| 8 | * name meaning |
| 9 | * */ |
| 10 | #if defined(__DMC__) || defined(_MSC_VER) |
| 11 | typedef unsigned long uint32_t; //unsigned 32 bit integer |
| 12 | typedef unsigned char uint8_t; //unsigned 8 bit integer (i.e., unsigned char) |
| 13 | #else |
| 14 | # include <stdint.h> |
| 15 | #endif |
| 16 | #include <sys/types.h> |
| 17 | #include "config.h" |
| 18 | #include "sha1.h" |
| 19 | |
| 20 | #define SHA1HashSize 20 |
| 21 | #define shaSuccess 0 |
| 22 | #define shaInputTooLong 1 |
| 23 | #define shaStateError 2 |
| 24 | |
| 25 | /* |
| 26 | * This structure will hold context information for the SHA-1 |
| 27 | * hashing operation |
| 28 | */ |
| 29 | typedef struct SHA1Context SHA1Context; |
| 30 | struct SHA1Context { |
| 31 | uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ |
| 32 | |
| 33 | uint32_t Length_Low; /* Message length in bits */ |
| 34 | uint32_t Length_High; /* Message length in bits */ |
| 35 | |
| 36 | int Message_Block_Index; /* Index into message block array */ |
| 37 | uint8_t Message_Block[64]; /* 512-bit message blocks */ |
| 38 | |
| 39 | int Computed; /* Is the digest computed? */ |
| 40 | int Corrupted; /* Is the message digest corrupted? */ |
| 41 | }; |
| 42 | |
| 43 | /* |
| 44 | * sha1.c |
| 45 | * |
| 46 | * Description: |
| 47 | * This file implements the Secure Hashing Algorithm 1 as |
| 48 | * defined in FIPS PUB 180-1 published April 17, 1995. |
| 49 | * |
| 50 | * The SHA-1, produces a 160-bit message digest for a given |
| 51 | * data stream. It should take about 2**n steps to find a |
| 52 | * message with the same digest as a given message and |
| 53 | * 2**(n/2) to find any two messages with the same digest, |
| 54 | * when n is the digest size in bits. Therefore, this |
| 55 | * algorithm can serve as a means of providing a |
| 56 | * "fingerprint" for a message. |
| 57 | * |
| 58 | * Portability Issues: |
| 59 | * SHA-1 is defined in terms of 32-bit "words". This code |
| 60 | * uses <stdint.h> (included via "sha1.h" to define 32 and 8 |
| 61 | * bit unsigned integer types. If your C compiler does not |
| 62 | * support 32 bit unsigned integers, this code is not |
| 63 | * appropriate. |
| 64 | * |
| 65 | * Caveats: |
| 66 | * SHA-1 is designed to work with messages less than 2^64 bits |
| 67 | * long. Although SHA-1 allows a message digest to be generated |
| 68 | * for messages of any number of bits less than 2^64, this |
| 69 | * implementation only works with messages with a length that is |
| 70 | * a multiple of the size of an 8-bit character. |
| 71 | * |
| 72 | */ |
| 73 | |
| 74 | /* |
| 75 | * Define the SHA1 circular left shift macro |
| 76 | */ |
| 77 | #define SHA1CircularShift(bits,word) \ |
| 78 | (((word) << (bits)) | ((word) >> (32-(bits)))) |
| 79 | |
| 80 | /* Local Function Prototyptes */ |
| 81 | static void SHA1PadMessage(SHA1Context *); |
| 82 | static void SHA1ProcessMessageBlock(SHA1Context *); |
| 83 | |
| 84 | /* |
| 85 | * SHA1Reset |
| 86 | * |
| 87 | * Description: |
| 88 | * This function will initialize the SHA1Context in preparation |
| 89 | * for computing a new SHA1 message digest. |
| 90 | * |
| 91 | * Parameters: |
| 92 | * context: [in/out] |
| 93 | * The context to reset. |
| 94 | * |
| 95 | * Returns: |
| 96 | * sha Error Code. |
| 97 | * |
| 98 | */ |
| 99 | static int SHA1Reset(SHA1Context *context) |
| 100 | { |
| 101 | context->Length_Low = 0; |
| 102 | context->Length_High = 0; |
| 103 | context->Message_Block_Index = 0; |
| 104 | |
| 105 | context->Intermediate_Hash[0] = 0x67452301; |
| 106 | context->Intermediate_Hash[1] = 0xEFCDAB89; |
| 107 | context->Intermediate_Hash[2] = 0x98BADCFE; |
| 108 | context->Intermediate_Hash[3] = 0x10325476; |
| 109 | context->Intermediate_Hash[4] = 0xC3D2E1F0; |
| 110 | |
| 111 | context->Computed = 0; |
| 112 | context->Corrupted = 0; |
| 113 | |
| 114 | return shaSuccess; |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * SHA1Result |
| 119 | * |
| 120 | * Description: |
| 121 | * This function will return the 160-bit message digest into the |
| 122 | * Message_Digest array provided by the caller. |
| 123 | * NOTE: The first octet of hash is stored in the 0th element, |
| 124 | * the last octet of hash in the 19th element. |
| 125 | * |
| 126 | * Parameters: |
| 127 | * context: [in/out] |
| 128 | * The context to use to calculate the SHA-1 hash. |
| 129 | * Message_Digest: [out] |
| 130 | * Where the digest is returned. |
| 131 | * |
| 132 | * Returns: |
| 133 | * sha Error Code. |
| 134 | * |
| 135 | */ |
| 136 | static int SHA1Result( SHA1Context *context, |
| 137 | uint8_t Message_Digest[SHA1HashSize]) |
| 138 | { |
| 139 | int i; |
| 140 | |
| 141 | if (context->Corrupted) |
| 142 | { |
| 143 | return context->Corrupted; |
| 144 | } |
| 145 | |
| 146 | if (!context->Computed) |
| 147 | { |
| 148 | SHA1PadMessage(context); |
| 149 | for(i=0; i<64; ++i) |
| 150 | { |
| 151 | /* message may be sensitive, clear it out */ |
| 152 | context->Message_Block[i] = 0; |
| 153 | } |
| 154 | context->Length_Low = 0; /* and clear length */ |
| 155 | context->Length_High = 0; |
| 156 | context->Computed = 1; |
| 157 | |
| 158 | } |
| 159 | |
| 160 | for(i = 0; i < SHA1HashSize; ++i) |
| 161 | { |
| 162 | Message_Digest[i] = context->Intermediate_Hash[i>>2] |
| 163 | >> 8 * ( 3 - ( i & 0x03 ) ); |
| 164 | } |
| 165 | |
| 166 | return shaSuccess; |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | * SHA1Input |
| 171 | * |
| 172 | * Description: |
| 173 | * This function accepts an array of octets as the next portion |
| 174 | * of the message. |
| 175 | * |
| 176 | * Parameters: |
| 177 | * context: [in/out] |
| 178 | * The SHA context to update |
| 179 | * message_array: [in] |
| 180 | * An array of characters representing the next portion of |
| 181 | * the message. |
| 182 | * length: [in] |
| 183 | * The length of the message in message_array |
| 184 | * |
| 185 | * Returns: |
| 186 | * sha Error Code. |
| 187 | * |
| 188 | */ |
| 189 | static |
| 190 | int SHA1Input( SHA1Context *context, |
| 191 | const uint8_t *message_array, |
| 192 | unsigned length) |
| 193 | { |
| 194 | if (!length) |
| 195 | { |
| 196 | return shaSuccess; |
| 197 | } |
| 198 | |
| 199 | if (context->Computed) |
| 200 | { |
| 201 | context->Corrupted = shaStateError; |
| 202 | |
| 203 | return shaStateError; |
| 204 | } |
| 205 | |
| 206 | if (context->Corrupted) |
| 207 | { |
| 208 | return context->Corrupted; |
| 209 | } |
| 210 | while(length-- && !context->Corrupted) |
| 211 | { |
| 212 | context->Message_Block[context->Message_Block_Index++] = |
| 213 | (*message_array & 0xFF); |
| 214 | |
| 215 | context->Length_Low += 8; |
| 216 | if (context->Length_Low == 0) |
| 217 | { |
| 218 | context->Length_High++; |
| 219 | if (context->Length_High == 0) |
| 220 | { |
| 221 | /* Message is too long */ |
| 222 | context->Corrupted = 1; |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | if (context->Message_Block_Index == 64) |
| 227 | { |
| 228 | SHA1ProcessMessageBlock(context); |
| 229 | } |
| 230 | |
| 231 | message_array++; |
| 232 | } |
| 233 | |
| 234 | return shaSuccess; |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | * SHA1ProcessMessageBlock |
| 239 | * |
| 240 | * Description: |
| 241 | * This function will process the next 512 bits of the message |
| 242 | * stored in the Message_Block array. |
| 243 | * |
| 244 | * Parameters: |
| 245 | * None. |
| 246 | * |
| 247 | * Returns: |
| 248 | * Nothing. |
| 249 | * |
| 250 | * Comments: |
| 251 | * Many of the variable names in this code, especially the |
| 252 | * single character names, were used because those were the |
| 253 | * names used in the publication. |
| 254 | * |
| 255 | * |
| 256 | */ |
| 257 | static void SHA1ProcessMessageBlock(SHA1Context *context) |
| 258 | { |
| 259 | const uint32_t K[] = { /* Constants defined in SHA-1 */ |
| 260 | 0x5A827999, |
| 261 | 0x6ED9EBA1, |
| 262 | 0x8F1BBCDC, |
| 263 | 0xCA62C1D6 |
| 264 | }; |
| 265 | int t; /* Loop counter */ |
| 266 | uint32_t temp; /* Temporary word value */ |
| 267 | uint32_t W[80]; /* Word sequence */ |
| 268 | uint32_t A, B, C, D, E; /* Word buffers */ |
| 269 | |
| 270 | /* |
| 271 | * Initialize the first 16 words in the array W |
| 272 | */ |
| 273 | for(t = 0; t < 16; t++) |
| 274 | { |
| 275 | W[t] = context->Message_Block[t * 4] << 24; |
| 276 | W[t] |= context->Message_Block[t * 4 + 1] << 16; |
| 277 | W[t] |= context->Message_Block[t * 4 + 2] << 8; |
| 278 | W[t] |= context->Message_Block[t * 4 + 3]; |
| 279 | } |
| 280 | |
| 281 | for(t = 16; t < 80; t++) |
| 282 | { |
| 283 | W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); |
| 284 | } |
| 285 | |
| 286 | A = context->Intermediate_Hash[0]; |
| 287 | B = context->Intermediate_Hash[1]; |
| 288 | C = context->Intermediate_Hash[2]; |
| 289 | D = context->Intermediate_Hash[3]; |
| 290 | E = context->Intermediate_Hash[4]; |
| 291 | |
| 292 | for(t = 0; t < 20; t++) |
| 293 | { |
| 294 | temp = SHA1CircularShift(5,A) + |
| 295 | ((B & C) | ((~B) & D)) + E + W[t] + K[0]; |
| 296 | E = D; |
| 297 | D = C; |
| 298 | C = SHA1CircularShift(30,B); |
| 299 | |
| 300 | B = A; |
| 301 | A = temp; |
| 302 | } |
| 303 | |
| 304 | for(t = 20; t < 40; t++) |
| 305 | { |
| 306 | temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; |
| 307 | E = D; |
| 308 | D = C; |
| 309 | C = SHA1CircularShift(30,B); |
| 310 | B = A; |
| 311 | A = temp; |
| 312 | } |
| 313 | |
| 314 | for(t = 40; t < 60; t++) |
| 315 | { |
| 316 | temp = SHA1CircularShift(5,A) + |
| 317 | ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; |
| 318 | E = D; |
| 319 | D = C; |
| 320 | C = SHA1CircularShift(30,B); |
| 321 | B = A; |
| 322 | A = temp; |
| 323 | } |
| 324 | |
| 325 | for(t = 60; t < 80; t++) |
| 326 | { |
| 327 | temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; |
| 328 | E = D; |
| 329 | D = C; |
| 330 | C = SHA1CircularShift(30,B); |
| 331 | B = A; |
| 332 | A = temp; |
| 333 | } |
| 334 | |
| 335 | context->Intermediate_Hash[0] += A; |
| 336 | context->Intermediate_Hash[1] += B; |
| 337 | context->Intermediate_Hash[2] += C; |
| 338 | context->Intermediate_Hash[3] += D; |
| 339 | context->Intermediate_Hash[4] += E; |
| 340 | |
| 341 | context->Message_Block_Index = 0; |
| 342 | } |
| 343 | |
| 344 | /* |
| 345 | * SHA1PadMessage |
| 346 | * |
| 347 | |
| 348 | * Description: |
| 349 | * According to the standard, the message must be padded to an even |
| 350 | * 512 bits. The first padding bit must be a '1'. The last 64 |
| 351 | * bits represent the length of the original message. All bits in |
| 352 | * between should be 0. This function will pad the message |
| 353 | * according to those rules by filling the Message_Block array |
| 354 | * accordingly. It will also call the ProcessMessageBlock function |
| 355 | * provided appropriately. When it returns, it can be assumed that |
| 356 | * the message digest has been computed. |
| 357 | * |
| 358 | * Parameters: |
| 359 | * context: [in/out] |
| 360 | * The context to pad |
| 361 | * ProcessMessageBlock: [in] |
| 362 | * The appropriate SHA*ProcessMessageBlock function |
| 363 | * Returns: |
| 364 | * Nothing. |
| 365 | * |
| 366 | */ |
| 367 | static void SHA1PadMessage(SHA1Context *context) |
| 368 | { |
| 369 | /* |
| 370 | * Check to see if the current message block is too small to hold |
| 371 | * the initial padding bits and length. If so, we will pad the |
| 372 | * block, process it, and then continue padding into a second |
| 373 | * block. |
| 374 | */ |
| 375 | if (context->Message_Block_Index > 55) |
| 376 | { |
| 377 | context->Message_Block[context->Message_Block_Index++] = 0x80; |
| 378 | while(context->Message_Block_Index < 64) |
| 379 | { |
| 380 | context->Message_Block[context->Message_Block_Index++] = 0; |
| 381 | } |
| 382 | |
| 383 | SHA1ProcessMessageBlock(context); |
| 384 | |
| 385 | while(context->Message_Block_Index < 56) |
| 386 | { |
| 387 | context->Message_Block[context->Message_Block_Index++] = 0; |
| 388 | } |
| 389 | } |
| 390 | else |
| 391 | { |
| 392 | context->Message_Block[context->Message_Block_Index++] = 0x80; |
| 393 | while(context->Message_Block_Index < 56) |
| 394 | { |
| 395 | |
| 396 | context->Message_Block[context->Message_Block_Index++] = 0; |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | /* |
| 401 | * Store the message length as the last 8 octets |
| 402 | */ |
| 403 | context->Message_Block[56] = context->Length_High >> 24; |
| 404 | context->Message_Block[57] = context->Length_High >> 16; |
| 405 | context->Message_Block[58] = context->Length_High >> 8; |
| 406 | context->Message_Block[59] = context->Length_High; |
| 407 | context->Message_Block[60] = context->Length_Low >> 24; |
| 408 | context->Message_Block[61] = context->Length_Low >> 16; |
| 409 | context->Message_Block[62] = context->Length_Low >> 8; |
| 410 | context->Message_Block[63] = context->Length_Low; |
| 411 | |
| 412 | SHA1ProcessMessageBlock(context); |
| 413 | } |
| 414 | |
| 415 | |
| 416 | /* |
| 417 | ** Convert a digest into base-16. digest should be declared as |
| @@ -441,18 +214,18 @@ | |
| 441 | /* |
| 442 | ** Add more text to the incremental SHA1 checksum. |
| 443 | */ |
| 444 | void sha1sum_step_text(const char *zText, int nBytes){ |
| 445 | if( !incrInit ){ |
| 446 | SHA1Reset(&incrCtx); |
| 447 | incrInit = 1; |
| 448 | } |
| 449 | if( nBytes<=0 ){ |
| 450 | if( nBytes==0 ) return; |
| 451 | nBytes = strlen(zText); |
| 452 | } |
| 453 | SHA1Input(&incrCtx, (unsigned char*)zText, nBytes); |
| 454 | } |
| 455 | |
| 456 | /* |
| 457 | ** Add the content of a blob to the incremental SHA1 checksum. |
| 458 | */ |
| @@ -470,11 +243,11 @@ | |
| 470 | */ |
| 471 | char *sha1sum_finish(Blob *pOut){ |
| 472 | unsigned char zResult[20]; |
| 473 | static char zOut[41]; |
| 474 | sha1sum_step_text(0,0); |
| 475 | SHA1Result(&incrCtx, zResult); |
| 476 | incrInit = 0; |
| 477 | DigestToBase16(zResult, zOut); |
| 478 | if( pOut ){ |
| 479 | blob_zero(pOut); |
| 480 | blob_append(pOut, zOut, 40); |
| @@ -497,21 +270,21 @@ | |
| 497 | |
| 498 | in = fopen(zFilename,"rb"); |
| 499 | if( in==0 ){ |
| 500 | return 1; |
| 501 | } |
| 502 | SHA1Reset(&ctx); |
| 503 | for(;;){ |
| 504 | int n; |
| 505 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 506 | if( n<=0 ) break; |
| 507 | SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n); |
| 508 | } |
| 509 | fclose(in); |
| 510 | blob_zero(pCksum); |
| 511 | blob_resize(pCksum, 40); |
| 512 | SHA1Result(&ctx, zResult); |
| 513 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 514 | return 0; |
| 515 | } |
| 516 | |
| 517 | /* |
| @@ -523,19 +296,19 @@ | |
| 523 | */ |
| 524 | int sha1sum_blob(const Blob *pIn, Blob *pCksum){ |
| 525 | SHA1Context ctx; |
| 526 | unsigned char zResult[20]; |
| 527 | |
| 528 | SHA1Reset(&ctx); |
| 529 | SHA1Input(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); |
| 530 | if( pIn==pCksum ){ |
| 531 | blob_reset(pCksum); |
| 532 | }else{ |
| 533 | blob_zero(pCksum); |
| 534 | } |
| 535 | blob_resize(pCksum, 40); |
| 536 | SHA1Result(&ctx, zResult); |
| 537 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 538 | return 0; |
| 539 | } |
| 540 | |
| 541 | /* |
| @@ -545,13 +318,13 @@ | |
| 545 | char *sha1sum(const char *zIn){ |
| 546 | SHA1Context ctx; |
| 547 | unsigned char zResult[20]; |
| 548 | char zDigest[41]; |
| 549 | |
| 550 | SHA1Reset(&ctx); |
| 551 | SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 552 | SHA1Result(&ctx, zResult); |
| 553 | DigestToBase16(zResult, zDigest); |
| 554 | return mprintf("%s", zDigest); |
| 555 | } |
| 556 | |
| 557 | /* |
| @@ -575,11 +348,11 @@ | |
| 575 | static char *zProjectId = 0; |
| 576 | SHA1Context ctx; |
| 577 | unsigned char zResult[20]; |
| 578 | char zDigest[41]; |
| 579 | |
| 580 | SHA1Reset(&ctx); |
| 581 | if( zProjectId==0 ){ |
| 582 | zProjectId = db_get("project-code", 0); |
| 583 | |
| 584 | /* On the first xfer request of a clone, the project-code is not yet |
| 585 | ** known. Use the cleartext password, since that is all we have. |
| @@ -586,16 +359,16 @@ | |
| 586 | */ |
| 587 | if( zProjectId==0 ){ |
| 588 | return mprintf("%s", zPw); |
| 589 | } |
| 590 | } |
| 591 | SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId)); |
| 592 | SHA1Input(&ctx, (unsigned char*)"/", 1); |
| 593 | SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin)); |
| 594 | SHA1Input(&ctx, (unsigned char*)"/", 1); |
| 595 | SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw)); |
| 596 | SHA1Result(&ctx, zResult); |
| 597 | DigestToBase16(zResult, zDigest); |
| 598 | return mprintf("%s", zDigest); |
| 599 | } |
| 600 | |
| 601 | /* |
| 602 | |
| 603 | DDED src/shell.c |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -1,417 +1,190 @@ | |
| 1 | /* |
| 2 | ** This implementation of SHA1. |
| 3 | */ |
| 4 | #include <sys/types.h> |
| 5 | #include "config.h" |
| 6 | #include "sha1.h" |
| 7 | |
| 8 | |
| 9 | /* |
| 10 | ** The SHA1 implementation below is adapted from: |
| 11 | ** |
| 12 | ** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ |
| 13 | ** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ |
| 14 | ** |
| 15 | ** SHA-1 in C |
| 16 | ** By Steve Reid <[email protected]> |
| 17 | ** 100% Public Domain |
| 18 | */ |
| 19 | typedef struct SHA1Context SHA1Context; |
| 20 | struct SHA1Context { |
| 21 | unsigned int state[5]; |
| 22 | unsigned int count[2]; |
| 23 | unsigned char buffer[64]; |
| 24 | }; |
| 25 | |
| 26 | /* |
| 27 | * blk0() and blk() perform the initial expand. |
| 28 | * I got the idea of expanding during the round function from SSLeay |
| 29 | * |
| 30 | * blk0le() for little-endian and blk0be() for big-endian. |
| 31 | */ |
| 32 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) |
| 33 | #define blk0le(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |
| 34 | |(rol(block->l[i],8)&0x00FF00FF)) |
| 35 | #define blk0be(i) block->l[i] |
| 36 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ |
| 37 | ^block->l[(i+2)&15]^block->l[i&15],1)) |
| 38 | |
| 39 | /* |
| 40 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 |
| 41 | * |
| 42 | * Rl0() for little-endian and Rb0() for big-endian. Endianness is |
| 43 | * determined at run-time. |
| 44 | */ |
| 45 | #define Rl0(v,w,x,y,z,i) \ |
| 46 | z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=rol(w,30); |
| 47 | #define Rb0(v,w,x,y,z,i) \ |
| 48 | z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=rol(w,30); |
| 49 | #define R1(v,w,x,y,z,i) \ |
| 50 | z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); |
| 51 | #define R2(v,w,x,y,z,i) \ |
| 52 | z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); |
| 53 | #define R3(v,w,x,y,z,i) \ |
| 54 | z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); |
| 55 | #define R4(v,w,x,y,z,i) \ |
| 56 | z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); |
| 57 | |
| 58 | typedef union { |
| 59 | unsigned char c[64]; |
| 60 | unsigned int l[16]; |
| 61 | } CHAR64LONG16; |
| 62 | |
| 63 | /* |
| 64 | * Hash a single 512-bit block. This is the core of the algorithm. |
| 65 | */ |
| 66 | void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) |
| 67 | { |
| 68 | unsigned int a, b, c, d, e; |
| 69 | CHAR64LONG16 *block; |
| 70 | static int one = 1; |
| 71 | CHAR64LONG16 workspace; |
| 72 | |
| 73 | block = &workspace; |
| 74 | (void)memcpy(block, buffer, 64); |
| 75 | |
| 76 | /* Copy context->state[] to working vars */ |
| 77 | a = state[0]; |
| 78 | b = state[1]; |
| 79 | c = state[2]; |
| 80 | d = state[3]; |
| 81 | e = state[4]; |
| 82 | |
| 83 | /* 4 rounds of 20 operations each. Loop unrolled. */ |
| 84 | if( 1 == *(unsigned char*)&one ){ |
| 85 | Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); |
| 86 | Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); |
| 87 | Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); |
| 88 | Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); |
| 89 | }else{ |
| 90 | Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); |
| 91 | Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); |
| 92 | Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); |
| 93 | Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); |
| 94 | } |
| 95 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); |
| 96 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); |
| 97 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); |
| 98 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); |
| 99 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); |
| 100 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); |
| 101 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); |
| 102 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); |
| 103 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); |
| 104 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); |
| 105 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); |
| 106 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); |
| 107 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); |
| 108 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); |
| 109 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); |
| 110 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); |
| 111 | |
| 112 | /* Add the working vars back into context.state[] */ |
| 113 | state[0] += a; |
| 114 | state[1] += b; |
| 115 | state[2] += c; |
| 116 | state[3] += d; |
| 117 | state[4] += e; |
| 118 | |
| 119 | /* Wipe variables */ |
| 120 | a = b = c = d = e = 0; |
| 121 | } |
| 122 | |
| 123 | |
| 124 | /* |
| 125 | * SHA1Init - Initialize new context |
| 126 | */ |
| 127 | static void SHA1Init(SHA1Context *context){ |
| 128 | /* SHA1 initialization constants */ |
| 129 | context->state[0] = 0x67452301; |
| 130 | context->state[1] = 0xEFCDAB89; |
| 131 | context->state[2] = 0x98BADCFE; |
| 132 | context->state[3] = 0x10325476; |
| 133 | context->state[4] = 0xC3D2E1F0; |
| 134 | context->count[0] = context->count[1] = 0; |
| 135 | } |
| 136 | |
| 137 | |
| 138 | /* |
| 139 | * Run your data through this. |
| 140 | */ |
| 141 | static void SHA1Update( |
| 142 | SHA1Context *context, |
| 143 | const unsigned char *data, |
| 144 | unsigned int len |
| 145 | ){ |
| 146 | unsigned int i, j; |
| 147 | |
| 148 | j = context->count[0]; |
| 149 | if ((context->count[0] += len << 3) < j) |
| 150 | context->count[1] += (len>>29)+1; |
| 151 | j = (j >> 3) & 63; |
| 152 | if ((j + len) > 63) { |
| 153 | (void)memcpy(&context->buffer[j], data, (i = 64-j)); |
| 154 | SHA1Transform(context->state, context->buffer); |
| 155 | for ( ; i + 63 < len; i += 64) |
| 156 | SHA1Transform(context->state, &data[i]); |
| 157 | j = 0; |
| 158 | } else { |
| 159 | i = 0; |
| 160 | } |
| 161 | (void)memcpy(&context->buffer[j], &data[i], len - i); |
| 162 | } |
| 163 | |
| 164 | |
| 165 | /* |
| 166 | * Add padding and return the message digest. |
| 167 | */ |
| 168 | static void SHA1Final(SHA1Context *context, unsigned char digest[20]){ |
| 169 | unsigned int i; |
| 170 | unsigned char finalcount[8]; |
| 171 | |
| 172 | for (i = 0; i < 8; i++) { |
| 173 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] |
| 174 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ |
| 175 | } |
| 176 | SHA1Update(context, (const unsigned char *)"\200", 1); |
| 177 | while ((context->count[0] & 504) != 448) |
| 178 | SHA1Update(context, (const unsigned char *)"\0", 1); |
| 179 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ |
| 180 | |
| 181 | if (digest) { |
| 182 | for (i = 0; i < 20; i++) |
| 183 | digest[i] = (unsigned char) |
| 184 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | |
| 189 | /* |
| 190 | ** Convert a digest into base-16. digest should be declared as |
| @@ -441,18 +214,18 @@ | |
| 214 | /* |
| 215 | ** Add more text to the incremental SHA1 checksum. |
| 216 | */ |
| 217 | void sha1sum_step_text(const char *zText, int nBytes){ |
| 218 | if( !incrInit ){ |
| 219 | SHA1Init(&incrCtx); |
| 220 | incrInit = 1; |
| 221 | } |
| 222 | if( nBytes<=0 ){ |
| 223 | if( nBytes==0 ) return; |
| 224 | nBytes = strlen(zText); |
| 225 | } |
| 226 | SHA1Update(&incrCtx, (unsigned char*)zText, nBytes); |
| 227 | } |
| 228 | |
| 229 | /* |
| 230 | ** Add the content of a blob to the incremental SHA1 checksum. |
| 231 | */ |
| @@ -470,11 +243,11 @@ | |
| 243 | */ |
| 244 | char *sha1sum_finish(Blob *pOut){ |
| 245 | unsigned char zResult[20]; |
| 246 | static char zOut[41]; |
| 247 | sha1sum_step_text(0,0); |
| 248 | SHA1Final(&incrCtx, zResult); |
| 249 | incrInit = 0; |
| 250 | DigestToBase16(zResult, zOut); |
| 251 | if( pOut ){ |
| 252 | blob_zero(pOut); |
| 253 | blob_append(pOut, zOut, 40); |
| @@ -497,21 +270,21 @@ | |
| 270 | |
| 271 | in = fopen(zFilename,"rb"); |
| 272 | if( in==0 ){ |
| 273 | return 1; |
| 274 | } |
| 275 | SHA1Init(&ctx); |
| 276 | for(;;){ |
| 277 | int n; |
| 278 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 279 | if( n<=0 ) break; |
| 280 | SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); |
| 281 | } |
| 282 | fclose(in); |
| 283 | blob_zero(pCksum); |
| 284 | blob_resize(pCksum, 40); |
| 285 | SHA1Final(&ctx, zResult); |
| 286 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 287 | return 0; |
| 288 | } |
| 289 | |
| 290 | /* |
| @@ -523,19 +296,19 @@ | |
| 296 | */ |
| 297 | int sha1sum_blob(const Blob *pIn, Blob *pCksum){ |
| 298 | SHA1Context ctx; |
| 299 | unsigned char zResult[20]; |
| 300 | |
| 301 | SHA1Init(&ctx); |
| 302 | SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); |
| 303 | if( pIn==pCksum ){ |
| 304 | blob_reset(pCksum); |
| 305 | }else{ |
| 306 | blob_zero(pCksum); |
| 307 | } |
| 308 | blob_resize(pCksum, 40); |
| 309 | SHA1Final(&ctx, zResult); |
| 310 | DigestToBase16(zResult, blob_buffer(pCksum)); |
| 311 | return 0; |
| 312 | } |
| 313 | |
| 314 | /* |
| @@ -545,13 +318,13 @@ | |
| 318 | char *sha1sum(const char *zIn){ |
| 319 | SHA1Context ctx; |
| 320 | unsigned char zResult[20]; |
| 321 | char zDigest[41]; |
| 322 | |
| 323 | SHA1Init(&ctx); |
| 324 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 325 | SHA1Final(&ctx, zResult); |
| 326 | DigestToBase16(zResult, zDigest); |
| 327 | return mprintf("%s", zDigest); |
| 328 | } |
| 329 | |
| 330 | /* |
| @@ -575,11 +348,11 @@ | |
| 348 | static char *zProjectId = 0; |
| 349 | SHA1Context ctx; |
| 350 | unsigned char zResult[20]; |
| 351 | char zDigest[41]; |
| 352 | |
| 353 | SHA1Init(&ctx); |
| 354 | if( zProjectId==0 ){ |
| 355 | zProjectId = db_get("project-code", 0); |
| 356 | |
| 357 | /* On the first xfer request of a clone, the project-code is not yet |
| 358 | ** known. Use the cleartext password, since that is all we have. |
| @@ -586,16 +359,16 @@ | |
| 359 | */ |
| 360 | if( zProjectId==0 ){ |
| 361 | return mprintf("%s", zPw); |
| 362 | } |
| 363 | } |
| 364 | SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId)); |
| 365 | SHA1Update(&ctx, (unsigned char*)"/", 1); |
| 366 | SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); |
| 367 | SHA1Update(&ctx, (unsigned char*)"/", 1); |
| 368 | SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); |
| 369 | SHA1Final(&ctx, zResult); |
| 370 | DigestToBase16(zResult, zDigest); |
| 371 | return mprintf("%s", zDigest); |
| 372 | } |
| 373 | |
| 374 | /* |
| 375 | |
| 376 | DDED src/shell.c |
+4
| --- a/src/shell.c | ||
| +++ b/src/shell.c | ||
| @@ -0,0 +1,4 @@ | ||
| 1 | +; | |
| 2 | + datVReader C#ifde;AllocPAGE**lseif(cannot close database \"%s\"rc++rc; | |
| 3 | +} | |
| 4 | +sqlite3**, db, |
| --- a/src/shell.c | |
| +++ b/src/shell.c | |
| @@ -0,0 +1,4 @@ | |
| --- a/src/shell.c | |
| +++ b/src/shell.c | |
| @@ -0,0 +1,4 @@ | |
| 1 | ; |
| 2 | datVReader C#ifde;AllocPAGE**lseif(cannot close database \"%s\"rc++rc; |
| 3 | } |
| 4 | sqlite3**, db, |
+36
-37
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -650,11 +650,11 @@ | ||
| 650 | 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | 652 | */ |
| 653 | 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | -#define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" | |
| 655 | +#define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" | |
| 656 | 656 | |
| 657 | 657 | /* |
| 658 | 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | 660 | ** |
| @@ -26956,16 +26956,26 @@ | ||
| 26956 | 26956 | |
| 26957 | 26957 | /* Parameter isDelete is only used on vxworks. Express this explicitly |
| 26958 | 26958 | ** here to prevent compiler warnings about unused parameters. |
| 26959 | 26959 | */ |
| 26960 | 26960 | UNUSED_PARAMETER(isDelete); |
| 26961 | + | |
| 26962 | + /* Usually the path zFilename should not be a relative pathname. The | |
| 26963 | + ** exception is when opening the proxy "conch" file in builds that | |
| 26964 | + ** include the special Apple locking styles. | |
| 26965 | + */ | |
| 26966 | +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE | |
| 26967 | + assert( zFilename==0 || zFilename[0]=='/' | |
| 26968 | + || pVfs->pAppData==(void*)&autolockIoFinder ); | |
| 26969 | +#else | |
| 26970 | + assert( zFilename==0 || zFilename[0]=='/' ); | |
| 26971 | +#endif | |
| 26961 | 26972 | |
| 26962 | 26973 | OSTRACE(("OPEN %-3d %s\n", h, zFilename)); |
| 26963 | 26974 | pNew->h = h; |
| 26964 | 26975 | pNew->dirfd = dirfd; |
| 26965 | 26976 | pNew->fileFlags = 0; |
| 26966 | - assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */ | |
| 26967 | 26977 | pNew->zPath = zFilename; |
| 26968 | 26978 | |
| 26969 | 26979 | #if OS_VXWORKS |
| 26970 | 26980 | pNew->pId = vxworksFindFileId(zFilename); |
| 26971 | 26981 | if( pNew->pId==0 ){ |
| @@ -39764,34 +39774,34 @@ | ||
| 39764 | 39774 | /* Higher-level routines never call this function if database is not |
| 39765 | 39775 | ** writable. But check anyway, just for robustness. */ |
| 39766 | 39776 | if( NEVER(pPager->readOnly) ) return SQLITE_PERM; |
| 39767 | 39777 | |
| 39768 | 39778 | CHECK_PAGE(pPg); |
| 39779 | + | |
| 39780 | + /* The journal file needs to be opened. Higher level routines have already | |
| 39781 | + ** obtained the necessary locks to begin the write-transaction, but the | |
| 39782 | + ** rollback journal might not yet be open. Open it now if this is the case. | |
| 39783 | + ** | |
| 39784 | + ** This is done before calling sqlite3PcacheMakeDirty() on the page. | |
| 39785 | + ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then | |
| 39786 | + ** an error might occur and the pager would end up in WRITER_LOCKED state | |
| 39787 | + ** with pages marked as dirty in the cache. | |
| 39788 | + */ | |
| 39789 | + if( pPager->eState==PAGER_WRITER_LOCKED ){ | |
| 39790 | + rc = pager_open_journal(pPager); | |
| 39791 | + if( rc!=SQLITE_OK ) return rc; | |
| 39792 | + } | |
| 39793 | + assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); | |
| 39794 | + assert( assert_pager_state(pPager) ); | |
| 39769 | 39795 | |
| 39770 | 39796 | /* Mark the page as dirty. If the page has already been written |
| 39771 | 39797 | ** to the journal then we can return right away. |
| 39772 | 39798 | */ |
| 39773 | 39799 | sqlite3PcacheMakeDirty(pPg); |
| 39774 | 39800 | if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ |
| 39775 | 39801 | assert( !pagerUseWal(pPager) ); |
| 39776 | - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); | |
| 39777 | 39802 | }else{ |
| 39778 | - | |
| 39779 | - /* If we get this far, it means that the page needs to be | |
| 39780 | - ** written to the transaction journal or the checkpoint journal | |
| 39781 | - ** or both. | |
| 39782 | - ** | |
| 39783 | - ** Higher level routines have already obtained the necessary locks | |
| 39784 | - ** to begin the write-transaction, but the rollback journal might not | |
| 39785 | - ** yet be open. Open it now if this is the case. | |
| 39786 | - */ | |
| 39787 | - if( pPager->eState==PAGER_WRITER_LOCKED ){ | |
| 39788 | - rc = pager_open_journal(pPager); | |
| 39789 | - if( rc!=SQLITE_OK ) return rc; | |
| 39790 | - } | |
| 39791 | - assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); | |
| 39792 | - assert( assert_pager_state(pPager) ); | |
| 39793 | 39803 | |
| 39794 | 39804 | /* The transaction journal now exists and we have a RESERVED or an |
| 39795 | 39805 | ** EXCLUSIVE lock on the main database file. Write the current page to |
| 39796 | 39806 | ** the transaction journal if it is not there already. |
| 39797 | 39807 | */ |
| @@ -43589,11 +43599,11 @@ | ||
| 43589 | 43599 | ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left |
| 43590 | 43600 | ** unchanged. |
| 43591 | 43601 | ** |
| 43592 | 43602 | ** SQLITE_OK is returned if no error is encountered (regardless of whether |
| 43593 | 43603 | ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned |
| 43594 | -** if some error | |
| 43604 | +** if an error occurs. | |
| 43595 | 43605 | */ |
| 43596 | 43606 | static int walRestartLog(Wal *pWal){ |
| 43597 | 43607 | int rc = SQLITE_OK; |
| 43598 | 43608 | int cnt; |
| 43599 | 43609 | |
| @@ -43622,10 +43632,12 @@ | ||
| 43622 | 43632 | walIndexWriteHdr(pWal); |
| 43623 | 43633 | pInfo->nBackfill = 0; |
| 43624 | 43634 | for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 43625 | 43635 | assert( pInfo->aReadMark[0]==0 ); |
| 43626 | 43636 | walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 43637 | + }else if( rc!=SQLITE_BUSY ){ | |
| 43638 | + return rc; | |
| 43627 | 43639 | } |
| 43628 | 43640 | } |
| 43629 | 43641 | walUnlockShared(pWal, WAL_READ_LOCK(0)); |
| 43630 | 43642 | pWal->readLock = -1; |
| 43631 | 43643 | cnt = 0; |
| @@ -54130,18 +54142,11 @@ | ||
| 54130 | 54142 | }else if( pMem->flags & MEM_Int ){ |
| 54131 | 54143 | return (double)pMem->u.i; |
| 54132 | 54144 | }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ |
| 54133 | 54145 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54134 | 54146 | double val = (double)0; |
| 54135 | - pMem->flags |= MEM_Str; | |
| 54136 | - if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) | |
| 54137 | - || sqlite3VdbeMemNulTerminate(pMem) ){ | |
| 54138 | - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ | |
| 54139 | - return (double)0; | |
| 54140 | - } | |
| 54141 | - assert( pMem->z ); | |
| 54142 | - sqlite3AtoF(pMem->z, &val, pMem->n, SQLITE_UTF8); | |
| 54147 | + sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); | |
| 54143 | 54148 | return val; |
| 54144 | 54149 | }else{ |
| 54145 | 54150 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54146 | 54151 | return (double)0; |
| 54147 | 54152 | } |
| @@ -87647,11 +87652,10 @@ | ||
| 87647 | 87652 | addr2 = sqlite3VdbeAddOp0(v, OP_Goto); |
| 87648 | 87653 | sqlite3VdbeJumpHere(v, addr1); |
| 87649 | 87654 | sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); |
| 87650 | 87655 | sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); |
| 87651 | 87656 | sqlite3VdbeJumpHere(v, addr2); |
| 87652 | - pSelect->iLimit = 0; | |
| 87653 | 87657 | } |
| 87654 | 87658 | } |
| 87655 | 87659 | |
| 87656 | 87660 | /* |
| 87657 | 87661 | ** Add code to implement the OFFSET |
| @@ -87926,15 +87930,15 @@ | ||
| 87926 | 87930 | break; |
| 87927 | 87931 | } |
| 87928 | 87932 | #endif |
| 87929 | 87933 | } |
| 87930 | 87934 | |
| 87931 | - /* Jump to the end of the loop if the LIMIT is reached. | |
| 87935 | + /* Jump to the end of the loop if the LIMIT is reached. Except, if | |
| 87936 | + ** there is a sorter, in which case the sorter has already limited | |
| 87937 | + ** the output for us. | |
| 87932 | 87938 | */ |
| 87933 | - if( p->iLimit ){ | |
| 87934 | - assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to | |
| 87935 | - ** pushOntoSorter() would have cleared p->iLimit */ | |
| 87939 | + if( pOrderBy==0 && p->iLimit ){ | |
| 87936 | 87940 | sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); |
| 87937 | 87941 | } |
| 87938 | 87942 | } |
| 87939 | 87943 | |
| 87940 | 87944 | /* |
| @@ -88065,14 +88069,10 @@ | ||
| 88065 | 88069 | } |
| 88066 | 88070 | } |
| 88067 | 88071 | sqlite3ReleaseTempReg(pParse, regRow); |
| 88068 | 88072 | sqlite3ReleaseTempReg(pParse, regRowid); |
| 88069 | 88073 | |
| 88070 | - /* LIMIT has been implemented by the pushOntoSorter() routine. | |
| 88071 | - */ | |
| 88072 | - assert( p->iLimit==0 ); | |
| 88073 | - | |
| 88074 | 88074 | /* The bottom of the loop |
| 88075 | 88075 | */ |
| 88076 | 88076 | sqlite3VdbeResolveLabel(v, addrContinue); |
| 88077 | 88077 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); |
| 88078 | 88078 | sqlite3VdbeResolveLabel(v, addrBreak); |
| @@ -89386,11 +89386,10 @@ | ||
| 89386 | 89386 | } |
| 89387 | 89387 | |
| 89388 | 89388 | /* Separate the left and the right query from one another |
| 89389 | 89389 | */ |
| 89390 | 89390 | p->pPrior = 0; |
| 89391 | - pPrior->pRightmost = 0; | |
| 89392 | 89391 | sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); |
| 89393 | 89392 | if( pPrior->pPrior==0 ){ |
| 89394 | 89393 | sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); |
| 89395 | 89394 | } |
| 89396 | 89395 | |
| 89397 | 89396 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -650,11 +650,11 @@ | |
| 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | */ |
| 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" |
| 656 | |
| 657 | /* |
| 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | ** |
| @@ -26956,16 +26956,26 @@ | |
| 26956 | |
| 26957 | /* Parameter isDelete is only used on vxworks. Express this explicitly |
| 26958 | ** here to prevent compiler warnings about unused parameters. |
| 26959 | */ |
| 26960 | UNUSED_PARAMETER(isDelete); |
| 26961 | |
| 26962 | OSTRACE(("OPEN %-3d %s\n", h, zFilename)); |
| 26963 | pNew->h = h; |
| 26964 | pNew->dirfd = dirfd; |
| 26965 | pNew->fileFlags = 0; |
| 26966 | assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */ |
| 26967 | pNew->zPath = zFilename; |
| 26968 | |
| 26969 | #if OS_VXWORKS |
| 26970 | pNew->pId = vxworksFindFileId(zFilename); |
| 26971 | if( pNew->pId==0 ){ |
| @@ -39764,34 +39774,34 @@ | |
| 39764 | /* Higher-level routines never call this function if database is not |
| 39765 | ** writable. But check anyway, just for robustness. */ |
| 39766 | if( NEVER(pPager->readOnly) ) return SQLITE_PERM; |
| 39767 | |
| 39768 | CHECK_PAGE(pPg); |
| 39769 | |
| 39770 | /* Mark the page as dirty. If the page has already been written |
| 39771 | ** to the journal then we can return right away. |
| 39772 | */ |
| 39773 | sqlite3PcacheMakeDirty(pPg); |
| 39774 | if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ |
| 39775 | assert( !pagerUseWal(pPager) ); |
| 39776 | assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); |
| 39777 | }else{ |
| 39778 | |
| 39779 | /* If we get this far, it means that the page needs to be |
| 39780 | ** written to the transaction journal or the checkpoint journal |
| 39781 | ** or both. |
| 39782 | ** |
| 39783 | ** Higher level routines have already obtained the necessary locks |
| 39784 | ** to begin the write-transaction, but the rollback journal might not |
| 39785 | ** yet be open. Open it now if this is the case. |
| 39786 | */ |
| 39787 | if( pPager->eState==PAGER_WRITER_LOCKED ){ |
| 39788 | rc = pager_open_journal(pPager); |
| 39789 | if( rc!=SQLITE_OK ) return rc; |
| 39790 | } |
| 39791 | assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); |
| 39792 | assert( assert_pager_state(pPager) ); |
| 39793 | |
| 39794 | /* The transaction journal now exists and we have a RESERVED or an |
| 39795 | ** EXCLUSIVE lock on the main database file. Write the current page to |
| 39796 | ** the transaction journal if it is not there already. |
| 39797 | */ |
| @@ -43589,11 +43599,11 @@ | |
| 43589 | ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left |
| 43590 | ** unchanged. |
| 43591 | ** |
| 43592 | ** SQLITE_OK is returned if no error is encountered (regardless of whether |
| 43593 | ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned |
| 43594 | ** if some error |
| 43595 | */ |
| 43596 | static int walRestartLog(Wal *pWal){ |
| 43597 | int rc = SQLITE_OK; |
| 43598 | int cnt; |
| 43599 | |
| @@ -43622,10 +43632,12 @@ | |
| 43622 | walIndexWriteHdr(pWal); |
| 43623 | pInfo->nBackfill = 0; |
| 43624 | for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 43625 | assert( pInfo->aReadMark[0]==0 ); |
| 43626 | walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 43627 | } |
| 43628 | } |
| 43629 | walUnlockShared(pWal, WAL_READ_LOCK(0)); |
| 43630 | pWal->readLock = -1; |
| 43631 | cnt = 0; |
| @@ -54130,18 +54142,11 @@ | |
| 54130 | }else if( pMem->flags & MEM_Int ){ |
| 54131 | return (double)pMem->u.i; |
| 54132 | }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ |
| 54133 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54134 | double val = (double)0; |
| 54135 | pMem->flags |= MEM_Str; |
| 54136 | if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8) |
| 54137 | || sqlite3VdbeMemNulTerminate(pMem) ){ |
| 54138 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54139 | return (double)0; |
| 54140 | } |
| 54141 | assert( pMem->z ); |
| 54142 | sqlite3AtoF(pMem->z, &val, pMem->n, SQLITE_UTF8); |
| 54143 | return val; |
| 54144 | }else{ |
| 54145 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54146 | return (double)0; |
| 54147 | } |
| @@ -87647,11 +87652,10 @@ | |
| 87647 | addr2 = sqlite3VdbeAddOp0(v, OP_Goto); |
| 87648 | sqlite3VdbeJumpHere(v, addr1); |
| 87649 | sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); |
| 87650 | sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); |
| 87651 | sqlite3VdbeJumpHere(v, addr2); |
| 87652 | pSelect->iLimit = 0; |
| 87653 | } |
| 87654 | } |
| 87655 | |
| 87656 | /* |
| 87657 | ** Add code to implement the OFFSET |
| @@ -87926,15 +87930,15 @@ | |
| 87926 | break; |
| 87927 | } |
| 87928 | #endif |
| 87929 | } |
| 87930 | |
| 87931 | /* Jump to the end of the loop if the LIMIT is reached. |
| 87932 | */ |
| 87933 | if( p->iLimit ){ |
| 87934 | assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to |
| 87935 | ** pushOntoSorter() would have cleared p->iLimit */ |
| 87936 | sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); |
| 87937 | } |
| 87938 | } |
| 87939 | |
| 87940 | /* |
| @@ -88065,14 +88069,10 @@ | |
| 88065 | } |
| 88066 | } |
| 88067 | sqlite3ReleaseTempReg(pParse, regRow); |
| 88068 | sqlite3ReleaseTempReg(pParse, regRowid); |
| 88069 | |
| 88070 | /* LIMIT has been implemented by the pushOntoSorter() routine. |
| 88071 | */ |
| 88072 | assert( p->iLimit==0 ); |
| 88073 | |
| 88074 | /* The bottom of the loop |
| 88075 | */ |
| 88076 | sqlite3VdbeResolveLabel(v, addrContinue); |
| 88077 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); |
| 88078 | sqlite3VdbeResolveLabel(v, addrBreak); |
| @@ -89386,11 +89386,10 @@ | |
| 89386 | } |
| 89387 | |
| 89388 | /* Separate the left and the right query from one another |
| 89389 | */ |
| 89390 | p->pPrior = 0; |
| 89391 | pPrior->pRightmost = 0; |
| 89392 | sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); |
| 89393 | if( pPrior->pPrior==0 ){ |
| 89394 | sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); |
| 89395 | } |
| 89396 | |
| 89397 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -650,11 +650,11 @@ | |
| 650 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 651 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 652 | */ |
| 653 | #define SQLITE_VERSION "3.7.3" |
| 654 | #define SQLITE_VERSION_NUMBER 3007003 |
| 655 | #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" |
| 656 | |
| 657 | /* |
| 658 | ** CAPI3REF: Run-Time Library Version Numbers |
| 659 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 660 | ** |
| @@ -26956,16 +26956,26 @@ | |
| 26956 | |
| 26957 | /* Parameter isDelete is only used on vxworks. Express this explicitly |
| 26958 | ** here to prevent compiler warnings about unused parameters. |
| 26959 | */ |
| 26960 | UNUSED_PARAMETER(isDelete); |
| 26961 | |
| 26962 | /* Usually the path zFilename should not be a relative pathname. The |
| 26963 | ** exception is when opening the proxy "conch" file in builds that |
| 26964 | ** include the special Apple locking styles. |
| 26965 | */ |
| 26966 | #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
| 26967 | assert( zFilename==0 || zFilename[0]=='/' |
| 26968 | || pVfs->pAppData==(void*)&autolockIoFinder ); |
| 26969 | #else |
| 26970 | assert( zFilename==0 || zFilename[0]=='/' ); |
| 26971 | #endif |
| 26972 | |
| 26973 | OSTRACE(("OPEN %-3d %s\n", h, zFilename)); |
| 26974 | pNew->h = h; |
| 26975 | pNew->dirfd = dirfd; |
| 26976 | pNew->fileFlags = 0; |
| 26977 | pNew->zPath = zFilename; |
| 26978 | |
| 26979 | #if OS_VXWORKS |
| 26980 | pNew->pId = vxworksFindFileId(zFilename); |
| 26981 | if( pNew->pId==0 ){ |
| @@ -39764,34 +39774,34 @@ | |
| 39774 | /* Higher-level routines never call this function if database is not |
| 39775 | ** writable. But check anyway, just for robustness. */ |
| 39776 | if( NEVER(pPager->readOnly) ) return SQLITE_PERM; |
| 39777 | |
| 39778 | CHECK_PAGE(pPg); |
| 39779 | |
| 39780 | /* The journal file needs to be opened. Higher level routines have already |
| 39781 | ** obtained the necessary locks to begin the write-transaction, but the |
| 39782 | ** rollback journal might not yet be open. Open it now if this is the case. |
| 39783 | ** |
| 39784 | ** This is done before calling sqlite3PcacheMakeDirty() on the page. |
| 39785 | ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then |
| 39786 | ** an error might occur and the pager would end up in WRITER_LOCKED state |
| 39787 | ** with pages marked as dirty in the cache. |
| 39788 | */ |
| 39789 | if( pPager->eState==PAGER_WRITER_LOCKED ){ |
| 39790 | rc = pager_open_journal(pPager); |
| 39791 | if( rc!=SQLITE_OK ) return rc; |
| 39792 | } |
| 39793 | assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); |
| 39794 | assert( assert_pager_state(pPager) ); |
| 39795 | |
| 39796 | /* Mark the page as dirty. If the page has already been written |
| 39797 | ** to the journal then we can return right away. |
| 39798 | */ |
| 39799 | sqlite3PcacheMakeDirty(pPg); |
| 39800 | if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ |
| 39801 | assert( !pagerUseWal(pPager) ); |
| 39802 | }else{ |
| 39803 | |
| 39804 | /* The transaction journal now exists and we have a RESERVED or an |
| 39805 | ** EXCLUSIVE lock on the main database file. Write the current page to |
| 39806 | ** the transaction journal if it is not there already. |
| 39807 | */ |
| @@ -43589,11 +43599,11 @@ | |
| 43599 | ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left |
| 43600 | ** unchanged. |
| 43601 | ** |
| 43602 | ** SQLITE_OK is returned if no error is encountered (regardless of whether |
| 43603 | ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned |
| 43604 | ** if an error occurs. |
| 43605 | */ |
| 43606 | static int walRestartLog(Wal *pWal){ |
| 43607 | int rc = SQLITE_OK; |
| 43608 | int cnt; |
| 43609 | |
| @@ -43622,10 +43632,12 @@ | |
| 43632 | walIndexWriteHdr(pWal); |
| 43633 | pInfo->nBackfill = 0; |
| 43634 | for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
| 43635 | assert( pInfo->aReadMark[0]==0 ); |
| 43636 | walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 43637 | }else if( rc!=SQLITE_BUSY ){ |
| 43638 | return rc; |
| 43639 | } |
| 43640 | } |
| 43641 | walUnlockShared(pWal, WAL_READ_LOCK(0)); |
| 43642 | pWal->readLock = -1; |
| 43643 | cnt = 0; |
| @@ -54130,18 +54142,11 @@ | |
| 54142 | }else if( pMem->flags & MEM_Int ){ |
| 54143 | return (double)pMem->u.i; |
| 54144 | }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ |
| 54145 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54146 | double val = (double)0; |
| 54147 | sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); |
| 54148 | return val; |
| 54149 | }else{ |
| 54150 | /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ |
| 54151 | return (double)0; |
| 54152 | } |
| @@ -87647,11 +87652,10 @@ | |
| 87652 | addr2 = sqlite3VdbeAddOp0(v, OP_Goto); |
| 87653 | sqlite3VdbeJumpHere(v, addr1); |
| 87654 | sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); |
| 87655 | sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); |
| 87656 | sqlite3VdbeJumpHere(v, addr2); |
| 87657 | } |
| 87658 | } |
| 87659 | |
| 87660 | /* |
| 87661 | ** Add code to implement the OFFSET |
| @@ -87926,15 +87930,15 @@ | |
| 87930 | break; |
| 87931 | } |
| 87932 | #endif |
| 87933 | } |
| 87934 | |
| 87935 | /* Jump to the end of the loop if the LIMIT is reached. Except, if |
| 87936 | ** there is a sorter, in which case the sorter has already limited |
| 87937 | ** the output for us. |
| 87938 | */ |
| 87939 | if( pOrderBy==0 && p->iLimit ){ |
| 87940 | sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); |
| 87941 | } |
| 87942 | } |
| 87943 | |
| 87944 | /* |
| @@ -88065,14 +88069,10 @@ | |
| 88069 | } |
| 88070 | } |
| 88071 | sqlite3ReleaseTempReg(pParse, regRow); |
| 88072 | sqlite3ReleaseTempReg(pParse, regRowid); |
| 88073 | |
| 88074 | /* The bottom of the loop |
| 88075 | */ |
| 88076 | sqlite3VdbeResolveLabel(v, addrContinue); |
| 88077 | sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); |
| 88078 | sqlite3VdbeResolveLabel(v, addrBreak); |
| @@ -89386,11 +89386,10 @@ | |
| 89386 | } |
| 89387 | |
| 89388 | /* Separate the left and the right query from one another |
| 89389 | */ |
| 89390 | p->pPrior = 0; |
| 89391 | sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); |
| 89392 | if( pPrior->pPrior==0 ){ |
| 89393 | sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); |
| 89394 | } |
| 89395 | |
| 89396 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | -#define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" | |
| 112 | +#define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | #define SQLITE_SOURCE_ID "2010-10-04 23:55:51 ece641eb8951c6314cedbdb3243f91cb199c3239" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.3" |
| 111 | #define SQLITE_VERSION_NUMBER 3007003 |
| 112 | #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+1
-1
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -103,11 +103,11 @@ | ||
| 103 | 103 | @ <tr><th>Server ID:</th><td> |
| 104 | 104 | @ %h(db_get("server-code","")) |
| 105 | 105 | @ </td></tr> |
| 106 | 106 | |
| 107 | 107 | @ <tr><th>Fossil Version:</th><td> |
| 108 | - @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 108 | + @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) (%h(COMPILER_NAME)) | |
| 109 | 109 | @ </td></tr> |
| 110 | 110 | @ <tr><th>SQLite Version:</th><td> |
| 111 | 111 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)", |
| 112 | 112 | SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION); |
| 113 | 113 | @ %s(zBuf) |
| 114 | 114 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | @ <tr><th>Server ID:</th><td> |
| 104 | @ %h(db_get("server-code","")) |
| 105 | @ </td></tr> |
| 106 | |
| 107 | @ <tr><th>Fossil Version:</th><td> |
| 108 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 109 | @ </td></tr> |
| 110 | @ <tr><th>SQLite Version:</th><td> |
| 111 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)", |
| 112 | SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION); |
| 113 | @ %s(zBuf) |
| 114 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | @ <tr><th>Server ID:</th><td> |
| 104 | @ %h(db_get("server-code","")) |
| 105 | @ </td></tr> |
| 106 | |
| 107 | @ <tr><th>Fossil Version:</th><td> |
| 108 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) (%h(COMPILER_NAME)) |
| 109 | @ </td></tr> |
| 110 | @ <tr><th>SQLite Version:</th><td> |
| 111 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)", |
| 112 | SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION); |
| 113 | @ %s(zBuf) |
| 114 |
+1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -101,10 +101,11 @@ | ||
| 101 | 101 | Th_Store("baseurl", g.zBaseURL); |
| 102 | 102 | Th_Store("index_page", db_get("index-page","/home")); |
| 103 | 103 | Th_Store("current_page", g.zPath); |
| 104 | 104 | Th_Store("manifest_version", MANIFEST_VERSION); |
| 105 | 105 | Th_Store("manifest_date", MANIFEST_DATE); |
| 106 | + Th_Store("compiler_name", COMPILER_NAME); | |
| 106 | 107 | if( g.zLogin ){ |
| 107 | 108 | Th_Store("login", g.zLogin); |
| 108 | 109 | } |
| 109 | 110 | if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); |
| 110 | 111 | Th_Render(zHeader); |
| 111 | 112 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -101,10 +101,11 @@ | |
| 101 | Th_Store("baseurl", g.zBaseURL); |
| 102 | Th_Store("index_page", db_get("index-page","/home")); |
| 103 | Th_Store("current_page", g.zPath); |
| 104 | Th_Store("manifest_version", MANIFEST_VERSION); |
| 105 | Th_Store("manifest_date", MANIFEST_DATE); |
| 106 | if( g.zLogin ){ |
| 107 | Th_Store("login", g.zLogin); |
| 108 | } |
| 109 | if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); |
| 110 | Th_Render(zHeader); |
| 111 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -101,10 +101,11 @@ | |
| 101 | Th_Store("baseurl", g.zBaseURL); |
| 102 | Th_Store("index_page", db_get("index-page","/home")); |
| 103 | Th_Store("current_page", g.zPath); |
| 104 | Th_Store("manifest_version", MANIFEST_VERSION); |
| 105 | Th_Store("manifest_date", MANIFEST_DATE); |
| 106 | Th_Store("compiler_name", COMPILER_NAME); |
| 107 | if( g.zLogin ){ |
| 108 | Th_Store("login", g.zLogin); |
| 109 | } |
| 110 | if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); |
| 111 | Th_Render(zHeader); |
| 112 |
+4
-4
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | } |
| 57 | 57 | zUrl = db_get("last-sync-url", 0); |
| 58 | 58 | if( zUrl==0 ){ |
| 59 | 59 | return; /* No default server */ |
| 60 | 60 | } |
| 61 | - zPw = db_get("last-sync-pw", 0); | |
| 61 | + zPw = unobscure(db_get("last-sync-pw", 0)); | |
| 62 | 62 | url_parse(zUrl); |
| 63 | 63 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 64 | 64 | g.urlPasswd = mprintf("%s", zPw); |
| 65 | 65 | } |
| 66 | 66 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| @@ -93,11 +93,11 @@ | ||
| 93 | 93 | url_proxy_options(); |
| 94 | 94 | db_find_and_open_repository(1); |
| 95 | 95 | db_open_config(0); |
| 96 | 96 | if( g.argc==2 ){ |
| 97 | 97 | zUrl = db_get("last-sync-url", 0); |
| 98 | - zPw = db_get("last-sync-pw", 0); | |
| 98 | + zPw = unobscure(db_get("last-sync-pw", 0)); | |
| 99 | 99 | if( db_get_boolean("auto-sync",1) ) configSync = CONFIGSET_SHUN; |
| 100 | 100 | }else if( g.argc==3 ){ |
| 101 | 101 | zUrl = g.argv[2]; |
| 102 | 102 | } |
| 103 | 103 | if( zUrl==0 ){ |
| @@ -105,11 +105,11 @@ | ||
| 105 | 105 | usage("URL"); |
| 106 | 106 | } |
| 107 | 107 | url_parse(zUrl); |
| 108 | 108 | if( !g.dontKeepUrl ){ |
| 109 | 109 | db_set("last-sync-url", g.urlCanonical, 0); |
| 110 | - if( g.urlPasswd ) db_set("last-sync-pw", g.urlPasswd, 0); | |
| 110 | + if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0); | |
| 111 | 111 | } |
| 112 | 112 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 113 | 113 | if( zPw==0 ){ |
| 114 | 114 | url_prompt_for_password(); |
| 115 | 115 | }else{ |
| @@ -232,11 +232,11 @@ | ||
| 232 | 232 | if( g.urlUser && g.urlPasswd==0 ){ |
| 233 | 233 | url_prompt_for_password(); |
| 234 | 234 | } |
| 235 | 235 | db_set("last-sync-url", g.urlCanonical, 0); |
| 236 | 236 | if( g.urlPasswd ){ |
| 237 | - db_set("last-sync-pw", g.urlPasswd, 0); | |
| 237 | + db_set("last-sync-pw", obscure(g.urlPasswd), 0); | |
| 238 | 238 | }else{ |
| 239 | 239 | db_unset("last-sync-pw", 0); |
| 240 | 240 | } |
| 241 | 241 | } |
| 242 | 242 | } |
| 243 | 243 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -56,11 +56,11 @@ | |
| 56 | } |
| 57 | zUrl = db_get("last-sync-url", 0); |
| 58 | if( zUrl==0 ){ |
| 59 | return; /* No default server */ |
| 60 | } |
| 61 | zPw = db_get("last-sync-pw", 0); |
| 62 | url_parse(zUrl); |
| 63 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 64 | g.urlPasswd = mprintf("%s", zPw); |
| 65 | } |
| 66 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| @@ -93,11 +93,11 @@ | |
| 93 | url_proxy_options(); |
| 94 | db_find_and_open_repository(1); |
| 95 | db_open_config(0); |
| 96 | if( g.argc==2 ){ |
| 97 | zUrl = db_get("last-sync-url", 0); |
| 98 | zPw = db_get("last-sync-pw", 0); |
| 99 | if( db_get_boolean("auto-sync",1) ) configSync = CONFIGSET_SHUN; |
| 100 | }else if( g.argc==3 ){ |
| 101 | zUrl = g.argv[2]; |
| 102 | } |
| 103 | if( zUrl==0 ){ |
| @@ -105,11 +105,11 @@ | |
| 105 | usage("URL"); |
| 106 | } |
| 107 | url_parse(zUrl); |
| 108 | if( !g.dontKeepUrl ){ |
| 109 | db_set("last-sync-url", g.urlCanonical, 0); |
| 110 | if( g.urlPasswd ) db_set("last-sync-pw", g.urlPasswd, 0); |
| 111 | } |
| 112 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 113 | if( zPw==0 ){ |
| 114 | url_prompt_for_password(); |
| 115 | }else{ |
| @@ -232,11 +232,11 @@ | |
| 232 | if( g.urlUser && g.urlPasswd==0 ){ |
| 233 | url_prompt_for_password(); |
| 234 | } |
| 235 | db_set("last-sync-url", g.urlCanonical, 0); |
| 236 | if( g.urlPasswd ){ |
| 237 | db_set("last-sync-pw", g.urlPasswd, 0); |
| 238 | }else{ |
| 239 | db_unset("last-sync-pw", 0); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -56,11 +56,11 @@ | |
| 56 | } |
| 57 | zUrl = db_get("last-sync-url", 0); |
| 58 | if( zUrl==0 ){ |
| 59 | return; /* No default server */ |
| 60 | } |
| 61 | zPw = unobscure(db_get("last-sync-pw", 0)); |
| 62 | url_parse(zUrl); |
| 63 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 64 | g.urlPasswd = mprintf("%s", zPw); |
| 65 | } |
| 66 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| @@ -93,11 +93,11 @@ | |
| 93 | url_proxy_options(); |
| 94 | db_find_and_open_repository(1); |
| 95 | db_open_config(0); |
| 96 | if( g.argc==2 ){ |
| 97 | zUrl = db_get("last-sync-url", 0); |
| 98 | zPw = unobscure(db_get("last-sync-pw", 0)); |
| 99 | if( db_get_boolean("auto-sync",1) ) configSync = CONFIGSET_SHUN; |
| 100 | }else if( g.argc==3 ){ |
| 101 | zUrl = g.argv[2]; |
| 102 | } |
| 103 | if( zUrl==0 ){ |
| @@ -105,11 +105,11 @@ | |
| 105 | usage("URL"); |
| 106 | } |
| 107 | url_parse(zUrl); |
| 108 | if( !g.dontKeepUrl ){ |
| 109 | db_set("last-sync-url", g.urlCanonical, 0); |
| 110 | if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0); |
| 111 | } |
| 112 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 113 | if( zPw==0 ){ |
| 114 | url_prompt_for_password(); |
| 115 | }else{ |
| @@ -232,11 +232,11 @@ | |
| 232 | if( g.urlUser && g.urlPasswd==0 ){ |
| 233 | url_prompt_for_password(); |
| 234 | } |
| 235 | db_set("last-sync-url", g.urlCanonical, 0); |
| 236 | if( g.urlPasswd ){ |
| 237 | db_set("last-sync-pw", obscure(g.urlPasswd), 0); |
| 238 | }else{ |
| 239 | db_unset("last-sync-pw", 0); |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -355,11 +355,11 @@ | ||
| 355 | 355 | ** fossil update tag:decaf |
| 356 | 356 | ** |
| 357 | 357 | ** will assume that "decaf" is a tag/branch name. |
| 358 | 358 | ** |
| 359 | 359 | ** only allow --date-override and --user-override in |
| 360 | -** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \ | |
| 360 | +** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\ | |
| 361 | 361 | ** --user-override user |
| 362 | 362 | ** in order to import history from other scm systems |
| 363 | 363 | */ |
| 364 | 364 | void tag_cmd(void){ |
| 365 | 365 | int n; |
| 366 | 366 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -355,11 +355,11 @@ | |
| 355 | ** fossil update tag:decaf |
| 356 | ** |
| 357 | ** will assume that "decaf" is a tag/branch name. |
| 358 | ** |
| 359 | ** only allow --date-override and --user-override in |
| 360 | ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \ |
| 361 | ** --user-override user |
| 362 | ** in order to import history from other scm systems |
| 363 | */ |
| 364 | void tag_cmd(void){ |
| 365 | int n; |
| 366 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -355,11 +355,11 @@ | |
| 355 | ** fossil update tag:decaf |
| 356 | ** |
| 357 | ** will assume that "decaf" is a tag/branch name. |
| 358 | ** |
| 359 | ** only allow --date-override and --user-override in |
| 360 | ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\ |
| 361 | ** --user-override user |
| 362 | ** in order to import history from other scm systems |
| 363 | */ |
| 364 | void tag_cmd(void){ |
| 365 | int n; |
| 366 |
+2
-1
| --- src/th_lang.c | ||
| +++ src/th_lang.c | ||
| @@ -7,10 +7,11 @@ | ||
| 7 | 7 | ** declared in th.h, so this file serves as both a part of the language |
| 8 | 8 | ** implementation and an example of how to extend the language with |
| 9 | 9 | ** new commands. |
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | +#include "config.h" | |
| 12 | 13 | #include "th.h" |
| 13 | 14 | #include <string.h> |
| 14 | 15 | #include <assert.h> |
| 15 | 16 | |
| 16 | 17 | int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ |
| @@ -571,11 +572,11 @@ | ||
| 571 | 572 | return Th_WrongNumArgs(interp, "return ?value?"); |
| 572 | 573 | } |
| 573 | 574 | if( argc==2 ){ |
| 574 | 575 | Th_SetResult(interp, argv[1], argl[1]); |
| 575 | 576 | } |
| 576 | - return (int)ctx; | |
| 577 | + return FOSSIL_PTR_TO_INT(ctx); | |
| 577 | 578 | } |
| 578 | 579 | |
| 579 | 580 | /* |
| 580 | 581 | ** TH Syntax: |
| 581 | 582 | ** |
| 582 | 583 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -7,10 +7,11 @@ | |
| 7 | ** declared in th.h, so this file serves as both a part of the language |
| 8 | ** implementation and an example of how to extend the language with |
| 9 | ** new commands. |
| 10 | */ |
| 11 | |
| 12 | #include "th.h" |
| 13 | #include <string.h> |
| 14 | #include <assert.h> |
| 15 | |
| 16 | int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ |
| @@ -571,11 +572,11 @@ | |
| 571 | return Th_WrongNumArgs(interp, "return ?value?"); |
| 572 | } |
| 573 | if( argc==2 ){ |
| 574 | Th_SetResult(interp, argv[1], argl[1]); |
| 575 | } |
| 576 | return (int)ctx; |
| 577 | } |
| 578 | |
| 579 | /* |
| 580 | ** TH Syntax: |
| 581 | ** |
| 582 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -7,10 +7,11 @@ | |
| 7 | ** declared in th.h, so this file serves as both a part of the language |
| 8 | ** implementation and an example of how to extend the language with |
| 9 | ** new commands. |
| 10 | */ |
| 11 | |
| 12 | #include "config.h" |
| 13 | #include "th.h" |
| 14 | #include <string.h> |
| 15 | #include <assert.h> |
| 16 | |
| 17 | int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ |
| @@ -571,11 +572,11 @@ | |
| 572 | return Th_WrongNumArgs(interp, "return ?value?"); |
| 573 | } |
| 574 | if( argc==2 ){ |
| 575 | Th_SetResult(interp, argv[1], argl[1]); |
| 576 | } |
| 577 | return FOSSIL_PTR_TO_INT(ctx); |
| 578 | } |
| 579 | |
| 580 | /* |
| 581 | ** TH Syntax: |
| 582 | ** |
| 583 |
+4
-4
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -30,11 +30,11 @@ | ||
| 30 | 30 | |
| 31 | 31 | /* |
| 32 | 32 | ** Implementations of malloc() and free() to pass to the interpreter. |
| 33 | 33 | */ |
| 34 | 34 | static void *xMalloc(unsigned int n){ |
| 35 | - void *p = malloc(n); | |
| 35 | + void *p = fossil_malloc(n); | |
| 36 | 36 | if( p ){ |
| 37 | 37 | nOutstandingMalloc++; |
| 38 | 38 | } |
| 39 | 39 | return p; |
| 40 | 40 | } |
| @@ -435,20 +435,20 @@ | ||
| 435 | 435 | int inBracket = 0; |
| 436 | 436 | if( z[0]=='<' ){ |
| 437 | 437 | inBracket = 1; |
| 438 | 438 | z++; |
| 439 | 439 | } |
| 440 | - if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){ | |
| 440 | + if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){ | |
| 441 | 441 | z += 3; |
| 442 | 442 | i += 3; |
| 443 | - }else if( isalpha(z[0]) ){ | |
| 443 | + }else if( fossil_isalpha(z[0]) ){ | |
| 444 | 444 | z ++; |
| 445 | 445 | i += 1; |
| 446 | 446 | }else{ |
| 447 | 447 | return 0; |
| 448 | 448 | } |
| 449 | - while( isalnum(z[0]) || z[0]=='_' ){ | |
| 449 | + while( fossil_isalnum(z[0]) || z[0]=='_' ){ | |
| 450 | 450 | z++; |
| 451 | 451 | i++; |
| 452 | 452 | } |
| 453 | 453 | if( inBracket ){ |
| 454 | 454 | if( z[0]!='>' ) return 0; |
| 455 | 455 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -30,11 +30,11 @@ | |
| 30 | |
| 31 | /* |
| 32 | ** Implementations of malloc() and free() to pass to the interpreter. |
| 33 | */ |
| 34 | static void *xMalloc(unsigned int n){ |
| 35 | void *p = malloc(n); |
| 36 | if( p ){ |
| 37 | nOutstandingMalloc++; |
| 38 | } |
| 39 | return p; |
| 40 | } |
| @@ -435,20 +435,20 @@ | |
| 435 | int inBracket = 0; |
| 436 | if( z[0]=='<' ){ |
| 437 | inBracket = 1; |
| 438 | z++; |
| 439 | } |
| 440 | if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){ |
| 441 | z += 3; |
| 442 | i += 3; |
| 443 | }else if( isalpha(z[0]) ){ |
| 444 | z ++; |
| 445 | i += 1; |
| 446 | }else{ |
| 447 | return 0; |
| 448 | } |
| 449 | while( isalnum(z[0]) || z[0]=='_' ){ |
| 450 | z++; |
| 451 | i++; |
| 452 | } |
| 453 | if( inBracket ){ |
| 454 | if( z[0]!='>' ) return 0; |
| 455 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -30,11 +30,11 @@ | |
| 30 | |
| 31 | /* |
| 32 | ** Implementations of malloc() and free() to pass to the interpreter. |
| 33 | */ |
| 34 | static void *xMalloc(unsigned int n){ |
| 35 | void *p = fossil_malloc(n); |
| 36 | if( p ){ |
| 37 | nOutstandingMalloc++; |
| 38 | } |
| 39 | return p; |
| 40 | } |
| @@ -435,20 +435,20 @@ | |
| 435 | int inBracket = 0; |
| 436 | if( z[0]=='<' ){ |
| 437 | inBracket = 1; |
| 438 | z++; |
| 439 | } |
| 440 | if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){ |
| 441 | z += 3; |
| 442 | i += 3; |
| 443 | }else if( fossil_isalpha(z[0]) ){ |
| 444 | z ++; |
| 445 | i += 1; |
| 446 | }else{ |
| 447 | return 0; |
| 448 | } |
| 449 | while( fossil_isalnum(z[0]) || z[0]=='_' ){ |
| 450 | z++; |
| 451 | i++; |
| 452 | } |
| 453 | if( inBracket ){ |
| 454 | if( z[0]!='>' ) return 0; |
| 455 |
+8
-5
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -830,11 +830,11 @@ | ||
| 830 | 830 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 831 | 831 | zSearch, zSearch); |
| 832 | 832 | url_add_parameter(&url, "s", zSearch); |
| 833 | 833 | } |
| 834 | 834 | if( zAfter ){ |
| 835 | - while( isspace(zAfter[0]) ){ zAfter++; } | |
| 835 | + while( fossil_isspace(zAfter[0]) ){ zAfter++; } | |
| 836 | 836 | if( zAfter[0] ){ |
| 837 | 837 | blob_appendf(&sql, |
| 838 | 838 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 839 | 839 | " ORDER BY event.mtime ASC", zAfter); |
| 840 | 840 | url_add_parameter(&url, "a", zAfter); |
| @@ -841,21 +841,21 @@ | ||
| 841 | 841 | zBefore = 0; |
| 842 | 842 | }else{ |
| 843 | 843 | zAfter = 0; |
| 844 | 844 | } |
| 845 | 845 | }else if( zBefore ){ |
| 846 | - while( isspace(zBefore[0]) ){ zBefore++; } | |
| 846 | + while( fossil_isspace(zBefore[0]) ){ zBefore++; } | |
| 847 | 847 | if( zBefore[0] ){ |
| 848 | 848 | blob_appendf(&sql, |
| 849 | 849 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 850 | 850 | " ORDER BY event.mtime DESC", zBefore); |
| 851 | 851 | url_add_parameter(&url, "b", zBefore); |
| 852 | 852 | }else{ |
| 853 | 853 | zBefore = 0; |
| 854 | 854 | } |
| 855 | 855 | }else if( zCirca ){ |
| 856 | - while( isspace(zCirca[0]) ){ zCirca++; } | |
| 856 | + while( fossil_isspace(zCirca[0]) ){ zCirca++; } | |
| 857 | 857 | if( zCirca[0] ){ |
| 858 | 858 | double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); |
| 859 | 859 | Blob sql2; |
| 860 | 860 | blob_init(&sql2, blob_str(&sql), -1); |
| 861 | 861 | blob_appendf(&sql2, |
| @@ -943,10 +943,13 @@ | ||
| 943 | 943 | if( nEntry<200 ){ |
| 944 | 944 | timeline_submenu(&url, "200 Entries", "n", "200", 0); |
| 945 | 945 | } |
| 946 | 946 | } |
| 947 | 947 | } |
| 948 | + if( P("showsql") ){ | |
| 949 | + @ <blockquote>%h(blob_str(&sql))</blockquote> | |
| 950 | + } | |
| 948 | 951 | blob_zero(&sql); |
| 949 | 952 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 950 | 953 | @ <h2>%b(&desc)</h2> |
| 951 | 954 | blob_reset(&desc); |
| 952 | 955 | www_print_timeline(&q, tmFlags, 0); |
| @@ -1057,12 +1060,12 @@ | ||
| 1057 | 1060 | */ |
| 1058 | 1061 | static int isIsoDate(const char *z){ |
| 1059 | 1062 | return strlen(z)==10 |
| 1060 | 1063 | && z[4]=='-' |
| 1061 | 1064 | && z[7]=='-' |
| 1062 | - && isdigit(z[0]) | |
| 1063 | - && isdigit(z[5]); | |
| 1065 | + && fossil_isdigit(z[0]) | |
| 1066 | + && fossil_isdigit(z[5]); | |
| 1064 | 1067 | } |
| 1065 | 1068 | |
| 1066 | 1069 | /* |
| 1067 | 1070 | ** COMMAND: timeline |
| 1068 | 1071 | ** |
| 1069 | 1072 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -830,11 +830,11 @@ | |
| 830 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 831 | zSearch, zSearch); |
| 832 | url_add_parameter(&url, "s", zSearch); |
| 833 | } |
| 834 | if( zAfter ){ |
| 835 | while( isspace(zAfter[0]) ){ zAfter++; } |
| 836 | if( zAfter[0] ){ |
| 837 | blob_appendf(&sql, |
| 838 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 839 | " ORDER BY event.mtime ASC", zAfter); |
| 840 | url_add_parameter(&url, "a", zAfter); |
| @@ -841,21 +841,21 @@ | |
| 841 | zBefore = 0; |
| 842 | }else{ |
| 843 | zAfter = 0; |
| 844 | } |
| 845 | }else if( zBefore ){ |
| 846 | while( isspace(zBefore[0]) ){ zBefore++; } |
| 847 | if( zBefore[0] ){ |
| 848 | blob_appendf(&sql, |
| 849 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 850 | " ORDER BY event.mtime DESC", zBefore); |
| 851 | url_add_parameter(&url, "b", zBefore); |
| 852 | }else{ |
| 853 | zBefore = 0; |
| 854 | } |
| 855 | }else if( zCirca ){ |
| 856 | while( isspace(zCirca[0]) ){ zCirca++; } |
| 857 | if( zCirca[0] ){ |
| 858 | double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); |
| 859 | Blob sql2; |
| 860 | blob_init(&sql2, blob_str(&sql), -1); |
| 861 | blob_appendf(&sql2, |
| @@ -943,10 +943,13 @@ | |
| 943 | if( nEntry<200 ){ |
| 944 | timeline_submenu(&url, "200 Entries", "n", "200", 0); |
| 945 | } |
| 946 | } |
| 947 | } |
| 948 | blob_zero(&sql); |
| 949 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 950 | @ <h2>%b(&desc)</h2> |
| 951 | blob_reset(&desc); |
| 952 | www_print_timeline(&q, tmFlags, 0); |
| @@ -1057,12 +1060,12 @@ | |
| 1057 | */ |
| 1058 | static int isIsoDate(const char *z){ |
| 1059 | return strlen(z)==10 |
| 1060 | && z[4]=='-' |
| 1061 | && z[7]=='-' |
| 1062 | && isdigit(z[0]) |
| 1063 | && isdigit(z[5]); |
| 1064 | } |
| 1065 | |
| 1066 | /* |
| 1067 | ** COMMAND: timeline |
| 1068 | ** |
| 1069 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -830,11 +830,11 @@ | |
| 830 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 831 | zSearch, zSearch); |
| 832 | url_add_parameter(&url, "s", zSearch); |
| 833 | } |
| 834 | if( zAfter ){ |
| 835 | while( fossil_isspace(zAfter[0]) ){ zAfter++; } |
| 836 | if( zAfter[0] ){ |
| 837 | blob_appendf(&sql, |
| 838 | " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 839 | " ORDER BY event.mtime ASC", zAfter); |
| 840 | url_add_parameter(&url, "a", zAfter); |
| @@ -841,21 +841,21 @@ | |
| 841 | zBefore = 0; |
| 842 | }else{ |
| 843 | zAfter = 0; |
| 844 | } |
| 845 | }else if( zBefore ){ |
| 846 | while( fossil_isspace(zBefore[0]) ){ zBefore++; } |
| 847 | if( zBefore[0] ){ |
| 848 | blob_appendf(&sql, |
| 849 | " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 850 | " ORDER BY event.mtime DESC", zBefore); |
| 851 | url_add_parameter(&url, "b", zBefore); |
| 852 | }else{ |
| 853 | zBefore = 0; |
| 854 | } |
| 855 | }else if( zCirca ){ |
| 856 | while( fossil_isspace(zCirca[0]) ){ zCirca++; } |
| 857 | if( zCirca[0] ){ |
| 858 | double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); |
| 859 | Blob sql2; |
| 860 | blob_init(&sql2, blob_str(&sql), -1); |
| 861 | blob_appendf(&sql2, |
| @@ -943,10 +943,13 @@ | |
| 943 | if( nEntry<200 ){ |
| 944 | timeline_submenu(&url, "200 Entries", "n", "200", 0); |
| 945 | } |
| 946 | } |
| 947 | } |
| 948 | if( P("showsql") ){ |
| 949 | @ <blockquote>%h(blob_str(&sql))</blockquote> |
| 950 | } |
| 951 | blob_zero(&sql); |
| 952 | db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); |
| 953 | @ <h2>%b(&desc)</h2> |
| 954 | blob_reset(&desc); |
| 955 | www_print_timeline(&q, tmFlags, 0); |
| @@ -1057,12 +1060,12 @@ | |
| 1060 | */ |
| 1061 | static int isIsoDate(const char *z){ |
| 1062 | return strlen(z)==10 |
| 1063 | && z[4]=='-' |
| 1064 | && z[7]=='-' |
| 1065 | && fossil_isdigit(z[0]) |
| 1066 | && fossil_isdigit(z[5]); |
| 1067 | } |
| 1068 | |
| 1069 | /* |
| 1070 | ** COMMAND: timeline |
| 1071 | ** |
| 1072 |
+257
-20
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -53,14 +53,11 @@ | ||
| 53 | 53 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 54 | 54 | while( db_step(&q)==SQLITE_ROW ){ |
| 55 | 55 | const char *zField = db_column_text(&q, 1); |
| 56 | 56 | if( strncmp(zField,"tkt_",4)==0 ) continue; |
| 57 | 57 | if( nField%10==0 ){ |
| 58 | - azField = realloc(azField, sizeof(azField)*3*(nField+10) ); | |
| 59 | - if( azField==0 ){ | |
| 60 | - fossil_fatal("out of memory"); | |
| 61 | - } | |
| 58 | + azField = fossil_realloc(azField, sizeof(azField)*3*(nField+10) ); | |
| 62 | 59 | } |
| 63 | 60 | azField[nField] = mprintf("%s", zField); |
| 64 | 61 | nField++; |
| 65 | 62 | } |
| 66 | 63 | db_finalize(&q); |
| @@ -217,25 +214,25 @@ | ||
| 217 | 214 | */ |
| 218 | 215 | void ticket_rebuild_entry(const char *zTktUuid){ |
| 219 | 216 | char *zTag = mprintf("tkt-%s", zTktUuid); |
| 220 | 217 | int tagid = tag_findid(zTag, 1); |
| 221 | 218 | Stmt q; |
| 222 | - Manifest manifest; | |
| 223 | - Blob content; | |
| 219 | + Manifest *pTicket; | |
| 224 | 220 | int createFlag = 1; |
| 225 | 221 | |
| 226 | 222 | db_multi_exec( |
| 227 | 223 | "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid |
| 228 | 224 | ); |
| 229 | 225 | db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); |
| 230 | 226 | while( db_step(&q)==SQLITE_ROW ){ |
| 231 | 227 | int rid = db_column_int(&q, 0); |
| 232 | - content_get(rid, &content); | |
| 233 | - manifest_parse(&manifest, &content); | |
| 234 | - ticket_insert(&manifest, createFlag, rid); | |
| 235 | - manifest_ticket_event(rid, &manifest, createFlag, tagid); | |
| 236 | - manifest_clear(&manifest); | |
| 228 | + pTicket = manifest_get(rid, CFTYPE_TICKET); | |
| 229 | + if( pTicket ){ | |
| 230 | + ticket_insert(pTicket, createFlag, rid); | |
| 231 | + manifest_ticket_event(rid, pTicket, createFlag, tagid); | |
| 232 | + manifest_destroy(pTicket); | |
| 233 | + } | |
| 237 | 234 | createFlag = 0; |
| 238 | 235 | } |
| 239 | 236 | db_finalize(&q); |
| 240 | 237 | } |
| 241 | 238 | |
| @@ -445,11 +442,11 @@ | ||
| 445 | 442 | blob_appendf(&tktchng, "J +%s %z\n", azField[i], |
| 446 | 443 | fossilize(azAppend[i], -1)); |
| 447 | 444 | }else{ |
| 448 | 445 | zValue = Th_Fetch(azField[i], &nValue); |
| 449 | 446 | if( zValue ){ |
| 450 | - while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; } | |
| 447 | + while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } | |
| 451 | 448 | if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ |
| 452 | 449 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 453 | 450 | zValue = db_conceal(zValue, nValue); |
| 454 | 451 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 455 | 452 | }else{ |
| @@ -469,11 +466,18 @@ | ||
| 469 | 466 | *(const char**)pUuid = zUuid; |
| 470 | 467 | blob_appendf(&tktchng, "K %s\n", zUuid); |
| 471 | 468 | blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); |
| 472 | 469 | md5sum_blob(&tktchng, &cksum); |
| 473 | 470 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 474 | - if( g.thTrace ){ | |
| 471 | + if( g.zPath[0]=='d' ){ | |
| 472 | + /* If called from /debug_tktnew or /debug_tktedit... */ | |
| 473 | + @ <font color="blue"> | |
| 474 | + @ <p>Ticket artifact that would have been submitted:</p> | |
| 475 | + @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> | |
| 476 | + @ <hr /></font> | |
| 477 | + return TH_OK; | |
| 478 | + }else if( g.thTrace ){ | |
| 475 | 479 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 476 | 480 | "}<br />\n", |
| 477 | 481 | blob_str(&tktchng)); |
| 478 | 482 | }else{ |
| 479 | 483 | rid = content_put(&tktchng, 0, 0); |
| @@ -749,12 +753,11 @@ | ||
| 749 | 753 | " AND blob.rid=attachid" |
| 750 | 754 | " ORDER BY 1 DESC", |
| 751 | 755 | tagid, tagid |
| 752 | 756 | ); |
| 753 | 757 | while( db_step(&q)==SQLITE_ROW ){ |
| 754 | - Blob content; | |
| 755 | - Manifest m; | |
| 758 | + Manifest *pTicket; | |
| 756 | 759 | char zShort[12]; |
| 757 | 760 | const char *zDate = db_column_text(&q, 0); |
| 758 | 761 | int rid = db_column_int(&q, 1); |
| 759 | 762 | const char *zChngUuid = db_column_text(&q, 2); |
| 760 | 763 | const char *zFile = db_column_text(&q, 4); |
| @@ -773,22 +776,22 @@ | ||
| 773 | 776 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 774 | 777 | @ (rid %d(rid)) by |
| 775 | 778 | hyperlink_to_user(zUser,zDate," on"); |
| 776 | 779 | hyperlink_to_date(zDate, ".</p>"); |
| 777 | 780 | }else{ |
| 778 | - content_get(rid, &content); | |
| 779 | - if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){ | |
| 781 | + pTicket = manifest_get(rid, CFTYPE_TICKET); | |
| 782 | + if( pTicket ){ | |
| 780 | 783 | @ |
| 781 | 784 | @ <p>Ticket change |
| 782 | 785 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 783 | 786 | @ (rid %d(rid)) by |
| 784 | - hyperlink_to_user(m.zUser,zDate," on"); | |
| 787 | + hyperlink_to_user(pTicket->zUser,zDate," on"); | |
| 785 | 788 | hyperlink_to_date(zDate, ":"); |
| 786 | 789 | @ </p> |
| 787 | - ticket_output_change_artifact(&m); | |
| 790 | + ticket_output_change_artifact(pTicket); | |
| 788 | 791 | } |
| 789 | - manifest_clear(&m); | |
| 792 | + manifest_destroy(pTicket); | |
| 790 | 793 | } |
| 791 | 794 | } |
| 792 | 795 | db_finalize(&q); |
| 793 | 796 | style_footer(); |
| 794 | 797 | } |
| @@ -830,5 +833,239 @@ | ||
| 830 | 833 | } |
| 831 | 834 | blob_reset(&val); |
| 832 | 835 | } |
| 833 | 836 | @ </ol> |
| 834 | 837 | } |
| 838 | + | |
| 839 | +/* | |
| 840 | +** COMMAND: ticket | |
| 841 | +** Usage: %fossil ticket SUBCOMMAND ... | |
| 842 | +** | |
| 843 | +** Run various subcommands to control tickets | |
| 844 | +** | |
| 845 | +** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? | |
| 846 | +** | |
| 847 | +** options can be: | |
| 848 | +** ?-l|--limit LIMITCHAR? | |
| 849 | +** ?-q|--quote? | |
| 850 | +** ?-R|--repository FILE? | |
| 851 | +** | |
| 852 | +** Run the ticket report, identified by the report format title | |
| 853 | +** used in the gui. The data is written as flat file on stdout, | |
| 854 | +** using "," as separator. The seperator "," can be changed using | |
| 855 | +** the -l or --limit option. | |
| 856 | +** If TICKETFILTER is given on the commandline, the query is | |
| 857 | +** limited with a new WHERE-condition. | |
| 858 | +** example: Report lists a column # with the uuid | |
| 859 | +** TICKETFILTER may be [#]='uuuuuuuuu' | |
| 860 | +** example: Report only lists rows with status not open | |
| 861 | +** TICKETFILTER: status != 'open' | |
| 862 | +** If the option -q|--quote is used, the tickets are encoded by | |
| 863 | +** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n, | |
| 864 | +** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). | |
| 865 | +** Otherwise, the simplified encoding as on the show report raw | |
| 866 | +** page in the gui is used. | |
| 867 | +** | |
| 868 | +** Instead of the report title its possible to use the report | |
| 869 | +** number. Using the special report number 0 list all columns, | |
| 870 | +** defined in the ticket table. | |
| 871 | +** | |
| 872 | +** %fossil ticket list fields | |
| 873 | +** | |
| 874 | +** list all fields, defined for ticket in the fossil repository | |
| 875 | +** | |
| 876 | +** %fossil ticket list reports | |
| 877 | +** | |
| 878 | +** list all ticket reports, defined in the fossil repository | |
| 879 | +** | |
| 880 | +** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 881 | +** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 882 | +** | |
| 883 | +** change ticket identified by TICKETUUID and set the value of | |
| 884 | +** field FIELD to VALUE. Valid field descriptions are: | |
| 885 | +** status, type, severity, priority, resolution, | |
| 886 | +** foundin, private_contact, resolution, title or comment | |
| 887 | +** Field names given above are the ones, defined in a standard | |
| 888 | +** fossil environment. If you have added, deleted columns, you | |
| 889 | +** change the all your configured columns. | |
| 890 | +** You can use more than one field/value pair on the commandline. | |
| 891 | +** Using -q|--quote enables the special character decoding as | |
| 892 | +** in "ticket show". So it's possible, to set multiline text or | |
| 893 | +** text with special characters. | |
| 894 | +** | |
| 895 | +** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? | |
| 896 | +** | |
| 897 | +** like set, but create a new ticket with the given values. | |
| 898 | +** | |
| 899 | +** The values in set|add are not validated against the definitions | |
| 900 | +** given in "Ticket Common Script". | |
| 901 | +*/ | |
| 902 | +void ticket_cmd(void){ | |
| 903 | + int n; | |
| 904 | + | |
| 905 | + /* do some ints, we want to be inside a checkout */ | |
| 906 | + db_find_and_open_repository(1); | |
| 907 | + user_select(); | |
| 908 | + /* | |
| 909 | + ** Check that the user exists. | |
| 910 | + */ | |
| 911 | + if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ | |
| 912 | + fossil_fatal("no such user: %s", g.zLogin); | |
| 913 | + } | |
| 914 | + | |
| 915 | + if( g.argc<3 ){ | |
| 916 | + usage("add|fieldlist|set|show"); | |
| 917 | + }else{ | |
| 918 | + n = strlen(g.argv[2]); | |
| 919 | + if( n==1 && g.argv[2][0]=='s' ){ | |
| 920 | + /* set/show cannot be distinguished, so show the usage */ | |
| 921 | + usage("add|fieldlist|set|show"); | |
| 922 | + }else if( strncmp(g.argv[2],"list",n)==0 ){ | |
| 923 | + if( g.argc==3 ){ | |
| 924 | + usage("list fields|reports"); | |
| 925 | + }else{ | |
| 926 | + n = strlen(g.argv[3]); | |
| 927 | + if( !strncmp(g.argv[3],"fields",n) ){ | |
| 928 | + /* simply show all field names */ | |
| 929 | + int i; | |
| 930 | + | |
| 931 | + /* read all available ticket fields */ | |
| 932 | + getAllTicketFields(); | |
| 933 | + for(i=0; i<nField; i++){ | |
| 934 | + printf("%s\n",azField[i]); | |
| 935 | + } | |
| 936 | + }else if( !strncmp(g.argv[3],"reports",n) ){ | |
| 937 | + rpt_list_reports(); | |
| 938 | + }else{ | |
| 939 | + fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); | |
| 940 | + } | |
| 941 | + } | |
| 942 | + }else{ | |
| 943 | + /* add a new ticket or set fields on existing tickets */ | |
| 944 | + tTktShowEncoding tktEncoding; | |
| 945 | + | |
| 946 | + tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; | |
| 947 | + | |
| 948 | + if( strncmp(g.argv[2],"show",n)==0 ){ | |
| 949 | + if( g.argc==3 ){ | |
| 950 | + usage("show REPORTNR"); | |
| 951 | + }else{ | |
| 952 | + const char *zRep = 0; | |
| 953 | + const char *zSep = 0; | |
| 954 | + const char *zFilterUuid = 0; | |
| 955 | + | |
| 956 | + zSep = find_option("limit","l",1); | |
| 957 | + zRep = g.argv[3]; | |
| 958 | + if( !strcmp(zRep,"0") ){ | |
| 959 | + zRep = 0; | |
| 960 | + } | |
| 961 | + if( g.argc>4 ){ | |
| 962 | + zFilterUuid = g.argv[4]; | |
| 963 | + } | |
| 964 | + | |
| 965 | + rptshow( zRep, zSep, zFilterUuid, tktEncoding ); | |
| 966 | + | |
| 967 | + } | |
| 968 | + }else{ | |
| 969 | + /* add a new ticket or update an existing ticket */ | |
| 970 | + enum { set,add,err } eCmd = err; | |
| 971 | + int i = 0; | |
| 972 | + int rid; | |
| 973 | + const char *zTktUuid = 0; | |
| 974 | + Blob tktchng, cksum; | |
| 975 | + | |
| 976 | + /* get command type (set/add) and get uuid, if needed for set */ | |
| 977 | + if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ | |
| 978 | + eCmd = set; | |
| 979 | + if( g.argc==3 ){ | |
| 980 | + usage("set TICKETUUID"); | |
| 981 | + } | |
| 982 | + zTktUuid = db_text(0, | |
| 983 | + "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] | |
| 984 | + ); | |
| 985 | + if( !zTktUuid ){ | |
| 986 | + fossil_fatal("unknown ticket: '%s'!",g.argv[3]); | |
| 987 | + } | |
| 988 | + i=4; | |
| 989 | + }else if( strncmp(g.argv[2],"add",n)==0 ){ | |
| 990 | + eCmd = add; | |
| 991 | + i = 3; | |
| 992 | + zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); | |
| 993 | + } | |
| 994 | + /* none of set/add, so show the usage! */ | |
| 995 | + if( eCmd==err ){ | |
| 996 | + usage("add|fieldlist|set|show"); | |
| 997 | + } | |
| 998 | + | |
| 999 | + /* read all given ticket field/value pairs from command line */ | |
| 1000 | + if( i==g.argc ){ | |
| 1001 | + fossil_fatal("empty %s command aborted!",g.argv[2]); | |
| 1002 | + } | |
| 1003 | + getAllTicketFields(); | |
| 1004 | + /* read commandline and assign fields in the azValue array */ | |
| 1005 | + while( i<g.argc ){ | |
| 1006 | + char *zFName; | |
| 1007 | + char *zFValue; | |
| 1008 | + int j; | |
| 1009 | + | |
| 1010 | + zFName = g.argv[i++]; | |
| 1011 | + if( i==g.argc ){ | |
| 1012 | + fossil_fatal("missing value for '%s'!",zFName); | |
| 1013 | + } | |
| 1014 | + zFValue = g.argv[i++]; | |
| 1015 | + j = fieldId(zFName); | |
| 1016 | + if( tktEncoding == tktFossilize ){ | |
| 1017 | + zFValue=mprintf("%s",zFValue); | |
| 1018 | + defossilize(zFValue); | |
| 1019 | + } | |
| 1020 | + if( j == -1 ){ | |
| 1021 | + fossil_fatal("unknown field name '%s'!",zFName); | |
| 1022 | + }else{ | |
| 1023 | + azValue[j] = zFValue; | |
| 1024 | + } | |
| 1025 | + } | |
| 1026 | + | |
| 1027 | + /* now add the needed artifacts to the repository */ | |
| 1028 | + blob_zero(&tktchng); | |
| 1029 | + { /* add the time to the ticket manifest */ | |
| 1030 | + char *zDate; | |
| 1031 | + | |
| 1032 | + zDate = db_text(0, "SELECT datetime('now')"); | |
| 1033 | + zDate[10] = 'T'; | |
| 1034 | + blob_appendf(&tktchng, "D %s\n", zDate); | |
| 1035 | + free(zDate); | |
| 1036 | + } | |
| 1037 | + /* append defined elements */ | |
| 1038 | + for(i=0; i<nField; i++){ | |
| 1039 | + char *zValue; | |
| 1040 | + | |
| 1041 | + zValue = azValue[i]; | |
| 1042 | + if( azValue[i] && azValue[i][0] ){ | |
| 1043 | + if( strncmp(azField[i], "private_", 8)==0 ){ | |
| 1044 | + zValue = db_conceal(zValue, strlen(zValue)); | |
| 1045 | + blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); | |
| 1046 | + }else{ | |
| 1047 | + blob_appendf(&tktchng, "J %s %#F\n", | |
| 1048 | + azField[i], strlen(zValue), zValue); | |
| 1049 | + } | |
| 1050 | + if( tktEncoding == tktFossilize ){ | |
| 1051 | + free(azValue[i]); | |
| 1052 | + } | |
| 1053 | + } | |
| 1054 | + } | |
| 1055 | + blob_appendf(&tktchng, "K %s\n", zTktUuid); | |
| 1056 | + blob_appendf(&tktchng, "U %F\n", g.zLogin); | |
| 1057 | + md5sum_blob(&tktchng, &cksum); | |
| 1058 | + blob_appendf(&tktchng, "Z %b\n", &cksum); | |
| 1059 | + rid = content_put(&tktchng, 0, 0); | |
| 1060 | + if( rid==0 ){ | |
| 1061 | + fossil_panic("trouble committing ticket: %s", g.zErrMsg); | |
| 1062 | + } | |
| 1063 | + manifest_crosslink_begin(); | |
| 1064 | + manifest_crosslink(rid, &tktchng); | |
| 1065 | + manifest_crosslink_end(); | |
| 1066 | + printf("ticket %s succeeded for UID %s\n", | |
| 1067 | + (eCmd==set?"set":"add"),zTktUuid); | |
| 1068 | + } | |
| 1069 | + } | |
| 1070 | + } | |
| 1071 | +} | |
| 835 | 1072 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -53,14 +53,11 @@ | |
| 53 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 54 | while( db_step(&q)==SQLITE_ROW ){ |
| 55 | const char *zField = db_column_text(&q, 1); |
| 56 | if( strncmp(zField,"tkt_",4)==0 ) continue; |
| 57 | if( nField%10==0 ){ |
| 58 | azField = realloc(azField, sizeof(azField)*3*(nField+10) ); |
| 59 | if( azField==0 ){ |
| 60 | fossil_fatal("out of memory"); |
| 61 | } |
| 62 | } |
| 63 | azField[nField] = mprintf("%s", zField); |
| 64 | nField++; |
| 65 | } |
| 66 | db_finalize(&q); |
| @@ -217,25 +214,25 @@ | |
| 217 | */ |
| 218 | void ticket_rebuild_entry(const char *zTktUuid){ |
| 219 | char *zTag = mprintf("tkt-%s", zTktUuid); |
| 220 | int tagid = tag_findid(zTag, 1); |
| 221 | Stmt q; |
| 222 | Manifest manifest; |
| 223 | Blob content; |
| 224 | int createFlag = 1; |
| 225 | |
| 226 | db_multi_exec( |
| 227 | "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid |
| 228 | ); |
| 229 | db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); |
| 230 | while( db_step(&q)==SQLITE_ROW ){ |
| 231 | int rid = db_column_int(&q, 0); |
| 232 | content_get(rid, &content); |
| 233 | manifest_parse(&manifest, &content); |
| 234 | ticket_insert(&manifest, createFlag, rid); |
| 235 | manifest_ticket_event(rid, &manifest, createFlag, tagid); |
| 236 | manifest_clear(&manifest); |
| 237 | createFlag = 0; |
| 238 | } |
| 239 | db_finalize(&q); |
| 240 | } |
| 241 | |
| @@ -445,11 +442,11 @@ | |
| 445 | blob_appendf(&tktchng, "J +%s %z\n", azField[i], |
| 446 | fossilize(azAppend[i], -1)); |
| 447 | }else{ |
| 448 | zValue = Th_Fetch(azField[i], &nValue); |
| 449 | if( zValue ){ |
| 450 | while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; } |
| 451 | if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ |
| 452 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 453 | zValue = db_conceal(zValue, nValue); |
| 454 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 455 | }else{ |
| @@ -469,11 +466,18 @@ | |
| 469 | *(const char**)pUuid = zUuid; |
| 470 | blob_appendf(&tktchng, "K %s\n", zUuid); |
| 471 | blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); |
| 472 | md5sum_blob(&tktchng, &cksum); |
| 473 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 474 | if( g.thTrace ){ |
| 475 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 476 | "}<br />\n", |
| 477 | blob_str(&tktchng)); |
| 478 | }else{ |
| 479 | rid = content_put(&tktchng, 0, 0); |
| @@ -749,12 +753,11 @@ | |
| 749 | " AND blob.rid=attachid" |
| 750 | " ORDER BY 1 DESC", |
| 751 | tagid, tagid |
| 752 | ); |
| 753 | while( db_step(&q)==SQLITE_ROW ){ |
| 754 | Blob content; |
| 755 | Manifest m; |
| 756 | char zShort[12]; |
| 757 | const char *zDate = db_column_text(&q, 0); |
| 758 | int rid = db_column_int(&q, 1); |
| 759 | const char *zChngUuid = db_column_text(&q, 2); |
| 760 | const char *zFile = db_column_text(&q, 4); |
| @@ -773,22 +776,22 @@ | |
| 773 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 774 | @ (rid %d(rid)) by |
| 775 | hyperlink_to_user(zUser,zDate," on"); |
| 776 | hyperlink_to_date(zDate, ".</p>"); |
| 777 | }else{ |
| 778 | content_get(rid, &content); |
| 779 | if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){ |
| 780 | @ |
| 781 | @ <p>Ticket change |
| 782 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 783 | @ (rid %d(rid)) by |
| 784 | hyperlink_to_user(m.zUser,zDate," on"); |
| 785 | hyperlink_to_date(zDate, ":"); |
| 786 | @ </p> |
| 787 | ticket_output_change_artifact(&m); |
| 788 | } |
| 789 | manifest_clear(&m); |
| 790 | } |
| 791 | } |
| 792 | db_finalize(&q); |
| 793 | style_footer(); |
| 794 | } |
| @@ -830,5 +833,239 @@ | |
| 830 | } |
| 831 | blob_reset(&val); |
| 832 | } |
| 833 | @ </ol> |
| 834 | } |
| 835 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -53,14 +53,11 @@ | |
| 53 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 54 | while( db_step(&q)==SQLITE_ROW ){ |
| 55 | const char *zField = db_column_text(&q, 1); |
| 56 | if( strncmp(zField,"tkt_",4)==0 ) continue; |
| 57 | if( nField%10==0 ){ |
| 58 | azField = fossil_realloc(azField, sizeof(azField)*3*(nField+10) ); |
| 59 | } |
| 60 | azField[nField] = mprintf("%s", zField); |
| 61 | nField++; |
| 62 | } |
| 63 | db_finalize(&q); |
| @@ -217,25 +214,25 @@ | |
| 214 | */ |
| 215 | void ticket_rebuild_entry(const char *zTktUuid){ |
| 216 | char *zTag = mprintf("tkt-%s", zTktUuid); |
| 217 | int tagid = tag_findid(zTag, 1); |
| 218 | Stmt q; |
| 219 | Manifest *pTicket; |
| 220 | int createFlag = 1; |
| 221 | |
| 222 | db_multi_exec( |
| 223 | "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid |
| 224 | ); |
| 225 | db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); |
| 226 | while( db_step(&q)==SQLITE_ROW ){ |
| 227 | int rid = db_column_int(&q, 0); |
| 228 | pTicket = manifest_get(rid, CFTYPE_TICKET); |
| 229 | if( pTicket ){ |
| 230 | ticket_insert(pTicket, createFlag, rid); |
| 231 | manifest_ticket_event(rid, pTicket, createFlag, tagid); |
| 232 | manifest_destroy(pTicket); |
| 233 | } |
| 234 | createFlag = 0; |
| 235 | } |
| 236 | db_finalize(&q); |
| 237 | } |
| 238 | |
| @@ -445,11 +442,11 @@ | |
| 442 | blob_appendf(&tktchng, "J +%s %z\n", azField[i], |
| 443 | fossilize(azAppend[i], -1)); |
| 444 | }else{ |
| 445 | zValue = Th_Fetch(azField[i], &nValue); |
| 446 | if( zValue ){ |
| 447 | while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } |
| 448 | if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ |
| 449 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 450 | zValue = db_conceal(zValue, nValue); |
| 451 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 452 | }else{ |
| @@ -469,11 +466,18 @@ | |
| 466 | *(const char**)pUuid = zUuid; |
| 467 | blob_appendf(&tktchng, "K %s\n", zUuid); |
| 468 | blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); |
| 469 | md5sum_blob(&tktchng, &cksum); |
| 470 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 471 | if( g.zPath[0]=='d' ){ |
| 472 | /* If called from /debug_tktnew or /debug_tktedit... */ |
| 473 | @ <font color="blue"> |
| 474 | @ <p>Ticket artifact that would have been submitted:</p> |
| 475 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 476 | @ <hr /></font> |
| 477 | return TH_OK; |
| 478 | }else if( g.thTrace ){ |
| 479 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 480 | "}<br />\n", |
| 481 | blob_str(&tktchng)); |
| 482 | }else{ |
| 483 | rid = content_put(&tktchng, 0, 0); |
| @@ -749,12 +753,11 @@ | |
| 753 | " AND blob.rid=attachid" |
| 754 | " ORDER BY 1 DESC", |
| 755 | tagid, tagid |
| 756 | ); |
| 757 | while( db_step(&q)==SQLITE_ROW ){ |
| 758 | Manifest *pTicket; |
| 759 | char zShort[12]; |
| 760 | const char *zDate = db_column_text(&q, 0); |
| 761 | int rid = db_column_int(&q, 1); |
| 762 | const char *zChngUuid = db_column_text(&q, 2); |
| 763 | const char *zFile = db_column_text(&q, 4); |
| @@ -773,22 +776,22 @@ | |
| 776 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 777 | @ (rid %d(rid)) by |
| 778 | hyperlink_to_user(zUser,zDate," on"); |
| 779 | hyperlink_to_date(zDate, ".</p>"); |
| 780 | }else{ |
| 781 | pTicket = manifest_get(rid, CFTYPE_TICKET); |
| 782 | if( pTicket ){ |
| 783 | @ |
| 784 | @ <p>Ticket change |
| 785 | @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>] |
| 786 | @ (rid %d(rid)) by |
| 787 | hyperlink_to_user(pTicket->zUser,zDate," on"); |
| 788 | hyperlink_to_date(zDate, ":"); |
| 789 | @ </p> |
| 790 | ticket_output_change_artifact(pTicket); |
| 791 | } |
| 792 | manifest_destroy(pTicket); |
| 793 | } |
| 794 | } |
| 795 | db_finalize(&q); |
| 796 | style_footer(); |
| 797 | } |
| @@ -830,5 +833,239 @@ | |
| 833 | } |
| 834 | blob_reset(&val); |
| 835 | } |
| 836 | @ </ol> |
| 837 | } |
| 838 | |
| 839 | /* |
| 840 | ** COMMAND: ticket |
| 841 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 842 | ** |
| 843 | ** Run various subcommands to control tickets |
| 844 | ** |
| 845 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| 846 | ** |
| 847 | ** options can be: |
| 848 | ** ?-l|--limit LIMITCHAR? |
| 849 | ** ?-q|--quote? |
| 850 | ** ?-R|--repository FILE? |
| 851 | ** |
| 852 | ** Run the ticket report, identified by the report format title |
| 853 | ** used in the gui. The data is written as flat file on stdout, |
| 854 | ** using "," as separator. The seperator "," can be changed using |
| 855 | ** the -l or --limit option. |
| 856 | ** If TICKETFILTER is given on the commandline, the query is |
| 857 | ** limited with a new WHERE-condition. |
| 858 | ** example: Report lists a column # with the uuid |
| 859 | ** TICKETFILTER may be [#]='uuuuuuuuu' |
| 860 | ** example: Report only lists rows with status not open |
| 861 | ** TICKETFILTER: status != 'open' |
| 862 | ** If the option -q|--quote is used, the tickets are encoded by |
| 863 | ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n, |
| 864 | ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). |
| 865 | ** Otherwise, the simplified encoding as on the show report raw |
| 866 | ** page in the gui is used. |
| 867 | ** |
| 868 | ** Instead of the report title its possible to use the report |
| 869 | ** number. Using the special report number 0 list all columns, |
| 870 | ** defined in the ticket table. |
| 871 | ** |
| 872 | ** %fossil ticket list fields |
| 873 | ** |
| 874 | ** list all fields, defined for ticket in the fossil repository |
| 875 | ** |
| 876 | ** %fossil ticket list reports |
| 877 | ** |
| 878 | ** list all ticket reports, defined in the fossil repository |
| 879 | ** |
| 880 | ** %fossil ticket set TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 881 | ** %fossil ticket change TICKETUUID FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 882 | ** |
| 883 | ** change ticket identified by TICKETUUID and set the value of |
| 884 | ** field FIELD to VALUE. Valid field descriptions are: |
| 885 | ** status, type, severity, priority, resolution, |
| 886 | ** foundin, private_contact, resolution, title or comment |
| 887 | ** Field names given above are the ones, defined in a standard |
| 888 | ** fossil environment. If you have added, deleted columns, you |
| 889 | ** change the all your configured columns. |
| 890 | ** You can use more than one field/value pair on the commandline. |
| 891 | ** Using -q|--quote enables the special character decoding as |
| 892 | ** in "ticket show". So it's possible, to set multiline text or |
| 893 | ** text with special characters. |
| 894 | ** |
| 895 | ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? |
| 896 | ** |
| 897 | ** like set, but create a new ticket with the given values. |
| 898 | ** |
| 899 | ** The values in set|add are not validated against the definitions |
| 900 | ** given in "Ticket Common Script". |
| 901 | */ |
| 902 | void ticket_cmd(void){ |
| 903 | int n; |
| 904 | |
| 905 | /* do some ints, we want to be inside a checkout */ |
| 906 | db_find_and_open_repository(1); |
| 907 | user_select(); |
| 908 | /* |
| 909 | ** Check that the user exists. |
| 910 | */ |
| 911 | if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ |
| 912 | fossil_fatal("no such user: %s", g.zLogin); |
| 913 | } |
| 914 | |
| 915 | if( g.argc<3 ){ |
| 916 | usage("add|fieldlist|set|show"); |
| 917 | }else{ |
| 918 | n = strlen(g.argv[2]); |
| 919 | if( n==1 && g.argv[2][0]=='s' ){ |
| 920 | /* set/show cannot be distinguished, so show the usage */ |
| 921 | usage("add|fieldlist|set|show"); |
| 922 | }else if( strncmp(g.argv[2],"list",n)==0 ){ |
| 923 | if( g.argc==3 ){ |
| 924 | usage("list fields|reports"); |
| 925 | }else{ |
| 926 | n = strlen(g.argv[3]); |
| 927 | if( !strncmp(g.argv[3],"fields",n) ){ |
| 928 | /* simply show all field names */ |
| 929 | int i; |
| 930 | |
| 931 | /* read all available ticket fields */ |
| 932 | getAllTicketFields(); |
| 933 | for(i=0; i<nField; i++){ |
| 934 | printf("%s\n",azField[i]); |
| 935 | } |
| 936 | }else if( !strncmp(g.argv[3],"reports",n) ){ |
| 937 | rpt_list_reports(); |
| 938 | }else{ |
| 939 | fossil_fatal("unknown ticket list option '%s'!",g.argv[3]); |
| 940 | } |
| 941 | } |
| 942 | }else{ |
| 943 | /* add a new ticket or set fields on existing tickets */ |
| 944 | tTktShowEncoding tktEncoding; |
| 945 | |
| 946 | tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; |
| 947 | |
| 948 | if( strncmp(g.argv[2],"show",n)==0 ){ |
| 949 | if( g.argc==3 ){ |
| 950 | usage("show REPORTNR"); |
| 951 | }else{ |
| 952 | const char *zRep = 0; |
| 953 | const char *zSep = 0; |
| 954 | const char *zFilterUuid = 0; |
| 955 | |
| 956 | zSep = find_option("limit","l",1); |
| 957 | zRep = g.argv[3]; |
| 958 | if( !strcmp(zRep,"0") ){ |
| 959 | zRep = 0; |
| 960 | } |
| 961 | if( g.argc>4 ){ |
| 962 | zFilterUuid = g.argv[4]; |
| 963 | } |
| 964 | |
| 965 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); |
| 966 | |
| 967 | } |
| 968 | }else{ |
| 969 | /* add a new ticket or update an existing ticket */ |
| 970 | enum { set,add,err } eCmd = err; |
| 971 | int i = 0; |
| 972 | int rid; |
| 973 | const char *zTktUuid = 0; |
| 974 | Blob tktchng, cksum; |
| 975 | |
| 976 | /* get command type (set/add) and get uuid, if needed for set */ |
| 977 | if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){ |
| 978 | eCmd = set; |
| 979 | if( g.argc==3 ){ |
| 980 | usage("set TICKETUUID"); |
| 981 | } |
| 982 | zTktUuid = db_text(0, |
| 983 | "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3] |
| 984 | ); |
| 985 | if( !zTktUuid ){ |
| 986 | fossil_fatal("unknown ticket: '%s'!",g.argv[3]); |
| 987 | } |
| 988 | i=4; |
| 989 | }else if( strncmp(g.argv[2],"add",n)==0 ){ |
| 990 | eCmd = add; |
| 991 | i = 3; |
| 992 | zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 993 | } |
| 994 | /* none of set/add, so show the usage! */ |
| 995 | if( eCmd==err ){ |
| 996 | usage("add|fieldlist|set|show"); |
| 997 | } |
| 998 | |
| 999 | /* read all given ticket field/value pairs from command line */ |
| 1000 | if( i==g.argc ){ |
| 1001 | fossil_fatal("empty %s command aborted!",g.argv[2]); |
| 1002 | } |
| 1003 | getAllTicketFields(); |
| 1004 | /* read commandline and assign fields in the azValue array */ |
| 1005 | while( i<g.argc ){ |
| 1006 | char *zFName; |
| 1007 | char *zFValue; |
| 1008 | int j; |
| 1009 | |
| 1010 | zFName = g.argv[i++]; |
| 1011 | if( i==g.argc ){ |
| 1012 | fossil_fatal("missing value for '%s'!",zFName); |
| 1013 | } |
| 1014 | zFValue = g.argv[i++]; |
| 1015 | j = fieldId(zFName); |
| 1016 | if( tktEncoding == tktFossilize ){ |
| 1017 | zFValue=mprintf("%s",zFValue); |
| 1018 | defossilize(zFValue); |
| 1019 | } |
| 1020 | if( j == -1 ){ |
| 1021 | fossil_fatal("unknown field name '%s'!",zFName); |
| 1022 | }else{ |
| 1023 | azValue[j] = zFValue; |
| 1024 | } |
| 1025 | } |
| 1026 | |
| 1027 | /* now add the needed artifacts to the repository */ |
| 1028 | blob_zero(&tktchng); |
| 1029 | { /* add the time to the ticket manifest */ |
| 1030 | char *zDate; |
| 1031 | |
| 1032 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1033 | zDate[10] = 'T'; |
| 1034 | blob_appendf(&tktchng, "D %s\n", zDate); |
| 1035 | free(zDate); |
| 1036 | } |
| 1037 | /* append defined elements */ |
| 1038 | for(i=0; i<nField; i++){ |
| 1039 | char *zValue; |
| 1040 | |
| 1041 | zValue = azValue[i]; |
| 1042 | if( azValue[i] && azValue[i][0] ){ |
| 1043 | if( strncmp(azField[i], "private_", 8)==0 ){ |
| 1044 | zValue = db_conceal(zValue, strlen(zValue)); |
| 1045 | blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); |
| 1046 | }else{ |
| 1047 | blob_appendf(&tktchng, "J %s %#F\n", |
| 1048 | azField[i], strlen(zValue), zValue); |
| 1049 | } |
| 1050 | if( tktEncoding == tktFossilize ){ |
| 1051 | free(azValue[i]); |
| 1052 | } |
| 1053 | } |
| 1054 | } |
| 1055 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1056 | blob_appendf(&tktchng, "U %F\n", g.zLogin); |
| 1057 | md5sum_blob(&tktchng, &cksum); |
| 1058 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1059 | rid = content_put(&tktchng, 0, 0); |
| 1060 | if( rid==0 ){ |
| 1061 | fossil_panic("trouble committing ticket: %s", g.zErrMsg); |
| 1062 | } |
| 1063 | manifest_crosslink_begin(); |
| 1064 | manifest_crosslink(rid, &tktchng); |
| 1065 | manifest_crosslink_end(); |
| 1066 | printf("ticket %s succeeded for UID %s\n", |
| 1067 | (eCmd==set?"set":"add"),zTktUuid); |
| 1068 | } |
| 1069 | } |
| 1070 | } |
| 1071 | } |
| 1072 |
+14
-11
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -279,10 +279,11 @@ | ||
| 279 | 279 | db_end_transaction(1); /* With --nochange, rollback changes */ |
| 280 | 280 | }else{ |
| 281 | 281 | if( g.argc<=3 ){ |
| 282 | 282 | /* All files updated. Shift the current checkout to the target. */ |
| 283 | 283 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); |
| 284 | + checkout_set_all_exe(vid); | |
| 284 | 285 | manifest_to_disk(tid); |
| 285 | 286 | db_lset_int("checkout", tid); |
| 286 | 287 | }else{ |
| 287 | 288 | /* A subset of files have been checked out. Keep the current |
| 288 | 289 | ** checkout unchanged. */ |
| @@ -302,13 +303,13 @@ | ||
| 302 | 303 | const char *revision, /* The checkin containing the file */ |
| 303 | 304 | const char *file, /* Full treename of the file */ |
| 304 | 305 | Blob *content, /* Put the content here */ |
| 305 | 306 | int errCode /* Error code if file not found. Panic if 0. */ |
| 306 | 307 | ){ |
| 307 | - Blob mfile; | |
| 308 | - Manifest m; | |
| 309 | - int i, rid=0; | |
| 308 | + Manifest *pManifest; | |
| 309 | + ManifestFile *pFile; | |
| 310 | + int rid=0; | |
| 310 | 311 | |
| 311 | 312 | if( revision ){ |
| 312 | 313 | rid = name_to_rid(revision); |
| 313 | 314 | }else{ |
| 314 | 315 | rid = db_lget_int("checkout", 0); |
| @@ -315,21 +316,22 @@ | ||
| 315 | 316 | } |
| 316 | 317 | if( !is_a_version(rid) ){ |
| 317 | 318 | if( errCode>0 ) return errCode; |
| 318 | 319 | fossil_fatal("no such checkin: %s", revision); |
| 319 | 320 | } |
| 320 | - content_get(rid, &mfile); | |
| 321 | + pManifest = manifest_get(rid, CFTYPE_MANIFEST); | |
| 321 | 322 | |
| 322 | - if( manifest_parse(&m, &mfile) ){ | |
| 323 | - for(i=0; i<m.nFile; i++){ | |
| 324 | - if( strcmp(m.aFile[i].zName, file)==0 ){ | |
| 325 | - rid = uuid_to_rid(m.aFile[i].zUuid, 0); | |
| 326 | - manifest_clear(&m); | |
| 323 | + if( pManifest ){ | |
| 324 | + manifest_file_rewind(pManifest); | |
| 325 | + while( (pFile = manifest_file_next(pManifest,0))!=0 ){ | |
| 326 | + if( strcmp(pFile->zName, file)==0 ){ | |
| 327 | + rid = uuid_to_rid(pFile->zUuid, 0); | |
| 328 | + manifest_destroy(pManifest); | |
| 327 | 329 | return content_get(rid, content); |
| 328 | 330 | } |
| 329 | 331 | } |
| 330 | - manifest_clear(&m); | |
| 332 | + manifest_destroy(pManifest); | |
| 331 | 333 | if( errCode<=0 ){ |
| 332 | 334 | fossil_fatal("file %s does not exist in checkin: %s", file, revision); |
| 333 | 335 | } |
| 334 | 336 | }else if( errCode<=0 ){ |
| 335 | 337 | fossil_panic("could not parse manifest for checkin: %s", revision); |
| @@ -386,14 +388,15 @@ | ||
| 386 | 388 | }else{ |
| 387 | 389 | int vid; |
| 388 | 390 | vid = db_lget_int("checkout", 0); |
| 389 | 391 | vfile_check_signature(vid, 0); |
| 390 | 392 | db_multi_exec( |
| 393 | + "DELETE FROM vmerge;" | |
| 391 | 394 | "INSERT INTO torevert " |
| 392 | 395 | "SELECT pathname" |
| 393 | 396 | " FROM vfile " |
| 394 | - " WHERE chnged OR deleted OR rid=0 OR pathname!=origname" | |
| 397 | + " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" | |
| 395 | 398 | ); |
| 396 | 399 | } |
| 397 | 400 | blob_zero(&record); |
| 398 | 401 | db_prepare(&q, "SELECT name FROM torevert"); |
| 399 | 402 | while( db_step(&q)==SQLITE_ROW ){ |
| 400 | 403 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -279,10 +279,11 @@ | |
| 279 | db_end_transaction(1); /* With --nochange, rollback changes */ |
| 280 | }else{ |
| 281 | if( g.argc<=3 ){ |
| 282 | /* All files updated. Shift the current checkout to the target. */ |
| 283 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); |
| 284 | manifest_to_disk(tid); |
| 285 | db_lset_int("checkout", tid); |
| 286 | }else{ |
| 287 | /* A subset of files have been checked out. Keep the current |
| 288 | ** checkout unchanged. */ |
| @@ -302,13 +303,13 @@ | |
| 302 | const char *revision, /* The checkin containing the file */ |
| 303 | const char *file, /* Full treename of the file */ |
| 304 | Blob *content, /* Put the content here */ |
| 305 | int errCode /* Error code if file not found. Panic if 0. */ |
| 306 | ){ |
| 307 | Blob mfile; |
| 308 | Manifest m; |
| 309 | int i, rid=0; |
| 310 | |
| 311 | if( revision ){ |
| 312 | rid = name_to_rid(revision); |
| 313 | }else{ |
| 314 | rid = db_lget_int("checkout", 0); |
| @@ -315,21 +316,22 @@ | |
| 315 | } |
| 316 | if( !is_a_version(rid) ){ |
| 317 | if( errCode>0 ) return errCode; |
| 318 | fossil_fatal("no such checkin: %s", revision); |
| 319 | } |
| 320 | content_get(rid, &mfile); |
| 321 | |
| 322 | if( manifest_parse(&m, &mfile) ){ |
| 323 | for(i=0; i<m.nFile; i++){ |
| 324 | if( strcmp(m.aFile[i].zName, file)==0 ){ |
| 325 | rid = uuid_to_rid(m.aFile[i].zUuid, 0); |
| 326 | manifest_clear(&m); |
| 327 | return content_get(rid, content); |
| 328 | } |
| 329 | } |
| 330 | manifest_clear(&m); |
| 331 | if( errCode<=0 ){ |
| 332 | fossil_fatal("file %s does not exist in checkin: %s", file, revision); |
| 333 | } |
| 334 | }else if( errCode<=0 ){ |
| 335 | fossil_panic("could not parse manifest for checkin: %s", revision); |
| @@ -386,14 +388,15 @@ | |
| 386 | }else{ |
| 387 | int vid; |
| 388 | vid = db_lget_int("checkout", 0); |
| 389 | vfile_check_signature(vid, 0); |
| 390 | db_multi_exec( |
| 391 | "INSERT INTO torevert " |
| 392 | "SELECT pathname" |
| 393 | " FROM vfile " |
| 394 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname" |
| 395 | ); |
| 396 | } |
| 397 | blob_zero(&record); |
| 398 | db_prepare(&q, "SELECT name FROM torevert"); |
| 399 | while( db_step(&q)==SQLITE_ROW ){ |
| 400 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -279,10 +279,11 @@ | |
| 279 | db_end_transaction(1); /* With --nochange, rollback changes */ |
| 280 | }else{ |
| 281 | if( g.argc<=3 ){ |
| 282 | /* All files updated. Shift the current checkout to the target. */ |
| 283 | db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); |
| 284 | checkout_set_all_exe(vid); |
| 285 | manifest_to_disk(tid); |
| 286 | db_lset_int("checkout", tid); |
| 287 | }else{ |
| 288 | /* A subset of files have been checked out. Keep the current |
| 289 | ** checkout unchanged. */ |
| @@ -302,13 +303,13 @@ | |
| 303 | const char *revision, /* The checkin containing the file */ |
| 304 | const char *file, /* Full treename of the file */ |
| 305 | Blob *content, /* Put the content here */ |
| 306 | int errCode /* Error code if file not found. Panic if 0. */ |
| 307 | ){ |
| 308 | Manifest *pManifest; |
| 309 | ManifestFile *pFile; |
| 310 | int rid=0; |
| 311 | |
| 312 | if( revision ){ |
| 313 | rid = name_to_rid(revision); |
| 314 | }else{ |
| 315 | rid = db_lget_int("checkout", 0); |
| @@ -315,21 +316,22 @@ | |
| 316 | } |
| 317 | if( !is_a_version(rid) ){ |
| 318 | if( errCode>0 ) return errCode; |
| 319 | fossil_fatal("no such checkin: %s", revision); |
| 320 | } |
| 321 | pManifest = manifest_get(rid, CFTYPE_MANIFEST); |
| 322 | |
| 323 | if( pManifest ){ |
| 324 | manifest_file_rewind(pManifest); |
| 325 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 326 | if( strcmp(pFile->zName, file)==0 ){ |
| 327 | rid = uuid_to_rid(pFile->zUuid, 0); |
| 328 | manifest_destroy(pManifest); |
| 329 | return content_get(rid, content); |
| 330 | } |
| 331 | } |
| 332 | manifest_destroy(pManifest); |
| 333 | if( errCode<=0 ){ |
| 334 | fossil_fatal("file %s does not exist in checkin: %s", file, revision); |
| 335 | } |
| 336 | }else if( errCode<=0 ){ |
| 337 | fossil_panic("could not parse manifest for checkin: %s", revision); |
| @@ -386,14 +388,15 @@ | |
| 388 | }else{ |
| 389 | int vid; |
| 390 | vid = db_lget_int("checkout", 0); |
| 391 | vfile_check_signature(vid, 0); |
| 392 | db_multi_exec( |
| 393 | "DELETE FROM vmerge;" |
| 394 | "INSERT INTO torevert " |
| 395 | "SELECT pathname" |
| 396 | " FROM vfile " |
| 397 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" |
| 398 | ); |
| 399 | } |
| 400 | blob_zero(&record); |
| 401 | db_prepare(&q, "SELECT name FROM torevert"); |
| 402 | while( db_step(&q)==SQLITE_ROW ){ |
| 403 |
+2
-2
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -23,11 +23,11 @@ | ||
| 23 | 23 | /* |
| 24 | 24 | ** Convert a string to lower-case. |
| 25 | 25 | */ |
| 26 | 26 | static void url_tolower(char *z){ |
| 27 | 27 | while( *z ){ |
| 28 | - *z = tolower(*z); | |
| 28 | + *z = fossil_tolower(*z); | |
| 29 | 29 | z++; |
| 30 | 30 | } |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | 33 | /* |
| @@ -109,11 +109,11 @@ | ||
| 109 | 109 | } |
| 110 | 110 | url_tolower(g.urlName); |
| 111 | 111 | if( c==':' ){ |
| 112 | 112 | g.urlPort = 0; |
| 113 | 113 | i++; |
| 114 | - while( (c = zUrl[i])!=0 && isdigit(c) ){ | |
| 114 | + while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ | |
| 115 | 115 | g.urlPort = g.urlPort*10 + c - '0'; |
| 116 | 116 | i++; |
| 117 | 117 | } |
| 118 | 118 | g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 119 | 119 | }else{ |
| 120 | 120 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | /* |
| 24 | ** Convert a string to lower-case. |
| 25 | */ |
| 26 | static void url_tolower(char *z){ |
| 27 | while( *z ){ |
| 28 | *z = tolower(*z); |
| 29 | z++; |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | /* |
| @@ -109,11 +109,11 @@ | |
| 109 | } |
| 110 | url_tolower(g.urlName); |
| 111 | if( c==':' ){ |
| 112 | g.urlPort = 0; |
| 113 | i++; |
| 114 | while( (c = zUrl[i])!=0 && isdigit(c) ){ |
| 115 | g.urlPort = g.urlPort*10 + c - '0'; |
| 116 | i++; |
| 117 | } |
| 118 | g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 119 | }else{ |
| 120 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -23,11 +23,11 @@ | |
| 23 | /* |
| 24 | ** Convert a string to lower-case. |
| 25 | */ |
| 26 | static void url_tolower(char *z){ |
| 27 | while( *z ){ |
| 28 | *z = fossil_tolower(*z); |
| 29 | z++; |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | /* |
| @@ -109,11 +109,11 @@ | |
| 109 | } |
| 110 | url_tolower(g.urlName); |
| 111 | if( c==':' ){ |
| 112 | g.urlPort = 0; |
| 113 | i++; |
| 114 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 115 | g.urlPort = g.urlPort*10 + c - '0'; |
| 116 | i++; |
| 117 | } |
| 118 | g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 119 | }else{ |
| 120 |
+2
-2
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -27,14 +27,14 @@ | ||
| 27 | 27 | ** onto the end of a blob. |
| 28 | 28 | */ |
| 29 | 29 | static void strip_string(Blob *pBlob, char *z){ |
| 30 | 30 | int i; |
| 31 | 31 | blob_reset(pBlob); |
| 32 | - while( isspace(*z) ){ z++; } | |
| 32 | + while( fossil_isspace(*z) ){ z++; } | |
| 33 | 33 | for(i=0; z[i]; i++){ |
| 34 | 34 | if( z[i]=='\r' || z[i]=='\n' ){ |
| 35 | - while( i>0 && isspace(z[i-1]) ){ i--; } | |
| 35 | + while( i>0 && fossil_isspace(z[i-1]) ){ i--; } | |
| 36 | 36 | z[i] = 0; |
| 37 | 37 | break; |
| 38 | 38 | } |
| 39 | 39 | if( z[i]<' ' ) z[i] = ' '; |
| 40 | 40 | } |
| 41 | 41 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -27,14 +27,14 @@ | |
| 27 | ** onto the end of a blob. |
| 28 | */ |
| 29 | static void strip_string(Blob *pBlob, char *z){ |
| 30 | int i; |
| 31 | blob_reset(pBlob); |
| 32 | while( isspace(*z) ){ z++; } |
| 33 | for(i=0; z[i]; i++){ |
| 34 | if( z[i]=='\r' || z[i]=='\n' ){ |
| 35 | while( i>0 && isspace(z[i-1]) ){ i--; } |
| 36 | z[i] = 0; |
| 37 | break; |
| 38 | } |
| 39 | if( z[i]<' ' ) z[i] = ' '; |
| 40 | } |
| 41 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -27,14 +27,14 @@ | |
| 27 | ** onto the end of a blob. |
| 28 | */ |
| 29 | static void strip_string(Blob *pBlob, char *z){ |
| 30 | int i; |
| 31 | blob_reset(pBlob); |
| 32 | while( fossil_isspace(*z) ){ z++; } |
| 33 | for(i=0; z[i]; i++){ |
| 34 | if( z[i]=='\r' || z[i]=='\n' ){ |
| 35 | while( i>0 && fossil_isspace(z[i-1]) ){ i--; } |
| 36 | z[i] = 0; |
| 37 | break; |
| 38 | } |
| 39 | if( z[i]<' ' ) z[i] = ' '; |
| 40 | } |
| 41 |
+31
-46
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -85,57 +85,38 @@ | ||
| 85 | 85 | } |
| 86 | 86 | } |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | /* |
| 90 | -** Build a catalog of all files in a baseline. | |
| 91 | -** We scan the baseline file for lines of the form: | |
| 92 | -** | |
| 93 | -** F NAME UUID | |
| 94 | -** | |
| 95 | -** Each such line makes an entry in the VFILE table. | |
| 90 | +** Build a catalog of all files in a checkin. | |
| 96 | 91 | */ |
| 97 | -void vfile_build(int vid, Blob *p){ | |
| 92 | +void vfile_build(int vid){ | |
| 98 | 93 | int rid; |
| 99 | - char *zName, *zUuid; | |
| 100 | 94 | Stmt ins; |
| 101 | - Blob line, token, name, uuid; | |
| 102 | - int seenHeader = 0; | |
| 95 | + Manifest *p; | |
| 96 | + ManifestFile *pFile; | |
| 97 | + | |
| 103 | 98 | db_begin_transaction(); |
| 104 | 99 | vfile_verify_not_phantom(vid, 0, 0); |
| 100 | + p = manifest_get(vid, CFTYPE_MANIFEST); | |
| 101 | + if( p==0 ) return; | |
| 105 | 102 | db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); |
| 106 | 103 | db_prepare(&ins, |
| 107 | 104 | "INSERT INTO vfile(vid,rid,mrid,pathname) " |
| 108 | 105 | " VALUES(:vid,:id,:id,:name)"); |
| 109 | 106 | db_bind_int(&ins, ":vid", vid); |
| 110 | - while( blob_line(p, &line) ){ | |
| 111 | - char *z = blob_buffer(&line); | |
| 112 | - if( z[0]=='-' ){ | |
| 113 | - if( seenHeader ) break; | |
| 114 | - while( blob_line(p, &line)>2 ){} | |
| 115 | - if( blob_line(p, &line)==0 ) break; | |
| 116 | - } | |
| 117 | - seenHeader = 1; | |
| 118 | - if( z[0]!='F' || z[1]!=' ' ) continue; | |
| 119 | - blob_token(&line, &token); /* Skip the "F" token */ | |
| 120 | - if( blob_token(&line, &name)==0 ) break; | |
| 121 | - if( blob_token(&line, &uuid)==0 ) break; | |
| 122 | - zName = blob_str(&name); | |
| 123 | - defossilize(zName); | |
| 124 | - zUuid = blob_str(&uuid); | |
| 125 | - rid = uuid_to_rid(zUuid, 0); | |
| 126 | - vfile_verify_not_phantom(rid, zName, zUuid); | |
| 127 | - if( rid>0 && file_is_simple_pathname(zName) ){ | |
| 128 | - db_bind_int(&ins, ":id", rid); | |
| 129 | - db_bind_text(&ins, ":name", zName); | |
| 130 | - db_step(&ins); | |
| 131 | - db_reset(&ins); | |
| 132 | - } | |
| 133 | - blob_reset(&name); | |
| 134 | - blob_reset(&uuid); | |
| 107 | + manifest_file_rewind(p); | |
| 108 | + while( (pFile = manifest_file_next(p,0))!=0 ){ | |
| 109 | + rid = uuid_to_rid(pFile->zUuid, 0); | |
| 110 | + vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid); | |
| 111 | + db_bind_int(&ins, ":id", rid); | |
| 112 | + db_bind_text(&ins, ":name", pFile->zName); | |
| 113 | + db_step(&ins); | |
| 114 | + db_reset(&ins); | |
| 135 | 115 | } |
| 136 | 116 | db_finalize(&ins); |
| 117 | + manifest_destroy(p); | |
| 137 | 118 | db_end_transaction(0); |
| 138 | 119 | } |
| 139 | 120 | |
| 140 | 121 | /* |
| 141 | 122 | ** Check the file signature of the disk image for every VFILE of vid. |
| @@ -369,10 +350,11 @@ | ||
| 369 | 350 | } |
| 370 | 351 | fseek(in, 0L, SEEK_END); |
| 371 | 352 | sprintf(zBuf, " %ld\n", ftell(in)); |
| 372 | 353 | fseek(in, 0L, SEEK_SET); |
| 373 | 354 | md5sum_step_text(zBuf, -1); |
| 355 | + /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ | |
| 374 | 356 | for(;;){ |
| 375 | 357 | int n; |
| 376 | 358 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 377 | 359 | if( n<=0 ) break; |
| 378 | 360 | md5sum_step_text(zBuf, n); |
| @@ -422,10 +404,11 @@ | ||
| 422 | 404 | int rid = db_column_int(&q, 1); |
| 423 | 405 | md5sum_step_text(zName, -1); |
| 424 | 406 | content_get(rid, &file); |
| 425 | 407 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 426 | 408 | md5sum_step_text(zBuf, -1); |
| 409 | + /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ | |
| 427 | 410 | md5sum_step_blob(&file); |
| 428 | 411 | blob_reset(&file); |
| 429 | 412 | } |
| 430 | 413 | db_finalize(&q); |
| 431 | 414 | md5sum_finish(pOut); |
| @@ -438,41 +421,43 @@ | ||
| 438 | 421 | ** |
| 439 | 422 | ** If pManOut is not NULL then fill it with the checksum found in the |
| 440 | 423 | ** "R" card near the end of the manifest. |
| 441 | 424 | */ |
| 442 | 425 | void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ |
| 443 | - int i, fid; | |
| 444 | - Blob file, mfile; | |
| 445 | - Manifest m; | |
| 426 | + int fid; | |
| 427 | + Blob file; | |
| 428 | + Manifest *pManifest; | |
| 429 | + ManifestFile *pFile; | |
| 446 | 430 | char zBuf[100]; |
| 447 | 431 | |
| 448 | 432 | blob_zero(pOut); |
| 449 | 433 | if( pManOut ){ |
| 450 | 434 | blob_zero(pManOut); |
| 451 | 435 | } |
| 452 | 436 | db_must_be_within_tree(); |
| 453 | - content_get(vid, &mfile); | |
| 454 | - if( manifest_parse(&m, &mfile)==0 ){ | |
| 437 | + pManifest = manifest_get(vid, CFTYPE_MANIFEST); | |
| 438 | + if( pManifest==0 ){ | |
| 455 | 439 | fossil_panic("manifest file (%d) is malformed", vid); |
| 456 | 440 | } |
| 457 | - for(i=0; i<m.nFile; i++){ | |
| 458 | - fid = uuid_to_rid(m.aFile[i].zUuid, 0); | |
| 459 | - md5sum_step_text(m.aFile[i].zName, -1); | |
| 441 | + manifest_file_rewind(pManifest); | |
| 442 | + while( (pFile = manifest_file_next(pManifest,0))!=0 ){ | |
| 443 | + fid = uuid_to_rid(pFile->zUuid, 0); | |
| 444 | + md5sum_step_text(pFile->zName, -1); | |
| 460 | 445 | content_get(fid, &file); |
| 461 | 446 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 462 | 447 | md5sum_step_text(zBuf, -1); |
| 463 | 448 | md5sum_step_blob(&file); |
| 464 | 449 | blob_reset(&file); |
| 465 | 450 | } |
| 466 | 451 | if( pManOut ){ |
| 467 | - if( m.zRepoCksum ){ | |
| 468 | - blob_append(pManOut, m.zRepoCksum, -1); | |
| 452 | + if( pManifest->zRepoCksum ){ | |
| 453 | + blob_append(pManOut, pManifest->zRepoCksum, -1); | |
| 469 | 454 | }else{ |
| 470 | 455 | blob_zero(pManOut); |
| 471 | 456 | } |
| 472 | 457 | } |
| 473 | - manifest_clear(&m); | |
| 458 | + manifest_destroy(pManifest); | |
| 474 | 459 | md5sum_finish(pOut); |
| 475 | 460 | } |
| 476 | 461 | |
| 477 | 462 | /* |
| 478 | 463 | ** COMMAND: test-agg-cksum |
| 479 | 464 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -85,57 +85,38 @@ | |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | ** Build a catalog of all files in a baseline. |
| 91 | ** We scan the baseline file for lines of the form: |
| 92 | ** |
| 93 | ** F NAME UUID |
| 94 | ** |
| 95 | ** Each such line makes an entry in the VFILE table. |
| 96 | */ |
| 97 | void vfile_build(int vid, Blob *p){ |
| 98 | int rid; |
| 99 | char *zName, *zUuid; |
| 100 | Stmt ins; |
| 101 | Blob line, token, name, uuid; |
| 102 | int seenHeader = 0; |
| 103 | db_begin_transaction(); |
| 104 | vfile_verify_not_phantom(vid, 0, 0); |
| 105 | db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); |
| 106 | db_prepare(&ins, |
| 107 | "INSERT INTO vfile(vid,rid,mrid,pathname) " |
| 108 | " VALUES(:vid,:id,:id,:name)"); |
| 109 | db_bind_int(&ins, ":vid", vid); |
| 110 | while( blob_line(p, &line) ){ |
| 111 | char *z = blob_buffer(&line); |
| 112 | if( z[0]=='-' ){ |
| 113 | if( seenHeader ) break; |
| 114 | while( blob_line(p, &line)>2 ){} |
| 115 | if( blob_line(p, &line)==0 ) break; |
| 116 | } |
| 117 | seenHeader = 1; |
| 118 | if( z[0]!='F' || z[1]!=' ' ) continue; |
| 119 | blob_token(&line, &token); /* Skip the "F" token */ |
| 120 | if( blob_token(&line, &name)==0 ) break; |
| 121 | if( blob_token(&line, &uuid)==0 ) break; |
| 122 | zName = blob_str(&name); |
| 123 | defossilize(zName); |
| 124 | zUuid = blob_str(&uuid); |
| 125 | rid = uuid_to_rid(zUuid, 0); |
| 126 | vfile_verify_not_phantom(rid, zName, zUuid); |
| 127 | if( rid>0 && file_is_simple_pathname(zName) ){ |
| 128 | db_bind_int(&ins, ":id", rid); |
| 129 | db_bind_text(&ins, ":name", zName); |
| 130 | db_step(&ins); |
| 131 | db_reset(&ins); |
| 132 | } |
| 133 | blob_reset(&name); |
| 134 | blob_reset(&uuid); |
| 135 | } |
| 136 | db_finalize(&ins); |
| 137 | db_end_transaction(0); |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | ** Check the file signature of the disk image for every VFILE of vid. |
| @@ -369,10 +350,11 @@ | |
| 369 | } |
| 370 | fseek(in, 0L, SEEK_END); |
| 371 | sprintf(zBuf, " %ld\n", ftell(in)); |
| 372 | fseek(in, 0L, SEEK_SET); |
| 373 | md5sum_step_text(zBuf, -1); |
| 374 | for(;;){ |
| 375 | int n; |
| 376 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 377 | if( n<=0 ) break; |
| 378 | md5sum_step_text(zBuf, n); |
| @@ -422,10 +404,11 @@ | |
| 422 | int rid = db_column_int(&q, 1); |
| 423 | md5sum_step_text(zName, -1); |
| 424 | content_get(rid, &file); |
| 425 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 426 | md5sum_step_text(zBuf, -1); |
| 427 | md5sum_step_blob(&file); |
| 428 | blob_reset(&file); |
| 429 | } |
| 430 | db_finalize(&q); |
| 431 | md5sum_finish(pOut); |
| @@ -438,41 +421,43 @@ | |
| 438 | ** |
| 439 | ** If pManOut is not NULL then fill it with the checksum found in the |
| 440 | ** "R" card near the end of the manifest. |
| 441 | */ |
| 442 | void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ |
| 443 | int i, fid; |
| 444 | Blob file, mfile; |
| 445 | Manifest m; |
| 446 | char zBuf[100]; |
| 447 | |
| 448 | blob_zero(pOut); |
| 449 | if( pManOut ){ |
| 450 | blob_zero(pManOut); |
| 451 | } |
| 452 | db_must_be_within_tree(); |
| 453 | content_get(vid, &mfile); |
| 454 | if( manifest_parse(&m, &mfile)==0 ){ |
| 455 | fossil_panic("manifest file (%d) is malformed", vid); |
| 456 | } |
| 457 | for(i=0; i<m.nFile; i++){ |
| 458 | fid = uuid_to_rid(m.aFile[i].zUuid, 0); |
| 459 | md5sum_step_text(m.aFile[i].zName, -1); |
| 460 | content_get(fid, &file); |
| 461 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 462 | md5sum_step_text(zBuf, -1); |
| 463 | md5sum_step_blob(&file); |
| 464 | blob_reset(&file); |
| 465 | } |
| 466 | if( pManOut ){ |
| 467 | if( m.zRepoCksum ){ |
| 468 | blob_append(pManOut, m.zRepoCksum, -1); |
| 469 | }else{ |
| 470 | blob_zero(pManOut); |
| 471 | } |
| 472 | } |
| 473 | manifest_clear(&m); |
| 474 | md5sum_finish(pOut); |
| 475 | } |
| 476 | |
| 477 | /* |
| 478 | ** COMMAND: test-agg-cksum |
| 479 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -85,57 +85,38 @@ | |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | ** Build a catalog of all files in a checkin. |
| 91 | */ |
| 92 | void vfile_build(int vid){ |
| 93 | int rid; |
| 94 | Stmt ins; |
| 95 | Manifest *p; |
| 96 | ManifestFile *pFile; |
| 97 | |
| 98 | db_begin_transaction(); |
| 99 | vfile_verify_not_phantom(vid, 0, 0); |
| 100 | p = manifest_get(vid, CFTYPE_MANIFEST); |
| 101 | if( p==0 ) return; |
| 102 | db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); |
| 103 | db_prepare(&ins, |
| 104 | "INSERT INTO vfile(vid,rid,mrid,pathname) " |
| 105 | " VALUES(:vid,:id,:id,:name)"); |
| 106 | db_bind_int(&ins, ":vid", vid); |
| 107 | manifest_file_rewind(p); |
| 108 | while( (pFile = manifest_file_next(p,0))!=0 ){ |
| 109 | rid = uuid_to_rid(pFile->zUuid, 0); |
| 110 | vfile_verify_not_phantom(rid, pFile->zName, pFile->zUuid); |
| 111 | db_bind_int(&ins, ":id", rid); |
| 112 | db_bind_text(&ins, ":name", pFile->zName); |
| 113 | db_step(&ins); |
| 114 | db_reset(&ins); |
| 115 | } |
| 116 | db_finalize(&ins); |
| 117 | manifest_destroy(p); |
| 118 | db_end_transaction(0); |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 | ** Check the file signature of the disk image for every VFILE of vid. |
| @@ -369,10 +350,11 @@ | |
| 350 | } |
| 351 | fseek(in, 0L, SEEK_END); |
| 352 | sprintf(zBuf, " %ld\n", ftell(in)); |
| 353 | fseek(in, 0L, SEEK_SET); |
| 354 | md5sum_step_text(zBuf, -1); |
| 355 | /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ |
| 356 | for(;;){ |
| 357 | int n; |
| 358 | n = fread(zBuf, 1, sizeof(zBuf), in); |
| 359 | if( n<=0 ) break; |
| 360 | md5sum_step_text(zBuf, n); |
| @@ -422,10 +404,11 @@ | |
| 404 | int rid = db_column_int(&q, 1); |
| 405 | md5sum_step_text(zName, -1); |
| 406 | content_get(rid, &file); |
| 407 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 408 | md5sum_step_text(zBuf, -1); |
| 409 | /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/ |
| 410 | md5sum_step_blob(&file); |
| 411 | blob_reset(&file); |
| 412 | } |
| 413 | db_finalize(&q); |
| 414 | md5sum_finish(pOut); |
| @@ -438,41 +421,43 @@ | |
| 421 | ** |
| 422 | ** If pManOut is not NULL then fill it with the checksum found in the |
| 423 | ** "R" card near the end of the manifest. |
| 424 | */ |
| 425 | void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ |
| 426 | int fid; |
| 427 | Blob file; |
| 428 | Manifest *pManifest; |
| 429 | ManifestFile *pFile; |
| 430 | char zBuf[100]; |
| 431 | |
| 432 | blob_zero(pOut); |
| 433 | if( pManOut ){ |
| 434 | blob_zero(pManOut); |
| 435 | } |
| 436 | db_must_be_within_tree(); |
| 437 | pManifest = manifest_get(vid, CFTYPE_MANIFEST); |
| 438 | if( pManifest==0 ){ |
| 439 | fossil_panic("manifest file (%d) is malformed", vid); |
| 440 | } |
| 441 | manifest_file_rewind(pManifest); |
| 442 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 443 | fid = uuid_to_rid(pFile->zUuid, 0); |
| 444 | md5sum_step_text(pFile->zName, -1); |
| 445 | content_get(fid, &file); |
| 446 | sprintf(zBuf, " %d\n", blob_size(&file)); |
| 447 | md5sum_step_text(zBuf, -1); |
| 448 | md5sum_step_blob(&file); |
| 449 | blob_reset(&file); |
| 450 | } |
| 451 | if( pManOut ){ |
| 452 | if( pManifest->zRepoCksum ){ |
| 453 | blob_append(pManOut, pManifest->zRepoCksum, -1); |
| 454 | }else{ |
| 455 | blob_zero(pManOut); |
| 456 | } |
| 457 | } |
| 458 | manifest_destroy(pManifest); |
| 459 | md5sum_finish(pOut); |
| 460 | } |
| 461 | |
| 462 | /* |
| 463 | ** COMMAND: test-agg-cksum |
| 464 |
+34
-60
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -86,14 +86,14 @@ | ||
| 86 | 86 | if( !g.okRdWiki ){ |
| 87 | 87 | cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); |
| 88 | 88 | } |
| 89 | 89 | if( zIndexPage ){ |
| 90 | 90 | const char *zPathInfo = P("PATH_INFO"); |
| 91 | + while( zIndexPage[0]=='/' ) zIndexPage++; | |
| 91 | 92 | if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; |
| 92 | 93 | } |
| 93 | 94 | if( zIndexPage ){ |
| 94 | - while( zIndexPage[0]=='/' ) zIndexPage++; | |
| 95 | 95 | cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage); |
| 96 | 96 | } |
| 97 | 97 | if( zPageName ){ |
| 98 | 98 | login_check_credentials(); |
| 99 | 99 | g.zExtra = zPageName; |
| @@ -127,11 +127,11 @@ | ||
| 127 | 127 | void wiki_page(void){ |
| 128 | 128 | char *zTag; |
| 129 | 129 | int rid = 0; |
| 130 | 130 | int isSandbox; |
| 131 | 131 | Blob wiki; |
| 132 | - Manifest m; | |
| 132 | + Manifest *pWiki = 0; | |
| 133 | 133 | const char *zPageName; |
| 134 | 134 | char *zBody = mprintf("%s","<i>Empty Page</i>"); |
| 135 | 135 | Stmt q; |
| 136 | 136 | int cnt = 0; |
| 137 | 137 | |
| @@ -179,20 +179,14 @@ | ||
| 179 | 179 | "SELECT rid FROM tagxref" |
| 180 | 180 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 181 | 181 | " ORDER BY mtime DESC", zTag |
| 182 | 182 | ); |
| 183 | 183 | free(zTag); |
| 184 | - memset(&m, 0, sizeof(m)); | |
| 185 | - blob_zero(&m.content); | |
| 186 | - if( rid ){ | |
| 187 | - Blob content; | |
| 188 | - content_get(rid, &content); | |
| 189 | - manifest_parse(&m, &content); | |
| 190 | - if( m.type==CFTYPE_WIKI && m.zWiki ){ | |
| 191 | - while( isspace(m.zWiki[0]) ) m.zWiki++; | |
| 192 | - if( m.zWiki[0] ) zBody = m.zWiki; | |
| 193 | - } | |
| 184 | + pWiki = manifest_get(rid, CFTYPE_WIKI); | |
| 185 | + if( pWiki ){ | |
| 186 | + while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++; | |
| 187 | + if( pWiki->zWiki[0] ) zBody = pWiki->zWiki; | |
| 194 | 188 | } |
| 195 | 189 | } |
| 196 | 190 | if( !g.isHome ){ |
| 197 | 191 | if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){ |
| 198 | 192 | style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", |
| @@ -249,13 +243,11 @@ | ||
| 249 | 243 | if( cnt ){ |
| 250 | 244 | @ </ul> |
| 251 | 245 | } |
| 252 | 246 | db_finalize(&q); |
| 253 | 247 | |
| 254 | - if( !isSandbox ){ | |
| 255 | - manifest_clear(&m); | |
| 256 | - } | |
| 248 | + manifest_destroy(pWiki); | |
| 257 | 249 | style_footer(); |
| 258 | 250 | } |
| 259 | 251 | |
| 260 | 252 | /* |
| 261 | 253 | ** WEBPAGE: wikiedit |
| @@ -264,11 +256,11 @@ | ||
| 264 | 256 | void wikiedit_page(void){ |
| 265 | 257 | char *zTag; |
| 266 | 258 | int rid = 0; |
| 267 | 259 | int isSandbox; |
| 268 | 260 | Blob wiki; |
| 269 | - Manifest m; | |
| 261 | + Manifest *pWiki = 0; | |
| 270 | 262 | const char *zPageName; |
| 271 | 263 | char *zHtmlPageName; |
| 272 | 264 | int n; |
| 273 | 265 | const char *z; |
| 274 | 266 | char *zBody = (char*)P("w"); |
| @@ -298,19 +290,12 @@ | ||
| 298 | 290 | free(zTag); |
| 299 | 291 | if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ |
| 300 | 292 | login_needed(); |
| 301 | 293 | return; |
| 302 | 294 | } |
| 303 | - memset(&m, 0, sizeof(m)); | |
| 304 | - blob_zero(&m.content); | |
| 305 | - if( rid && zBody==0 ){ | |
| 306 | - Blob content; | |
| 307 | - content_get(rid, &content); | |
| 308 | - manifest_parse(&m, &content); | |
| 309 | - if( m.type==CFTYPE_WIKI ){ | |
| 310 | - zBody = m.zWiki; | |
| 311 | - } | |
| 295 | + if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ | |
| 296 | + zBody = pWiki->zWiki; | |
| 312 | 297 | } |
| 313 | 298 | } |
| 314 | 299 | if( P("submit")!=0 && zBody!=0 ){ |
| 315 | 300 | char *zDate; |
| 316 | 301 | Blob cksum; |
| @@ -377,13 +362,11 @@ | ||
| 377 | 362 | @ <br /> |
| 378 | 363 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 379 | 364 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 380 | 365 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 381 | 366 | @ </div></form> |
| 382 | - if( !isSandbox ){ | |
| 383 | - manifest_clear(&m); | |
| 384 | - } | |
| 367 | + manifest_destroy(pWiki); | |
| 385 | 368 | style_footer(); |
| 386 | 369 | } |
| 387 | 370 | |
| 388 | 371 | /* |
| 389 | 372 | ** WEBPAGE: wikinew |
| @@ -477,27 +460,25 @@ | ||
| 477 | 460 | if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){ |
| 478 | 461 | char *zDate; |
| 479 | 462 | Blob cksum; |
| 480 | 463 | int nrid; |
| 481 | 464 | Blob body; |
| 482 | - Blob content; | |
| 483 | 465 | Blob wiki; |
| 484 | - Manifest m; | |
| 466 | + Manifest *pWiki = 0; | |
| 485 | 467 | |
| 486 | 468 | blob_zero(&body); |
| 487 | 469 | if( isSandbox ){ |
| 488 | 470 | blob_appendf(&body, db_get("sandbox","")); |
| 489 | 471 | appendRemark(&body); |
| 490 | 472 | db_set("sandbox", blob_str(&body), 0); |
| 491 | 473 | }else{ |
| 492 | 474 | login_verify_csrf_secret(); |
| 493 | - content_get(rid, &content); | |
| 494 | - manifest_parse(&m, &content); | |
| 495 | - if( m.type==CFTYPE_WIKI ){ | |
| 496 | - blob_append(&body, m.zWiki, -1); | |
| 475 | + pWiki = manifest_get(rid, CFTYPE_WIKI); | |
| 476 | + if( pWiki ){ | |
| 477 | + blob_append(&body, pWiki->zWiki, -1); | |
| 478 | + manifest_destroy(pWiki); | |
| 497 | 479 | } |
| 498 | - manifest_clear(&m); | |
| 499 | 480 | blob_zero(&wiki); |
| 500 | 481 | db_begin_transaction(); |
| 501 | 482 | zDate = db_text(0, "SELECT datetime('now')"); |
| 502 | 483 | zDate[10] = 'T'; |
| 503 | 484 | blob_appendf(&wiki, "D %s\n", zDate); |
| @@ -612,12 +593,11 @@ | ||
| 612 | 593 | */ |
| 613 | 594 | void wdiff_page(void){ |
| 614 | 595 | char *zTitle; |
| 615 | 596 | int rid1, rid2; |
| 616 | 597 | const char *zPageName; |
| 617 | - Blob content1, content2; | |
| 618 | - Manifest m1, m2; | |
| 598 | + Manifest *pW1, *pW2 = 0; | |
| 619 | 599 | Blob w1, w2, d; |
| 620 | 600 | |
| 621 | 601 | login_check_credentials(); |
| 622 | 602 | rid1 = atoi(PD("a","0")); |
| 623 | 603 | if( !g.okHistory ){ login_needed(); return; } |
| @@ -635,27 +615,24 @@ | ||
| 635 | 615 | " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" |
| 636 | 616 | " ORDER BY event.mtime DESC LIMIT 1", |
| 637 | 617 | zPageName, rid1 |
| 638 | 618 | ); |
| 639 | 619 | } |
| 640 | - content_get(rid1, &content1); | |
| 641 | - manifest_parse(&m1, &content1); | |
| 642 | - if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home(); | |
| 643 | - blob_init(&w1, m1.zWiki, -1); | |
| 620 | + pW1 = manifest_get(rid1, CFTYPE_WIKI); | |
| 621 | + if( pW1==0 ) fossil_redirect_home(); | |
| 622 | + blob_init(&w1, pW1->zWiki, -1); | |
| 644 | 623 | blob_zero(&w2); |
| 645 | - if( rid2 ){ | |
| 646 | - content_get(rid2, &content2); | |
| 647 | - manifest_parse(&m2, &content2); | |
| 648 | - if( m2.type==CFTYPE_WIKI ){ | |
| 649 | - blob_init(&w2, m2.zWiki, -1); | |
| 650 | - } | |
| 624 | + if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){ | |
| 625 | + blob_init(&w2, pW2->zWiki, -1); | |
| 651 | 626 | } |
| 652 | 627 | blob_zero(&d); |
| 653 | 628 | text_diff(&w2, &w1, &d, 5, 1); |
| 654 | 629 | @ <pre> |
| 655 | 630 | @ %h(blob_str(&d)) |
| 656 | 631 | @ </pre> |
| 632 | + manifest_destroy(pW1); | |
| 633 | + manifest_destroy(pW2); | |
| 657 | 634 | style_footer(); |
| 658 | 635 | } |
| 659 | 636 | |
| 660 | 637 | /* |
| 661 | 638 | ** WEBPAGE: wcontent |
| @@ -914,35 +891,31 @@ | ||
| 914 | 891 | } |
| 915 | 892 | |
| 916 | 893 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 917 | 894 | char const *zPageName; /* Name of the wiki page to export */ |
| 918 | 895 | char const *zFile; /* Name of the output file (0=stdout) */ |
| 919 | - int rid; /* Artifact ID of the wiki page */ | |
| 920 | - int i; /* Loop counter */ | |
| 921 | - char *zBody = 0; /* Wiki page content */ | |
| 922 | - Manifest m; /* Parsed wiki page content */ | |
| 896 | + int rid; /* Artifact ID of the wiki page */ | |
| 897 | + int i; /* Loop counter */ | |
| 898 | + char *zBody = 0; /* Wiki page content */ | |
| 899 | + Manifest *pWiki = 0; /* Parsed wiki page content */ | |
| 900 | + | |
| 923 | 901 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 924 | 902 | usage("export PAGENAME ?FILE?"); |
| 925 | 903 | } |
| 926 | 904 | zPageName = g.argv[3]; |
| 927 | 905 | rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" |
| 928 | 906 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 929 | 907 | " ORDER BY x.mtime DESC LIMIT 1", |
| 930 | 908 | zPageName |
| 931 | 909 | ); |
| 932 | - if( rid ){ | |
| 933 | - Blob content; | |
| 934 | - content_get(rid, &content); | |
| 935 | - manifest_parse(&m, &content); | |
| 936 | - if( m.type==CFTYPE_WIKI ){ | |
| 937 | - zBody = m.zWiki; | |
| 938 | - } | |
| 910 | + if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ | |
| 911 | + zBody = pWiki->zWiki; | |
| 939 | 912 | } |
| 940 | 913 | if( zBody==0 ){ |
| 941 | 914 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 942 | 915 | } |
| 943 | - for(i=strlen(zBody); i>0 && isspace(zBody[i-1]); i--){} | |
| 916 | + for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} | |
| 944 | 917 | zFile = (g.argc==4) ? 0 : g.argv[4]; |
| 945 | 918 | if( zFile ){ |
| 946 | 919 | FILE * zF; |
| 947 | 920 | short doClose = 0; |
| 948 | 921 | if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){ |
| @@ -955,12 +928,13 @@ | ||
| 955 | 928 | fossil_fatal("wiki export could not open output file for writing."); |
| 956 | 929 | } |
| 957 | 930 | fprintf(zF,"%.*s\n", i, zBody); |
| 958 | 931 | if( doClose ) fclose(zF); |
| 959 | 932 | }else{ |
| 960 | - printf("%.*s\n", i, zBody); | |
| 933 | + printf("%.*s\n", i, zBody); | |
| 961 | 934 | } |
| 935 | + manifest_destroy(pWiki); | |
| 962 | 936 | return; |
| 963 | 937 | }else |
| 964 | 938 | if( strncmp(g.argv[2],"commit",n)==0 |
| 965 | 939 | || strncmp(g.argv[2],"create",n)==0 ){ |
| 966 | 940 | char *zPageName; |
| 967 | 941 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -86,14 +86,14 @@ | |
| 86 | if( !g.okRdWiki ){ |
| 87 | cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); |
| 88 | } |
| 89 | if( zIndexPage ){ |
| 90 | const char *zPathInfo = P("PATH_INFO"); |
| 91 | if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; |
| 92 | } |
| 93 | if( zIndexPage ){ |
| 94 | while( zIndexPage[0]=='/' ) zIndexPage++; |
| 95 | cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage); |
| 96 | } |
| 97 | if( zPageName ){ |
| 98 | login_check_credentials(); |
| 99 | g.zExtra = zPageName; |
| @@ -127,11 +127,11 @@ | |
| 127 | void wiki_page(void){ |
| 128 | char *zTag; |
| 129 | int rid = 0; |
| 130 | int isSandbox; |
| 131 | Blob wiki; |
| 132 | Manifest m; |
| 133 | const char *zPageName; |
| 134 | char *zBody = mprintf("%s","<i>Empty Page</i>"); |
| 135 | Stmt q; |
| 136 | int cnt = 0; |
| 137 | |
| @@ -179,20 +179,14 @@ | |
| 179 | "SELECT rid FROM tagxref" |
| 180 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 181 | " ORDER BY mtime DESC", zTag |
| 182 | ); |
| 183 | free(zTag); |
| 184 | memset(&m, 0, sizeof(m)); |
| 185 | blob_zero(&m.content); |
| 186 | if( rid ){ |
| 187 | Blob content; |
| 188 | content_get(rid, &content); |
| 189 | manifest_parse(&m, &content); |
| 190 | if( m.type==CFTYPE_WIKI && m.zWiki ){ |
| 191 | while( isspace(m.zWiki[0]) ) m.zWiki++; |
| 192 | if( m.zWiki[0] ) zBody = m.zWiki; |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | if( !g.isHome ){ |
| 197 | if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){ |
| 198 | style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", |
| @@ -249,13 +243,11 @@ | |
| 249 | if( cnt ){ |
| 250 | @ </ul> |
| 251 | } |
| 252 | db_finalize(&q); |
| 253 | |
| 254 | if( !isSandbox ){ |
| 255 | manifest_clear(&m); |
| 256 | } |
| 257 | style_footer(); |
| 258 | } |
| 259 | |
| 260 | /* |
| 261 | ** WEBPAGE: wikiedit |
| @@ -264,11 +256,11 @@ | |
| 264 | void wikiedit_page(void){ |
| 265 | char *zTag; |
| 266 | int rid = 0; |
| 267 | int isSandbox; |
| 268 | Blob wiki; |
| 269 | Manifest m; |
| 270 | const char *zPageName; |
| 271 | char *zHtmlPageName; |
| 272 | int n; |
| 273 | const char *z; |
| 274 | char *zBody = (char*)P("w"); |
| @@ -298,19 +290,12 @@ | |
| 298 | free(zTag); |
| 299 | if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ |
| 300 | login_needed(); |
| 301 | return; |
| 302 | } |
| 303 | memset(&m, 0, sizeof(m)); |
| 304 | blob_zero(&m.content); |
| 305 | if( rid && zBody==0 ){ |
| 306 | Blob content; |
| 307 | content_get(rid, &content); |
| 308 | manifest_parse(&m, &content); |
| 309 | if( m.type==CFTYPE_WIKI ){ |
| 310 | zBody = m.zWiki; |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | if( P("submit")!=0 && zBody!=0 ){ |
| 315 | char *zDate; |
| 316 | Blob cksum; |
| @@ -377,13 +362,11 @@ | |
| 377 | @ <br /> |
| 378 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 379 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 380 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 381 | @ </div></form> |
| 382 | if( !isSandbox ){ |
| 383 | manifest_clear(&m); |
| 384 | } |
| 385 | style_footer(); |
| 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** WEBPAGE: wikinew |
| @@ -477,27 +460,25 @@ | |
| 477 | if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){ |
| 478 | char *zDate; |
| 479 | Blob cksum; |
| 480 | int nrid; |
| 481 | Blob body; |
| 482 | Blob content; |
| 483 | Blob wiki; |
| 484 | Manifest m; |
| 485 | |
| 486 | blob_zero(&body); |
| 487 | if( isSandbox ){ |
| 488 | blob_appendf(&body, db_get("sandbox","")); |
| 489 | appendRemark(&body); |
| 490 | db_set("sandbox", blob_str(&body), 0); |
| 491 | }else{ |
| 492 | login_verify_csrf_secret(); |
| 493 | content_get(rid, &content); |
| 494 | manifest_parse(&m, &content); |
| 495 | if( m.type==CFTYPE_WIKI ){ |
| 496 | blob_append(&body, m.zWiki, -1); |
| 497 | } |
| 498 | manifest_clear(&m); |
| 499 | blob_zero(&wiki); |
| 500 | db_begin_transaction(); |
| 501 | zDate = db_text(0, "SELECT datetime('now')"); |
| 502 | zDate[10] = 'T'; |
| 503 | blob_appendf(&wiki, "D %s\n", zDate); |
| @@ -612,12 +593,11 @@ | |
| 612 | */ |
| 613 | void wdiff_page(void){ |
| 614 | char *zTitle; |
| 615 | int rid1, rid2; |
| 616 | const char *zPageName; |
| 617 | Blob content1, content2; |
| 618 | Manifest m1, m2; |
| 619 | Blob w1, w2, d; |
| 620 | |
| 621 | login_check_credentials(); |
| 622 | rid1 = atoi(PD("a","0")); |
| 623 | if( !g.okHistory ){ login_needed(); return; } |
| @@ -635,27 +615,24 @@ | |
| 635 | " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" |
| 636 | " ORDER BY event.mtime DESC LIMIT 1", |
| 637 | zPageName, rid1 |
| 638 | ); |
| 639 | } |
| 640 | content_get(rid1, &content1); |
| 641 | manifest_parse(&m1, &content1); |
| 642 | if( m1.type!=CFTYPE_WIKI ) fossil_redirect_home(); |
| 643 | blob_init(&w1, m1.zWiki, -1); |
| 644 | blob_zero(&w2); |
| 645 | if( rid2 ){ |
| 646 | content_get(rid2, &content2); |
| 647 | manifest_parse(&m2, &content2); |
| 648 | if( m2.type==CFTYPE_WIKI ){ |
| 649 | blob_init(&w2, m2.zWiki, -1); |
| 650 | } |
| 651 | } |
| 652 | blob_zero(&d); |
| 653 | text_diff(&w2, &w1, &d, 5, 1); |
| 654 | @ <pre> |
| 655 | @ %h(blob_str(&d)) |
| 656 | @ </pre> |
| 657 | style_footer(); |
| 658 | } |
| 659 | |
| 660 | /* |
| 661 | ** WEBPAGE: wcontent |
| @@ -914,35 +891,31 @@ | |
| 914 | } |
| 915 | |
| 916 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 917 | char const *zPageName; /* Name of the wiki page to export */ |
| 918 | char const *zFile; /* Name of the output file (0=stdout) */ |
| 919 | int rid; /* Artifact ID of the wiki page */ |
| 920 | int i; /* Loop counter */ |
| 921 | char *zBody = 0; /* Wiki page content */ |
| 922 | Manifest m; /* Parsed wiki page content */ |
| 923 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 924 | usage("export PAGENAME ?FILE?"); |
| 925 | } |
| 926 | zPageName = g.argv[3]; |
| 927 | rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" |
| 928 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 929 | " ORDER BY x.mtime DESC LIMIT 1", |
| 930 | zPageName |
| 931 | ); |
| 932 | if( rid ){ |
| 933 | Blob content; |
| 934 | content_get(rid, &content); |
| 935 | manifest_parse(&m, &content); |
| 936 | if( m.type==CFTYPE_WIKI ){ |
| 937 | zBody = m.zWiki; |
| 938 | } |
| 939 | } |
| 940 | if( zBody==0 ){ |
| 941 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 942 | } |
| 943 | for(i=strlen(zBody); i>0 && isspace(zBody[i-1]); i--){} |
| 944 | zFile = (g.argc==4) ? 0 : g.argv[4]; |
| 945 | if( zFile ){ |
| 946 | FILE * zF; |
| 947 | short doClose = 0; |
| 948 | if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){ |
| @@ -955,12 +928,13 @@ | |
| 955 | fossil_fatal("wiki export could not open output file for writing."); |
| 956 | } |
| 957 | fprintf(zF,"%.*s\n", i, zBody); |
| 958 | if( doClose ) fclose(zF); |
| 959 | }else{ |
| 960 | printf("%.*s\n", i, zBody); |
| 961 | } |
| 962 | return; |
| 963 | }else |
| 964 | if( strncmp(g.argv[2],"commit",n)==0 |
| 965 | || strncmp(g.argv[2],"create",n)==0 ){ |
| 966 | char *zPageName; |
| 967 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -86,14 +86,14 @@ | |
| 86 | if( !g.okRdWiki ){ |
| 87 | cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); |
| 88 | } |
| 89 | if( zIndexPage ){ |
| 90 | const char *zPathInfo = P("PATH_INFO"); |
| 91 | while( zIndexPage[0]=='/' ) zIndexPage++; |
| 92 | if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; |
| 93 | } |
| 94 | if( zIndexPage ){ |
| 95 | cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage); |
| 96 | } |
| 97 | if( zPageName ){ |
| 98 | login_check_credentials(); |
| 99 | g.zExtra = zPageName; |
| @@ -127,11 +127,11 @@ | |
| 127 | void wiki_page(void){ |
| 128 | char *zTag; |
| 129 | int rid = 0; |
| 130 | int isSandbox; |
| 131 | Blob wiki; |
| 132 | Manifest *pWiki = 0; |
| 133 | const char *zPageName; |
| 134 | char *zBody = mprintf("%s","<i>Empty Page</i>"); |
| 135 | Stmt q; |
| 136 | int cnt = 0; |
| 137 | |
| @@ -179,20 +179,14 @@ | |
| 179 | "SELECT rid FROM tagxref" |
| 180 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 181 | " ORDER BY mtime DESC", zTag |
| 182 | ); |
| 183 | free(zTag); |
| 184 | pWiki = manifest_get(rid, CFTYPE_WIKI); |
| 185 | if( pWiki ){ |
| 186 | while( fossil_isspace(pWiki->zWiki[0]) ) pWiki->zWiki++; |
| 187 | if( pWiki->zWiki[0] ) zBody = pWiki->zWiki; |
| 188 | } |
| 189 | } |
| 190 | if( !g.isHome ){ |
| 191 | if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){ |
| 192 | style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", |
| @@ -249,13 +243,11 @@ | |
| 243 | if( cnt ){ |
| 244 | @ </ul> |
| 245 | } |
| 246 | db_finalize(&q); |
| 247 | |
| 248 | manifest_destroy(pWiki); |
| 249 | style_footer(); |
| 250 | } |
| 251 | |
| 252 | /* |
| 253 | ** WEBPAGE: wikiedit |
| @@ -264,11 +256,11 @@ | |
| 256 | void wikiedit_page(void){ |
| 257 | char *zTag; |
| 258 | int rid = 0; |
| 259 | int isSandbox; |
| 260 | Blob wiki; |
| 261 | Manifest *pWiki = 0; |
| 262 | const char *zPageName; |
| 263 | char *zHtmlPageName; |
| 264 | int n; |
| 265 | const char *z; |
| 266 | char *zBody = (char*)P("w"); |
| @@ -298,19 +290,12 @@ | |
| 290 | free(zTag); |
| 291 | if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){ |
| 292 | login_needed(); |
| 293 | return; |
| 294 | } |
| 295 | if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ |
| 296 | zBody = pWiki->zWiki; |
| 297 | } |
| 298 | } |
| 299 | if( P("submit")!=0 && zBody!=0 ){ |
| 300 | char *zDate; |
| 301 | Blob cksum; |
| @@ -377,13 +362,11 @@ | |
| 362 | @ <br /> |
| 363 | @ <input type="submit" name="preview" value="Preview Your Changes" /> |
| 364 | @ <input type="submit" name="submit" value="Apply These Changes" /> |
| 365 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 366 | @ </div></form> |
| 367 | manifest_destroy(pWiki); |
| 368 | style_footer(); |
| 369 | } |
| 370 | |
| 371 | /* |
| 372 | ** WEBPAGE: wikinew |
| @@ -477,27 +460,25 @@ | |
| 460 | if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){ |
| 461 | char *zDate; |
| 462 | Blob cksum; |
| 463 | int nrid; |
| 464 | Blob body; |
| 465 | Blob wiki; |
| 466 | Manifest *pWiki = 0; |
| 467 | |
| 468 | blob_zero(&body); |
| 469 | if( isSandbox ){ |
| 470 | blob_appendf(&body, db_get("sandbox","")); |
| 471 | appendRemark(&body); |
| 472 | db_set("sandbox", blob_str(&body), 0); |
| 473 | }else{ |
| 474 | login_verify_csrf_secret(); |
| 475 | pWiki = manifest_get(rid, CFTYPE_WIKI); |
| 476 | if( pWiki ){ |
| 477 | blob_append(&body, pWiki->zWiki, -1); |
| 478 | manifest_destroy(pWiki); |
| 479 | } |
| 480 | blob_zero(&wiki); |
| 481 | db_begin_transaction(); |
| 482 | zDate = db_text(0, "SELECT datetime('now')"); |
| 483 | zDate[10] = 'T'; |
| 484 | blob_appendf(&wiki, "D %s\n", zDate); |
| @@ -612,12 +593,11 @@ | |
| 593 | */ |
| 594 | void wdiff_page(void){ |
| 595 | char *zTitle; |
| 596 | int rid1, rid2; |
| 597 | const char *zPageName; |
| 598 | Manifest *pW1, *pW2 = 0; |
| 599 | Blob w1, w2, d; |
| 600 | |
| 601 | login_check_credentials(); |
| 602 | rid1 = atoi(PD("a","0")); |
| 603 | if( !g.okHistory ){ login_needed(); return; } |
| @@ -635,27 +615,24 @@ | |
| 615 | " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" |
| 616 | " ORDER BY event.mtime DESC LIMIT 1", |
| 617 | zPageName, rid1 |
| 618 | ); |
| 619 | } |
| 620 | pW1 = manifest_get(rid1, CFTYPE_WIKI); |
| 621 | if( pW1==0 ) fossil_redirect_home(); |
| 622 | blob_init(&w1, pW1->zWiki, -1); |
| 623 | blob_zero(&w2); |
| 624 | if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){ |
| 625 | blob_init(&w2, pW2->zWiki, -1); |
| 626 | } |
| 627 | blob_zero(&d); |
| 628 | text_diff(&w2, &w1, &d, 5, 1); |
| 629 | @ <pre> |
| 630 | @ %h(blob_str(&d)) |
| 631 | @ </pre> |
| 632 | manifest_destroy(pW1); |
| 633 | manifest_destroy(pW2); |
| 634 | style_footer(); |
| 635 | } |
| 636 | |
| 637 | /* |
| 638 | ** WEBPAGE: wcontent |
| @@ -914,35 +891,31 @@ | |
| 891 | } |
| 892 | |
| 893 | if( strncmp(g.argv[2],"export",n)==0 ){ |
| 894 | char const *zPageName; /* Name of the wiki page to export */ |
| 895 | char const *zFile; /* Name of the output file (0=stdout) */ |
| 896 | int rid; /* Artifact ID of the wiki page */ |
| 897 | int i; /* Loop counter */ |
| 898 | char *zBody = 0; /* Wiki page content */ |
| 899 | Manifest *pWiki = 0; /* Parsed wiki page content */ |
| 900 | |
| 901 | if( (g.argc!=4) && (g.argc!=5) ){ |
| 902 | usage("export PAGENAME ?FILE?"); |
| 903 | } |
| 904 | zPageName = g.argv[3]; |
| 905 | rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" |
| 906 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 907 | " ORDER BY x.mtime DESC LIMIT 1", |
| 908 | zPageName |
| 909 | ); |
| 910 | if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ |
| 911 | zBody = pWiki->zWiki; |
| 912 | } |
| 913 | if( zBody==0 ){ |
| 914 | fossil_fatal("wiki page [%s] not found",zPageName); |
| 915 | } |
| 916 | for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} |
| 917 | zFile = (g.argc==4) ? 0 : g.argv[4]; |
| 918 | if( zFile ){ |
| 919 | FILE * zF; |
| 920 | short doClose = 0; |
| 921 | if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){ |
| @@ -955,12 +928,13 @@ | |
| 928 | fossil_fatal("wiki export could not open output file for writing."); |
| 929 | } |
| 930 | fprintf(zF,"%.*s\n", i, zBody); |
| 931 | if( doClose ) fclose(zF); |
| 932 | }else{ |
| 933 | printf("%.*s\n", i, zBody); |
| 934 | } |
| 935 | manifest_destroy(pWiki); |
| 936 | return; |
| 937 | }else |
| 938 | if( strncmp(g.argv[2],"commit",n)==0 |
| 939 | || strncmp(g.argv[2],"create",n)==0 ){ |
| 940 | char *zPageName; |
| 941 |
+29
-32
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -412,15 +412,15 @@ | ||
| 412 | 412 | static int markupLength(const char *z){ |
| 413 | 413 | int n = 1; |
| 414 | 414 | int inparen = 0; |
| 415 | 415 | int c; |
| 416 | 416 | if( z[n]=='/' ){ n++; } |
| 417 | - if( !isalpha(z[n]) ) return 0; | |
| 418 | - while( isalnum(z[n]) ){ n++; } | |
| 417 | + if( !fossil_isalpha(z[n]) ) return 0; | |
| 418 | + while( fossil_isalnum(z[n]) ){ n++; } | |
| 419 | 419 | c = z[n]; |
| 420 | 420 | if( c=='/' && z[n+1]=='>' ){ return n+2; } |
| 421 | - if( c!='>' && !isspace(c) ) return 0; | |
| 421 | + if( c!='>' && !fossil_isspace(c) ) return 0; | |
| 422 | 422 | while( (c = z[n])!=0 && (c!='>' || inparen) ){ |
| 423 | 423 | if( c==inparen ){ |
| 424 | 424 | inparen = 0; |
| 425 | 425 | }else if( inparen==0 && (c=='"' || c=='\'') ){ |
| 426 | 426 | inparen = c; |
| @@ -437,11 +437,11 @@ | ||
| 437 | 437 | ** of characters through the closing "\n". If not, return 0. |
| 438 | 438 | */ |
| 439 | 439 | static int paragraphBreakLength(const char *z){ |
| 440 | 440 | int i, n; |
| 441 | 441 | int nNewline = 1; |
| 442 | - for(i=1, n=0; isspace(z[i]); i++){ | |
| 442 | + for(i=1, n=0; fossil_isspace(z[i]); i++){ | |
| 443 | 443 | if( z[i]=='\n' ){ |
| 444 | 444 | nNewline++; |
| 445 | 445 | n = i; |
| 446 | 446 | } |
| 447 | 447 | } |
| @@ -482,14 +482,14 @@ | ||
| 482 | 482 | */ |
| 483 | 483 | static int isElement(const char *z){ |
| 484 | 484 | int i; |
| 485 | 485 | assert( z[0]=='&' ); |
| 486 | 486 | if( z[1]=='#' ){ |
| 487 | - for(i=2; isdigit(z[i]); i++){} | |
| 487 | + for(i=2; fossil_isdigit(z[i]); i++){} | |
| 488 | 488 | return i>2 && z[i]==';'; |
| 489 | 489 | }else{ |
| 490 | - for(i=1; isalpha(z[i]); i++){} | |
| 490 | + for(i=1; fossil_isalpha(z[i]); i++){} | |
| 491 | 491 | return i>1 && z[i]==';'; |
| 492 | 492 | } |
| 493 | 493 | } |
| 494 | 494 | |
| 495 | 495 | /* |
| @@ -511,11 +511,11 @@ | ||
| 511 | 511 | while( z[n]==' ' || z[n]=='\t' ){ |
| 512 | 512 | if( z[n]=='\t' ) i++; |
| 513 | 513 | i++; |
| 514 | 514 | n++; |
| 515 | 515 | } |
| 516 | - if( i<2 || isspace(z[n]) ) return 0; | |
| 516 | + if( i<2 || fossil_isspace(z[n]) ) return 0; | |
| 517 | 517 | return n; |
| 518 | 518 | } |
| 519 | 519 | |
| 520 | 520 | /* |
| 521 | 521 | ** Check to see if the z[] string is the beginning of a enumeration value. |
| @@ -536,11 +536,11 @@ | ||
| 536 | 536 | if( z[n]=='\t' ) i++; |
| 537 | 537 | i++; |
| 538 | 538 | n++; |
| 539 | 539 | } |
| 540 | 540 | if( i<2 ) return 0; |
| 541 | - for(i=0; isdigit(z[n]); i++, n++){} | |
| 541 | + for(i=0; fossil_isdigit(z[n]); i++, n++){} | |
| 542 | 542 | if( i==0 ) return 0; |
| 543 | 543 | if( z[n]=='.' ){ |
| 544 | 544 | n++; |
| 545 | 545 | } |
| 546 | 546 | i = 0; |
| @@ -547,11 +547,11 @@ | ||
| 547 | 547 | while( z[n]==' ' || z[n]=='\t' ){ |
| 548 | 548 | if( z[n]=='\t' ) i++; |
| 549 | 549 | i++; |
| 550 | 550 | n++; |
| 551 | 551 | } |
| 552 | - if( i<2 || isspace(z[n]) ) return 0; | |
| 552 | + if( i<2 || fossil_isspace(z[n]) ) return 0; | |
| 553 | 553 | return n; |
| 554 | 554 | } |
| 555 | 555 | |
| 556 | 556 | /* |
| 557 | 557 | ** Check to see if the z[] string is the beginning of an indented |
| @@ -565,11 +565,11 @@ | ||
| 565 | 565 | while( z[n]==' ' || z[n]=='\t' ){ |
| 566 | 566 | if( z[n]=='\t' ) i++; |
| 567 | 567 | i++; |
| 568 | 568 | n++; |
| 569 | 569 | } |
| 570 | - if( i<2 || isspace(z[n]) ) return 0; | |
| 570 | + if( i<2 || fossil_isspace(z[n]) ) return 0; | |
| 571 | 571 | return n; |
| 572 | 572 | } |
| 573 | 573 | |
| 574 | 574 | /* |
| 575 | 575 | ** Check to see if the z[] string is a wiki hyperlink. If it is, |
| @@ -612,16 +612,16 @@ | ||
| 612 | 612 | if( z[0]=='\n' ){ |
| 613 | 613 | n = paragraphBreakLength(z); |
| 614 | 614 | if( n>0 ){ |
| 615 | 615 | *pTokenType = TOKEN_PARAGRAPH; |
| 616 | 616 | return n; |
| 617 | - }else if( isspace(z[1]) ){ | |
| 617 | + }else if( fossil_isspace(z[1]) ){ | |
| 618 | 618 | *pTokenType = TOKEN_NEWLINE; |
| 619 | 619 | return 1; |
| 620 | 620 | } |
| 621 | 621 | } |
| 622 | - if( (p->state & AT_NEWLINE)!=0 && isspace(z[0]) ){ | |
| 622 | + if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){ | |
| 623 | 623 | n = listItemLength(z, '*'); |
| 624 | 624 | if( n>0 ){ |
| 625 | 625 | *pTokenType = TOKEN_BUL_LI; |
| 626 | 626 | return n; |
| 627 | 627 | } |
| @@ -634,11 +634,11 @@ | ||
| 634 | 634 | if( n>0 ){ |
| 635 | 635 | *pTokenType = TOKEN_ENUM; |
| 636 | 636 | return n; |
| 637 | 637 | } |
| 638 | 638 | } |
| 639 | - if( (p->state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){ | |
| 639 | + if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){ | |
| 640 | 640 | n = indentLength(z); |
| 641 | 641 | if( n>0 ){ |
| 642 | 642 | *pTokenType = TOKEN_INDENT; |
| 643 | 643 | return n; |
| 644 | 644 | } |
| @@ -705,37 +705,37 @@ | ||
| 705 | 705 | }else{ |
| 706 | 706 | p->endTag = 0; |
| 707 | 707 | i = 1; |
| 708 | 708 | } |
| 709 | 709 | j = 0; |
| 710 | - while( isalnum(z[i]) ){ | |
| 711 | - if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); | |
| 710 | + while( fossil_isalnum(z[i]) ){ | |
| 711 | + if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); | |
| 712 | 712 | i++; |
| 713 | 713 | } |
| 714 | 714 | zTag[j] = 0; |
| 715 | 715 | p->iCode = findTag(zTag); |
| 716 | 716 | p->iType = aMarkup[p->iCode].iType; |
| 717 | 717 | p->nAttr = 0; |
| 718 | - while( isspace(z[i]) ){ i++; } | |
| 719 | - while( p->nAttr<8 && isalpha(z[i]) ){ | |
| 718 | + while( fossil_isspace(z[i]) ){ i++; } | |
| 719 | + while( p->nAttr<8 && fossil_isalpha(z[i]) ){ | |
| 720 | 720 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 721 | 721 | j = 0; |
| 722 | - while( isalnum(z[i]) ){ | |
| 723 | - if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); | |
| 722 | + while( fossil_isalnum(z[i]) ){ | |
| 723 | + if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); | |
| 724 | 724 | i++; |
| 725 | 725 | } |
| 726 | 726 | zTag[j] = 0; |
| 727 | 727 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| 728 | 728 | attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; |
| 729 | - while( isspace(z[i]) ){ z++; } | |
| 729 | + while( fossil_isspace(z[i]) ){ z++; } | |
| 730 | 730 | if( z[i]!='=' ){ |
| 731 | 731 | p->aAttr[p->nAttr].zValue = 0; |
| 732 | 732 | p->aAttr[p->nAttr].cTerm = 0; |
| 733 | 733 | c = 0; |
| 734 | 734 | }else{ |
| 735 | 735 | i++; |
| 736 | - while( isspace(z[i]) ){ z++; } | |
| 736 | + while( fossil_isspace(z[i]) ){ z++; } | |
| 737 | 737 | if( z[i]=='"' ){ |
| 738 | 738 | i++; |
| 739 | 739 | zValue = &z[i]; |
| 740 | 740 | while( z[i] && z[i]!='"' ){ i++; } |
| 741 | 741 | }else if( z[i]=='\'' ){ |
| @@ -742,11 +742,11 @@ | ||
| 742 | 742 | i++; |
| 743 | 743 | zValue = &z[i]; |
| 744 | 744 | while( z[i] && z[i]!='\'' ){ i++; } |
| 745 | 745 | }else{ |
| 746 | 746 | zValue = &z[i]; |
| 747 | - while( !isspace(z[i]) && z[i]!='>' ){ z++; } | |
| 747 | + while( !fossil_isspace(z[i]) && z[i]!='>' ){ z++; } | |
| 748 | 748 | } |
| 749 | 749 | if( attrOk ){ |
| 750 | 750 | p->aAttr[p->nAttr].zValue = zValue; |
| 751 | 751 | p->aAttr[p->nAttr].cTerm = c = z[i]; |
| 752 | 752 | z[i] = 0; |
| @@ -755,11 +755,11 @@ | ||
| 755 | 755 | } |
| 756 | 756 | if( attrOk ){ |
| 757 | 757 | seen |= aAttribute[iACode].iMask; |
| 758 | 758 | p->nAttr++; |
| 759 | 759 | } |
| 760 | - while( isspace(z[i]) ){ i++; } | |
| 760 | + while( fossil_isspace(z[i]) ){ i++; } | |
| 761 | 761 | if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 762 | 762 | } |
| 763 | 763 | } |
| 764 | 764 | |
| 765 | 765 | /* |
| @@ -838,14 +838,11 @@ | ||
| 838 | 838 | ** if necessary. |
| 839 | 839 | */ |
| 840 | 840 | static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ |
| 841 | 841 | if( p->nStack>=p->nAlloc ){ |
| 842 | 842 | p->nAlloc = p->nAlloc*2 + 100; |
| 843 | - p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); | |
| 844 | - if( p->aStack==0 ){ | |
| 845 | - fossil_panic("out of memory"); | |
| 846 | - } | |
| 843 | + p->aStack = fossil_realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); | |
| 847 | 844 | } |
| 848 | 845 | p->aStack[p->nStack].iCode = elem; |
| 849 | 846 | p->aStack[p->nStack].zId = zId; |
| 850 | 847 | p->aStack[p->nStack].allowWiki = w; |
| 851 | 848 | p->nStack++; |
| @@ -1067,11 +1064,11 @@ | ||
| 1067 | 1064 | } |
| 1068 | 1065 | } |
| 1069 | 1066 | }else if( g.okHistory ){ |
| 1070 | 1067 | blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget); |
| 1071 | 1068 | } |
| 1072 | - }else if( strlen(zTarget)>=10 && isdigit(zTarget[0]) && zTarget[4]=='-' | |
| 1069 | + }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' | |
| 1073 | 1070 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1074 | 1071 | blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget); |
| 1075 | 1072 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1076 | 1073 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1077 | 1074 | zTarget += 5; |
| @@ -1245,18 +1242,18 @@ | ||
| 1245 | 1242 | zTarget = &z[1]; |
| 1246 | 1243 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1247 | 1244 | if( z[i]=='|' && zDisplay==0 ){ |
| 1248 | 1245 | zDisplay = &z[i+1]; |
| 1249 | 1246 | z[i] = 0; |
| 1250 | - for(j=i-1; j>0 && isspace(z[j]); j--){ z[j] = 0; } | |
| 1247 | + for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; } | |
| 1251 | 1248 | } |
| 1252 | 1249 | } |
| 1253 | 1250 | z[i] = 0; |
| 1254 | 1251 | if( zDisplay==0 ){ |
| 1255 | 1252 | zDisplay = zTarget; |
| 1256 | 1253 | }else{ |
| 1257 | - while( isspace(*zDisplay) ) zDisplay++; | |
| 1254 | + while( fossil_isspace(*zDisplay) ) zDisplay++; | |
| 1258 | 1255 | } |
| 1259 | 1256 | openHyperlink(p, zTarget, zClose, sizeof(zClose)); |
| 1260 | 1257 | savedState = p->state; |
| 1261 | 1258 | p->state &= ~ALLOW_WIKI; |
| 1262 | 1259 | p->state |= FONT_MARKUP_ONLY; |
| @@ -1265,11 +1262,11 @@ | ||
| 1265 | 1262 | blob_append(p->pOut, zClose, -1); |
| 1266 | 1263 | break; |
| 1267 | 1264 | } |
| 1268 | 1265 | case TOKEN_TEXT: { |
| 1269 | 1266 | int i; |
| 1270 | - for(i=0; i<n && isspace(z[i]); i++){} | |
| 1267 | + for(i=0; i<n && fossil_isspace(z[i]); i++){} | |
| 1271 | 1268 | if( i<n ) startAutoParagraph(p); |
| 1272 | 1269 | blob_append(p->pOut, z, n); |
| 1273 | 1270 | break; |
| 1274 | 1271 | } |
| 1275 | 1272 | case TOKEN_RAW: { |
| @@ -1521,11 +1518,11 @@ | ||
| 1521 | 1518 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1522 | 1519 | char *z; |
| 1523 | 1520 | int i; |
| 1524 | 1521 | int iStart; |
| 1525 | 1522 | z = skip_bom(blob_str(pIn)); |
| 1526 | - for(i=0; isspace(z[i]); i++){} | |
| 1523 | + for(i=0; fossil_isspace(z[i]); i++){} | |
| 1527 | 1524 | if( z[i]!='<' ) return 0; |
| 1528 | 1525 | i++; |
| 1529 | 1526 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1530 | 1527 | iStart = i+6; |
| 1531 | 1528 | for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} |
| 1532 | 1529 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -412,15 +412,15 @@ | |
| 412 | static int markupLength(const char *z){ |
| 413 | int n = 1; |
| 414 | int inparen = 0; |
| 415 | int c; |
| 416 | if( z[n]=='/' ){ n++; } |
| 417 | if( !isalpha(z[n]) ) return 0; |
| 418 | while( isalnum(z[n]) ){ n++; } |
| 419 | c = z[n]; |
| 420 | if( c=='/' && z[n+1]=='>' ){ return n+2; } |
| 421 | if( c!='>' && !isspace(c) ) return 0; |
| 422 | while( (c = z[n])!=0 && (c!='>' || inparen) ){ |
| 423 | if( c==inparen ){ |
| 424 | inparen = 0; |
| 425 | }else if( inparen==0 && (c=='"' || c=='\'') ){ |
| 426 | inparen = c; |
| @@ -437,11 +437,11 @@ | |
| 437 | ** of characters through the closing "\n". If not, return 0. |
| 438 | */ |
| 439 | static int paragraphBreakLength(const char *z){ |
| 440 | int i, n; |
| 441 | int nNewline = 1; |
| 442 | for(i=1, n=0; isspace(z[i]); i++){ |
| 443 | if( z[i]=='\n' ){ |
| 444 | nNewline++; |
| 445 | n = i; |
| 446 | } |
| 447 | } |
| @@ -482,14 +482,14 @@ | |
| 482 | */ |
| 483 | static int isElement(const char *z){ |
| 484 | int i; |
| 485 | assert( z[0]=='&' ); |
| 486 | if( z[1]=='#' ){ |
| 487 | for(i=2; isdigit(z[i]); i++){} |
| 488 | return i>2 && z[i]==';'; |
| 489 | }else{ |
| 490 | for(i=1; isalpha(z[i]); i++){} |
| 491 | return i>1 && z[i]==';'; |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | /* |
| @@ -511,11 +511,11 @@ | |
| 511 | while( z[n]==' ' || z[n]=='\t' ){ |
| 512 | if( z[n]=='\t' ) i++; |
| 513 | i++; |
| 514 | n++; |
| 515 | } |
| 516 | if( i<2 || isspace(z[n]) ) return 0; |
| 517 | return n; |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 | ** Check to see if the z[] string is the beginning of a enumeration value. |
| @@ -536,11 +536,11 @@ | |
| 536 | if( z[n]=='\t' ) i++; |
| 537 | i++; |
| 538 | n++; |
| 539 | } |
| 540 | if( i<2 ) return 0; |
| 541 | for(i=0; isdigit(z[n]); i++, n++){} |
| 542 | if( i==0 ) return 0; |
| 543 | if( z[n]=='.' ){ |
| 544 | n++; |
| 545 | } |
| 546 | i = 0; |
| @@ -547,11 +547,11 @@ | |
| 547 | while( z[n]==' ' || z[n]=='\t' ){ |
| 548 | if( z[n]=='\t' ) i++; |
| 549 | i++; |
| 550 | n++; |
| 551 | } |
| 552 | if( i<2 || isspace(z[n]) ) return 0; |
| 553 | return n; |
| 554 | } |
| 555 | |
| 556 | /* |
| 557 | ** Check to see if the z[] string is the beginning of an indented |
| @@ -565,11 +565,11 @@ | |
| 565 | while( z[n]==' ' || z[n]=='\t' ){ |
| 566 | if( z[n]=='\t' ) i++; |
| 567 | i++; |
| 568 | n++; |
| 569 | } |
| 570 | if( i<2 || isspace(z[n]) ) return 0; |
| 571 | return n; |
| 572 | } |
| 573 | |
| 574 | /* |
| 575 | ** Check to see if the z[] string is a wiki hyperlink. If it is, |
| @@ -612,16 +612,16 @@ | |
| 612 | if( z[0]=='\n' ){ |
| 613 | n = paragraphBreakLength(z); |
| 614 | if( n>0 ){ |
| 615 | *pTokenType = TOKEN_PARAGRAPH; |
| 616 | return n; |
| 617 | }else if( isspace(z[1]) ){ |
| 618 | *pTokenType = TOKEN_NEWLINE; |
| 619 | return 1; |
| 620 | } |
| 621 | } |
| 622 | if( (p->state & AT_NEWLINE)!=0 && isspace(z[0]) ){ |
| 623 | n = listItemLength(z, '*'); |
| 624 | if( n>0 ){ |
| 625 | *pTokenType = TOKEN_BUL_LI; |
| 626 | return n; |
| 627 | } |
| @@ -634,11 +634,11 @@ | |
| 634 | if( n>0 ){ |
| 635 | *pTokenType = TOKEN_ENUM; |
| 636 | return n; |
| 637 | } |
| 638 | } |
| 639 | if( (p->state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){ |
| 640 | n = indentLength(z); |
| 641 | if( n>0 ){ |
| 642 | *pTokenType = TOKEN_INDENT; |
| 643 | return n; |
| 644 | } |
| @@ -705,37 +705,37 @@ | |
| 705 | }else{ |
| 706 | p->endTag = 0; |
| 707 | i = 1; |
| 708 | } |
| 709 | j = 0; |
| 710 | while( isalnum(z[i]) ){ |
| 711 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 712 | i++; |
| 713 | } |
| 714 | zTag[j] = 0; |
| 715 | p->iCode = findTag(zTag); |
| 716 | p->iType = aMarkup[p->iCode].iType; |
| 717 | p->nAttr = 0; |
| 718 | while( isspace(z[i]) ){ i++; } |
| 719 | while( p->nAttr<8 && isalpha(z[i]) ){ |
| 720 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 721 | j = 0; |
| 722 | while( isalnum(z[i]) ){ |
| 723 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 724 | i++; |
| 725 | } |
| 726 | zTag[j] = 0; |
| 727 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| 728 | attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; |
| 729 | while( isspace(z[i]) ){ z++; } |
| 730 | if( z[i]!='=' ){ |
| 731 | p->aAttr[p->nAttr].zValue = 0; |
| 732 | p->aAttr[p->nAttr].cTerm = 0; |
| 733 | c = 0; |
| 734 | }else{ |
| 735 | i++; |
| 736 | while( isspace(z[i]) ){ z++; } |
| 737 | if( z[i]=='"' ){ |
| 738 | i++; |
| 739 | zValue = &z[i]; |
| 740 | while( z[i] && z[i]!='"' ){ i++; } |
| 741 | }else if( z[i]=='\'' ){ |
| @@ -742,11 +742,11 @@ | |
| 742 | i++; |
| 743 | zValue = &z[i]; |
| 744 | while( z[i] && z[i]!='\'' ){ i++; } |
| 745 | }else{ |
| 746 | zValue = &z[i]; |
| 747 | while( !isspace(z[i]) && z[i]!='>' ){ z++; } |
| 748 | } |
| 749 | if( attrOk ){ |
| 750 | p->aAttr[p->nAttr].zValue = zValue; |
| 751 | p->aAttr[p->nAttr].cTerm = c = z[i]; |
| 752 | z[i] = 0; |
| @@ -755,11 +755,11 @@ | |
| 755 | } |
| 756 | if( attrOk ){ |
| 757 | seen |= aAttribute[iACode].iMask; |
| 758 | p->nAttr++; |
| 759 | } |
| 760 | while( isspace(z[i]) ){ i++; } |
| 761 | if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | /* |
| @@ -838,14 +838,11 @@ | |
| 838 | ** if necessary. |
| 839 | */ |
| 840 | static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ |
| 841 | if( p->nStack>=p->nAlloc ){ |
| 842 | p->nAlloc = p->nAlloc*2 + 100; |
| 843 | p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); |
| 844 | if( p->aStack==0 ){ |
| 845 | fossil_panic("out of memory"); |
| 846 | } |
| 847 | } |
| 848 | p->aStack[p->nStack].iCode = elem; |
| 849 | p->aStack[p->nStack].zId = zId; |
| 850 | p->aStack[p->nStack].allowWiki = w; |
| 851 | p->nStack++; |
| @@ -1067,11 +1064,11 @@ | |
| 1067 | } |
| 1068 | } |
| 1069 | }else if( g.okHistory ){ |
| 1070 | blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget); |
| 1071 | } |
| 1072 | }else if( strlen(zTarget)>=10 && isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1073 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1074 | blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget); |
| 1075 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1076 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1077 | zTarget += 5; |
| @@ -1245,18 +1242,18 @@ | |
| 1245 | zTarget = &z[1]; |
| 1246 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1247 | if( z[i]=='|' && zDisplay==0 ){ |
| 1248 | zDisplay = &z[i+1]; |
| 1249 | z[i] = 0; |
| 1250 | for(j=i-1; j>0 && isspace(z[j]); j--){ z[j] = 0; } |
| 1251 | } |
| 1252 | } |
| 1253 | z[i] = 0; |
| 1254 | if( zDisplay==0 ){ |
| 1255 | zDisplay = zTarget; |
| 1256 | }else{ |
| 1257 | while( isspace(*zDisplay) ) zDisplay++; |
| 1258 | } |
| 1259 | openHyperlink(p, zTarget, zClose, sizeof(zClose)); |
| 1260 | savedState = p->state; |
| 1261 | p->state &= ~ALLOW_WIKI; |
| 1262 | p->state |= FONT_MARKUP_ONLY; |
| @@ -1265,11 +1262,11 @@ | |
| 1265 | blob_append(p->pOut, zClose, -1); |
| 1266 | break; |
| 1267 | } |
| 1268 | case TOKEN_TEXT: { |
| 1269 | int i; |
| 1270 | for(i=0; i<n && isspace(z[i]); i++){} |
| 1271 | if( i<n ) startAutoParagraph(p); |
| 1272 | blob_append(p->pOut, z, n); |
| 1273 | break; |
| 1274 | } |
| 1275 | case TOKEN_RAW: { |
| @@ -1521,11 +1518,11 @@ | |
| 1521 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1522 | char *z; |
| 1523 | int i; |
| 1524 | int iStart; |
| 1525 | z = skip_bom(blob_str(pIn)); |
| 1526 | for(i=0; isspace(z[i]); i++){} |
| 1527 | if( z[i]!='<' ) return 0; |
| 1528 | i++; |
| 1529 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1530 | iStart = i+6; |
| 1531 | for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} |
| 1532 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -412,15 +412,15 @@ | |
| 412 | static int markupLength(const char *z){ |
| 413 | int n = 1; |
| 414 | int inparen = 0; |
| 415 | int c; |
| 416 | if( z[n]=='/' ){ n++; } |
| 417 | if( !fossil_isalpha(z[n]) ) return 0; |
| 418 | while( fossil_isalnum(z[n]) ){ n++; } |
| 419 | c = z[n]; |
| 420 | if( c=='/' && z[n+1]=='>' ){ return n+2; } |
| 421 | if( c!='>' && !fossil_isspace(c) ) return 0; |
| 422 | while( (c = z[n])!=0 && (c!='>' || inparen) ){ |
| 423 | if( c==inparen ){ |
| 424 | inparen = 0; |
| 425 | }else if( inparen==0 && (c=='"' || c=='\'') ){ |
| 426 | inparen = c; |
| @@ -437,11 +437,11 @@ | |
| 437 | ** of characters through the closing "\n". If not, return 0. |
| 438 | */ |
| 439 | static int paragraphBreakLength(const char *z){ |
| 440 | int i, n; |
| 441 | int nNewline = 1; |
| 442 | for(i=1, n=0; fossil_isspace(z[i]); i++){ |
| 443 | if( z[i]=='\n' ){ |
| 444 | nNewline++; |
| 445 | n = i; |
| 446 | } |
| 447 | } |
| @@ -482,14 +482,14 @@ | |
| 482 | */ |
| 483 | static int isElement(const char *z){ |
| 484 | int i; |
| 485 | assert( z[0]=='&' ); |
| 486 | if( z[1]=='#' ){ |
| 487 | for(i=2; fossil_isdigit(z[i]); i++){} |
| 488 | return i>2 && z[i]==';'; |
| 489 | }else{ |
| 490 | for(i=1; fossil_isalpha(z[i]); i++){} |
| 491 | return i>1 && z[i]==';'; |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | /* |
| @@ -511,11 +511,11 @@ | |
| 511 | while( z[n]==' ' || z[n]=='\t' ){ |
| 512 | if( z[n]=='\t' ) i++; |
| 513 | i++; |
| 514 | n++; |
| 515 | } |
| 516 | if( i<2 || fossil_isspace(z[n]) ) return 0; |
| 517 | return n; |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 | ** Check to see if the z[] string is the beginning of a enumeration value. |
| @@ -536,11 +536,11 @@ | |
| 536 | if( z[n]=='\t' ) i++; |
| 537 | i++; |
| 538 | n++; |
| 539 | } |
| 540 | if( i<2 ) return 0; |
| 541 | for(i=0; fossil_isdigit(z[n]); i++, n++){} |
| 542 | if( i==0 ) return 0; |
| 543 | if( z[n]=='.' ){ |
| 544 | n++; |
| 545 | } |
| 546 | i = 0; |
| @@ -547,11 +547,11 @@ | |
| 547 | while( z[n]==' ' || z[n]=='\t' ){ |
| 548 | if( z[n]=='\t' ) i++; |
| 549 | i++; |
| 550 | n++; |
| 551 | } |
| 552 | if( i<2 || fossil_isspace(z[n]) ) return 0; |
| 553 | return n; |
| 554 | } |
| 555 | |
| 556 | /* |
| 557 | ** Check to see if the z[] string is the beginning of an indented |
| @@ -565,11 +565,11 @@ | |
| 565 | while( z[n]==' ' || z[n]=='\t' ){ |
| 566 | if( z[n]=='\t' ) i++; |
| 567 | i++; |
| 568 | n++; |
| 569 | } |
| 570 | if( i<2 || fossil_isspace(z[n]) ) return 0; |
| 571 | return n; |
| 572 | } |
| 573 | |
| 574 | /* |
| 575 | ** Check to see if the z[] string is a wiki hyperlink. If it is, |
| @@ -612,16 +612,16 @@ | |
| 612 | if( z[0]=='\n' ){ |
| 613 | n = paragraphBreakLength(z); |
| 614 | if( n>0 ){ |
| 615 | *pTokenType = TOKEN_PARAGRAPH; |
| 616 | return n; |
| 617 | }else if( fossil_isspace(z[1]) ){ |
| 618 | *pTokenType = TOKEN_NEWLINE; |
| 619 | return 1; |
| 620 | } |
| 621 | } |
| 622 | if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){ |
| 623 | n = listItemLength(z, '*'); |
| 624 | if( n>0 ){ |
| 625 | *pTokenType = TOKEN_BUL_LI; |
| 626 | return n; |
| 627 | } |
| @@ -634,11 +634,11 @@ | |
| 634 | if( n>0 ){ |
| 635 | *pTokenType = TOKEN_ENUM; |
| 636 | return n; |
| 637 | } |
| 638 | } |
| 639 | if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){ |
| 640 | n = indentLength(z); |
| 641 | if( n>0 ){ |
| 642 | *pTokenType = TOKEN_INDENT; |
| 643 | return n; |
| 644 | } |
| @@ -705,37 +705,37 @@ | |
| 705 | }else{ |
| 706 | p->endTag = 0; |
| 707 | i = 1; |
| 708 | } |
| 709 | j = 0; |
| 710 | while( fossil_isalnum(z[i]) ){ |
| 711 | if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); |
| 712 | i++; |
| 713 | } |
| 714 | zTag[j] = 0; |
| 715 | p->iCode = findTag(zTag); |
| 716 | p->iType = aMarkup[p->iCode].iType; |
| 717 | p->nAttr = 0; |
| 718 | while( fossil_isspace(z[i]) ){ i++; } |
| 719 | while( p->nAttr<8 && fossil_isalpha(z[i]) ){ |
| 720 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 721 | j = 0; |
| 722 | while( fossil_isalnum(z[i]) ){ |
| 723 | if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); |
| 724 | i++; |
| 725 | } |
| 726 | zTag[j] = 0; |
| 727 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| 728 | attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; |
| 729 | while( fossil_isspace(z[i]) ){ z++; } |
| 730 | if( z[i]!='=' ){ |
| 731 | p->aAttr[p->nAttr].zValue = 0; |
| 732 | p->aAttr[p->nAttr].cTerm = 0; |
| 733 | c = 0; |
| 734 | }else{ |
| 735 | i++; |
| 736 | while( fossil_isspace(z[i]) ){ z++; } |
| 737 | if( z[i]=='"' ){ |
| 738 | i++; |
| 739 | zValue = &z[i]; |
| 740 | while( z[i] && z[i]!='"' ){ i++; } |
| 741 | }else if( z[i]=='\'' ){ |
| @@ -742,11 +742,11 @@ | |
| 742 | i++; |
| 743 | zValue = &z[i]; |
| 744 | while( z[i] && z[i]!='\'' ){ i++; } |
| 745 | }else{ |
| 746 | zValue = &z[i]; |
| 747 | while( !fossil_isspace(z[i]) && z[i]!='>' ){ z++; } |
| 748 | } |
| 749 | if( attrOk ){ |
| 750 | p->aAttr[p->nAttr].zValue = zValue; |
| 751 | p->aAttr[p->nAttr].cTerm = c = z[i]; |
| 752 | z[i] = 0; |
| @@ -755,11 +755,11 @@ | |
| 755 | } |
| 756 | if( attrOk ){ |
| 757 | seen |= aAttribute[iACode].iMask; |
| 758 | p->nAttr++; |
| 759 | } |
| 760 | while( fossil_isspace(z[i]) ){ i++; } |
| 761 | if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | /* |
| @@ -838,14 +838,11 @@ | |
| 838 | ** if necessary. |
| 839 | */ |
| 840 | static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ |
| 841 | if( p->nStack>=p->nAlloc ){ |
| 842 | p->nAlloc = p->nAlloc*2 + 100; |
| 843 | p->aStack = fossil_realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); |
| 844 | } |
| 845 | p->aStack[p->nStack].iCode = elem; |
| 846 | p->aStack[p->nStack].zId = zId; |
| 847 | p->aStack[p->nStack].allowWiki = w; |
| 848 | p->nStack++; |
| @@ -1067,11 +1064,11 @@ | |
| 1064 | } |
| 1065 | } |
| 1066 | }else if( g.okHistory ){ |
| 1067 | blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget); |
| 1068 | } |
| 1069 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1070 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1071 | blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget); |
| 1072 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1073 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1074 | zTarget += 5; |
| @@ -1245,18 +1242,18 @@ | |
| 1242 | zTarget = &z[1]; |
| 1243 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1244 | if( z[i]=='|' && zDisplay==0 ){ |
| 1245 | zDisplay = &z[i+1]; |
| 1246 | z[i] = 0; |
| 1247 | for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; } |
| 1248 | } |
| 1249 | } |
| 1250 | z[i] = 0; |
| 1251 | if( zDisplay==0 ){ |
| 1252 | zDisplay = zTarget; |
| 1253 | }else{ |
| 1254 | while( fossil_isspace(*zDisplay) ) zDisplay++; |
| 1255 | } |
| 1256 | openHyperlink(p, zTarget, zClose, sizeof(zClose)); |
| 1257 | savedState = p->state; |
| 1258 | p->state &= ~ALLOW_WIKI; |
| 1259 | p->state |= FONT_MARKUP_ONLY; |
| @@ -1265,11 +1262,11 @@ | |
| 1262 | blob_append(p->pOut, zClose, -1); |
| 1263 | break; |
| 1264 | } |
| 1265 | case TOKEN_TEXT: { |
| 1266 | int i; |
| 1267 | for(i=0; i<n && fossil_isspace(z[i]); i++){} |
| 1268 | if( i<n ) startAutoParagraph(p); |
| 1269 | blob_append(p->pOut, z, n); |
| 1270 | break; |
| 1271 | } |
| 1272 | case TOKEN_RAW: { |
| @@ -1521,11 +1518,11 @@ | |
| 1518 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1519 | char *z; |
| 1520 | int i; |
| 1521 | int iStart; |
| 1522 | z = skip_bom(blob_str(pIn)); |
| 1523 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1524 | if( z[i]!='<' ) return 0; |
| 1525 | i++; |
| 1526 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1527 | iStart = i+6; |
| 1528 | for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} |
| 1529 |
+4
-7
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -106,14 +106,14 @@ | ||
| 106 | 106 | wanted -= got; |
| 107 | 107 | } |
| 108 | 108 | fclose(out); |
| 109 | 109 | out = 0; |
| 110 | 110 | sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", |
| 111 | - g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, | |
| 111 | + _pgmptr, g.zRepositoryName, zRequestFName, zReplyFName, | |
| 112 | 112 | inet_ntoa(p->addr.sin_addr), p->zNotFound |
| 113 | 113 | ); |
| 114 | - portable_system(zCmd); | |
| 114 | + fossil_system(zCmd); | |
| 115 | 115 | in = fopen(zReplyFName, "rb"); |
| 116 | 116 | if( in ){ |
| 117 | 117 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| 118 | 118 | send(p->s, zHdr, got, 0); |
| 119 | 119 | } |
| @@ -190,11 +190,11 @@ | ||
| 190 | 190 | zTempPrefix = mprintf("fossil_server_P%d_", iPort); |
| 191 | 191 | printf("Listening for HTTP requests on TCP port %d\n", iPort); |
| 192 | 192 | if( zBrowser ){ |
| 193 | 193 | zBrowser = mprintf(zBrowser, iPort); |
| 194 | 194 | printf("Launch webbrowser: %s\n", zBrowser); |
| 195 | - portable_system(zBrowser); | |
| 195 | + fossil_system(zBrowser); | |
| 196 | 196 | } |
| 197 | 197 | printf("Type Ctrl-C to stop the HTTP server\n"); |
| 198 | 198 | for(;;){ |
| 199 | 199 | SOCKET client; |
| 200 | 200 | SOCKADDR_IN client_addr; |
| @@ -207,14 +207,11 @@ | ||
| 207 | 207 | } |
| 208 | 208 | if( client==INVALID_SOCKET ){ |
| 209 | 209 | closesocket(s); |
| 210 | 210 | fossil_fatal("error from accept()"); |
| 211 | 211 | } |
| 212 | - p = malloc( sizeof(*p) ); | |
| 213 | - if( p==0 ){ | |
| 214 | - fossil_fatal("out of memory"); | |
| 215 | - } | |
| 212 | + p = fossil_malloc( sizeof(*p) ); | |
| 216 | 213 | p->id = ++idCnt; |
| 217 | 214 | p->s = client; |
| 218 | 215 | p->addr = client_addr; |
| 219 | 216 | p->zNotFound = zNotFoundOption; |
| 220 | 217 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 221 | 218 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -106,14 +106,14 @@ | |
| 106 | wanted -= got; |
| 107 | } |
| 108 | fclose(out); |
| 109 | out = 0; |
| 110 | sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", |
| 111 | g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName, |
| 112 | inet_ntoa(p->addr.sin_addr), p->zNotFound |
| 113 | ); |
| 114 | portable_system(zCmd); |
| 115 | in = fopen(zReplyFName, "rb"); |
| 116 | if( in ){ |
| 117 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| 118 | send(p->s, zHdr, got, 0); |
| 119 | } |
| @@ -190,11 +190,11 @@ | |
| 190 | zTempPrefix = mprintf("fossil_server_P%d_", iPort); |
| 191 | printf("Listening for HTTP requests on TCP port %d\n", iPort); |
| 192 | if( zBrowser ){ |
| 193 | zBrowser = mprintf(zBrowser, iPort); |
| 194 | printf("Launch webbrowser: %s\n", zBrowser); |
| 195 | portable_system(zBrowser); |
| 196 | } |
| 197 | printf("Type Ctrl-C to stop the HTTP server\n"); |
| 198 | for(;;){ |
| 199 | SOCKET client; |
| 200 | SOCKADDR_IN client_addr; |
| @@ -207,14 +207,11 @@ | |
| 207 | } |
| 208 | if( client==INVALID_SOCKET ){ |
| 209 | closesocket(s); |
| 210 | fossil_fatal("error from accept()"); |
| 211 | } |
| 212 | p = malloc( sizeof(*p) ); |
| 213 | if( p==0 ){ |
| 214 | fossil_fatal("out of memory"); |
| 215 | } |
| 216 | p->id = ++idCnt; |
| 217 | p->s = client; |
| 218 | p->addr = client_addr; |
| 219 | p->zNotFound = zNotFoundOption; |
| 220 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 221 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -106,14 +106,14 @@ | |
| 106 | wanted -= got; |
| 107 | } |
| 108 | fclose(out); |
| 109 | out = 0; |
| 110 | sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", |
| 111 | _pgmptr, g.zRepositoryName, zRequestFName, zReplyFName, |
| 112 | inet_ntoa(p->addr.sin_addr), p->zNotFound |
| 113 | ); |
| 114 | fossil_system(zCmd); |
| 115 | in = fopen(zReplyFName, "rb"); |
| 116 | if( in ){ |
| 117 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| 118 | send(p->s, zHdr, got, 0); |
| 119 | } |
| @@ -190,11 +190,11 @@ | |
| 190 | zTempPrefix = mprintf("fossil_server_P%d_", iPort); |
| 191 | printf("Listening for HTTP requests on TCP port %d\n", iPort); |
| 192 | if( zBrowser ){ |
| 193 | zBrowser = mprintf(zBrowser, iPort); |
| 194 | printf("Launch webbrowser: %s\n", zBrowser); |
| 195 | fossil_system(zBrowser); |
| 196 | } |
| 197 | printf("Type Ctrl-C to stop the HTTP server\n"); |
| 198 | for(;;){ |
| 199 | SOCKET client; |
| 200 | SOCKADDR_IN client_addr; |
| @@ -207,14 +207,11 @@ | |
| 207 | } |
| 208 | if( client==INVALID_SOCKET ){ |
| 209 | closesocket(s); |
| 210 | fossil_fatal("error from accept()"); |
| 211 | } |
| 212 | p = fossil_malloc( sizeof(*p) ); |
| 213 | p->id = ++idCnt; |
| 214 | p->s = client; |
| 215 | p->addr = client_addr; |
| 216 | p->zNotFound = zNotFoundOption; |
| 217 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 218 |
+130
-42
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -72,11 +72,17 @@ | ||
| 72 | 72 | /* |
| 73 | 73 | ** Remember that the other side of the connection already has a copy |
| 74 | 74 | ** of the file rid. |
| 75 | 75 | */ |
| 76 | 76 | static void remote_has(int rid){ |
| 77 | - if( rid ) db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid); | |
| 77 | + if( rid ){ | |
| 78 | + static Stmt q; | |
| 79 | + db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)"); | |
| 80 | + db_bind_int(&q, ":r", rid); | |
| 81 | + db_step(&q); | |
| 82 | + db_reset(&q); | |
| 83 | + } | |
| 78 | 84 | } |
| 79 | 85 | |
| 80 | 86 | /* |
| 81 | 87 | ** The aToken[0..nToken-1] blob array is a parse of a "file" line |
| 82 | 88 | ** message. This routine finishes parsing that message and does |
| @@ -95,11 +101,11 @@ | ||
| 95 | 101 | ** be initialized to an empty string. |
| 96 | 102 | ** |
| 97 | 103 | ** Any artifact successfully received by this routine is considered to |
| 98 | 104 | ** be public and is therefore removed from the "private" table. |
| 99 | 105 | */ |
| 100 | -static void xfer_accept_file(Xfer *pXfer){ | |
| 106 | +static void xfer_accept_file(Xfer *pXfer, int cloneFlag){ | |
| 101 | 107 | int n; |
| 102 | 108 | int rid; |
| 103 | 109 | int srcid = 0; |
| 104 | 110 | Blob content, hash; |
| 105 | 111 | |
| @@ -114,13 +120,26 @@ | ||
| 114 | 120 | return; |
| 115 | 121 | } |
| 116 | 122 | blob_zero(&content); |
| 117 | 123 | blob_zero(&hash); |
| 118 | 124 | blob_extract(pXfer->pIn, n, &content); |
| 119 | - if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ | |
| 125 | + if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ | |
| 120 | 126 | /* Ignore files that have been shunned */ |
| 121 | 127 | return; |
| 128 | + } | |
| 129 | + if( cloneFlag ){ | |
| 130 | + if( pXfer->nToken==4 ){ | |
| 131 | + srcid = rid_from_uuid(&pXfer->aToken[2], 1); | |
| 132 | + pXfer->nDeltaRcvd++; | |
| 133 | + }else{ | |
| 134 | + srcid = 0; | |
| 135 | + pXfer->nFileRcvd++; | |
| 136 | + } | |
| 137 | + rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); | |
| 138 | + remote_has(rid); | |
| 139 | + blob_reset(&content); | |
| 140 | + return; | |
| 122 | 141 | } |
| 123 | 142 | if( pXfer->nToken==4 ){ |
| 124 | 143 | Blob src, next; |
| 125 | 144 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 126 | 145 | if( content_get(srcid, &src)==0 ){ |
| @@ -467,14 +486,15 @@ | ||
| 467 | 486 | ** Check to see if the number of unclustered entries is greater than |
| 468 | 487 | ** 100 and if it is, form a new cluster. Unclustered phantoms do not |
| 469 | 488 | ** count toward the 100 total. And phantoms are never added to a new |
| 470 | 489 | ** cluster. |
| 471 | 490 | */ |
| 472 | -static void create_cluster(void){ | |
| 491 | +void create_cluster(void){ | |
| 473 | 492 | Blob cluster, cksum; |
| 474 | 493 | Stmt q; |
| 475 | 494 | int nUncl; |
| 495 | + int nRow = 0; | |
| 476 | 496 | |
| 477 | 497 | /* We should not ever get any private artifacts in the unclustered table. |
| 478 | 498 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 479 | 499 | db_multi_exec( |
| 480 | 500 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -481,30 +501,41 @@ | ||
| 481 | 501 | ); |
| 482 | 502 | |
| 483 | 503 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 484 | 504 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 485 | 505 | " WHERE rid=unclustered.rid)"); |
| 486 | - if( nUncl<100 ){ | |
| 487 | - return; | |
| 488 | - } | |
| 489 | - blob_zero(&cluster); | |
| 490 | - db_prepare(&q, "SELECT uuid FROM unclustered, blob" | |
| 491 | - " WHERE NOT EXISTS(SELECT 1 FROM phantom" | |
| 492 | - " WHERE rid=unclustered.rid)" | |
| 493 | - " AND unclustered.rid=blob.rid" | |
| 494 | - " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" | |
| 495 | - " ORDER BY 1"); | |
| 496 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 497 | - blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); | |
| 498 | - } | |
| 499 | - db_finalize(&q); | |
| 500 | - md5sum_blob(&cluster, &cksum); | |
| 501 | - blob_appendf(&cluster, "Z %b\n", &cksum); | |
| 502 | - blob_reset(&cksum); | |
| 503 | - db_multi_exec("DELETE FROM unclustered"); | |
| 504 | - content_put(&cluster, 0, 0); | |
| 505 | - blob_reset(&cluster); | |
| 506 | + if( nUncl>=100 ){ | |
| 507 | + blob_zero(&cluster); | |
| 508 | + db_prepare(&q, "SELECT uuid FROM unclustered, blob" | |
| 509 | + " WHERE NOT EXISTS(SELECT 1 FROM phantom" | |
| 510 | + " WHERE rid=unclustered.rid)" | |
| 511 | + " AND unclustered.rid=blob.rid" | |
| 512 | + " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" | |
| 513 | + " ORDER BY 1"); | |
| 514 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 515 | + blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); | |
| 516 | + nRow++; | |
| 517 | + if( nRow>=800 && nUncl>nRow+100 ){ | |
| 518 | + md5sum_blob(&cluster, &cksum); | |
| 519 | + blob_appendf(&cluster, "Z %b\n", &cksum); | |
| 520 | + blob_reset(&cksum); | |
| 521 | + content_put(&cluster, 0, 0); | |
| 522 | + blob_reset(&cluster); | |
| 523 | + nUncl -= nRow; | |
| 524 | + nRow = 0; | |
| 525 | + } | |
| 526 | + } | |
| 527 | + db_finalize(&q); | |
| 528 | + db_multi_exec("DELETE FROM unclustered"); | |
| 529 | + if( nRow>0 ){ | |
| 530 | + md5sum_blob(&cluster, &cksum); | |
| 531 | + blob_appendf(&cluster, "Z %b\n", &cksum); | |
| 532 | + blob_reset(&cksum); | |
| 533 | + content_put(&cluster, 0, 0); | |
| 534 | + blob_reset(&cluster); | |
| 535 | + } | |
| 536 | + } | |
| 506 | 537 | } |
| 507 | 538 | |
| 508 | 539 | /* |
| 509 | 540 | ** Send an igot message for every entry in unclustered table. |
| 510 | 541 | ** Return the number of cards sent. |
| @@ -606,19 +637,17 @@ | ||
| 606 | 637 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 607 | 638 | cgi_set_content_type(g.zContentType); |
| 608 | 639 | blob_zero(&xfer.err); |
| 609 | 640 | xfer.pIn = &g.cgiIn; |
| 610 | 641 | xfer.pOut = cgi_output_blob(); |
| 611 | - xfer.mxSend = db_get_int("max-download", 5000000); | |
| 642 | + xfer.mxSend = db_get_int("max-download", 20000000); | |
| 612 | 643 | g.xferPanic = 1; |
| 613 | 644 | |
| 614 | 645 | db_begin_transaction(); |
| 615 | 646 | db_multi_exec( |
| 616 | 647 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 617 | 648 | ); |
| 618 | - zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); | |
| 619 | - @ # timestamp %s(zNow) | |
| 620 | 649 | manifest_crosslink_begin(); |
| 621 | 650 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 622 | 651 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 623 | 652 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 624 | 653 | |
| @@ -632,11 +661,11 @@ | ||
| 632 | 661 | cgi_reset_content(); |
| 633 | 662 | @ error not\sauthorized\sto\swrite |
| 634 | 663 | nErr++; |
| 635 | 664 | break; |
| 636 | 665 | } |
| 637 | - xfer_accept_file(&xfer); | |
| 666 | + xfer_accept_file(&xfer, 0); | |
| 638 | 667 | if( blob_size(&xfer.err) ){ |
| 639 | 668 | cgi_reset_content(); |
| 640 | 669 | @ error %T(blob_str(&xfer.err)) |
| 641 | 670 | nErr++; |
| 642 | 671 | break; |
| @@ -718,26 +747,42 @@ | ||
| 718 | 747 | isPush = 1; |
| 719 | 748 | } |
| 720 | 749 | } |
| 721 | 750 | }else |
| 722 | 751 | |
| 723 | - /* clone | |
| 752 | + /* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER? | |
| 724 | 753 | ** |
| 725 | 754 | ** The client knows nothing. Tell all. |
| 726 | 755 | */ |
| 727 | 756 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 757 | + int iVers; | |
| 728 | 758 | login_check_credentials(); |
| 729 | 759 | if( !g.okClone ){ |
| 730 | 760 | cgi_reset_content(); |
| 731 | 761 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 732 | 762 | @ error not\sauthorized\sto\sclone |
| 733 | 763 | nErr++; |
| 734 | 764 | break; |
| 735 | 765 | } |
| 736 | - isClone = 1; | |
| 737 | - isPull = 1; | |
| 738 | - deltaFlag = 1; | |
| 766 | + if( xfer.nToken==3 | |
| 767 | + && blob_is_int(&xfer.aToken[1], &iVers) | |
| 768 | + && iVers>=2 | |
| 769 | + ){ | |
| 770 | + int seqno, max; | |
| 771 | + blob_is_int(&xfer.aToken[2], &seqno); | |
| 772 | + max = db_int(0, "SELECT max(rid) FROM blob"); | |
| 773 | + while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ | |
| 774 | + send_file(&xfer, seqno, 0, 1); | |
| 775 | + seqno++; | |
| 776 | + } | |
| 777 | + if( seqno>=max ) seqno = 0; | |
| 778 | + @ clone_seqno %d(seqno) | |
| 779 | + }else{ | |
| 780 | + isClone = 1; | |
| 781 | + isPull = 1; | |
| 782 | + deltaFlag = 1; | |
| 783 | + } | |
| 739 | 784 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 740 | 785 | }else |
| 741 | 786 | |
| 742 | 787 | /* login USER NONCE SIGNATURE |
| 743 | 788 | ** |
| @@ -874,10 +919,18 @@ | ||
| 874 | 919 | } |
| 875 | 920 | if( recvConfig ){ |
| 876 | 921 | configure_finalize_receive(); |
| 877 | 922 | } |
| 878 | 923 | manifest_crosslink_end(); |
| 924 | + | |
| 925 | + /* Send the server timestamp last, in case prior processing happened | |
| 926 | + ** to use up a significant fraction of our time window. | |
| 927 | + */ | |
| 928 | + zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); | |
| 929 | + @ # timestamp %s(zNow) | |
| 930 | + free(zNow); | |
| 931 | + | |
| 879 | 932 | db_end_transaction(0); |
| 880 | 933 | } |
| 881 | 934 | |
| 882 | 935 | /* |
| 883 | 936 | ** COMMAND: test-xfer |
| @@ -943,13 +996,17 @@ | ||
| 943 | 996 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 944 | 997 | int nFileRecv; /* Number of files received */ |
| 945 | 998 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 946 | 999 | const char *zCookie; /* Server cookie */ |
| 947 | 1000 | int nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 1001 | + int cloneSeqno = 1; /* Sequence number for clones */ | |
| 948 | 1002 | Blob send; /* Text we are sending to the server */ |
| 949 | 1003 | Blob recv; /* Reply we got back from the server */ |
| 950 | 1004 | Xfer xfer; /* Transfer data */ |
| 1005 | + int pctDone; /* Percentage done with a message */ | |
| 1006 | + int lastPctDone = -1; /* Last displayed pctDone */ | |
| 1007 | + double rArrivalTime; /* Time at which a message arrived */ | |
| 951 | 1008 | const char *zSCode = db_get("server-code", "x"); |
| 952 | 1009 | const char *zPCode = db_get("project-code", 0); |
| 953 | 1010 | |
| 954 | 1011 | if( db_get_boolean("dont-push", 0) ) pushFlag = 0; |
| 955 | 1012 | if( pushFlag + pullFlag + cloneFlag == 0 |
| @@ -977,15 +1034,16 @@ | ||
| 977 | 1034 | |
| 978 | 1035 | /* |
| 979 | 1036 | ** Always begin with a clone, pull, or push message |
| 980 | 1037 | */ |
| 981 | 1038 | if( cloneFlag ){ |
| 982 | - blob_appendf(&send, "clone\n"); | |
| 1039 | + blob_appendf(&send, "clone 2 %d\n", cloneSeqno); | |
| 983 | 1040 | pushFlag = 0; |
| 984 | 1041 | pullFlag = 0; |
| 985 | 1042 | nCardSent++; |
| 986 | 1043 | /* TBD: Request all transferable configuration values */ |
| 1044 | + content_enable_dephantomize(0); | |
| 987 | 1045 | }else if( pullFlag ){ |
| 988 | 1046 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 989 | 1047 | nCardSent++; |
| 990 | 1048 | } |
| 991 | 1049 | if( pushFlag ){ |
| @@ -1009,11 +1067,11 @@ | ||
| 1009 | 1067 | } |
| 1010 | 1068 | |
| 1011 | 1069 | /* Generate gimme cards for phantoms and leaf cards |
| 1012 | 1070 | ** for all leaves. |
| 1013 | 1071 | */ |
| 1014 | - if( pullFlag || cloneFlag ){ | |
| 1072 | + if( pullFlag || (cloneFlag && cloneSeqno==1) ){ | |
| 1015 | 1073 | request_phantoms(&xfer, mxPhantomReq); |
| 1016 | 1074 | } |
| 1017 | 1075 | if( pushFlag ){ |
| 1018 | 1076 | send_unsent(&xfer); |
| 1019 | 1077 | nCardSent += send_unclustered(&xfer); |
| @@ -1058,22 +1116,27 @@ | ||
| 1058 | 1116 | blob_appendf(&send, "# %s\n", zRandomness); |
| 1059 | 1117 | free(zRandomness); |
| 1060 | 1118 | |
| 1061 | 1119 | /* Exchange messages with the server */ |
| 1062 | 1120 | nFileSend = xfer.nFileSent + xfer.nDeltaSent; |
| 1063 | - fossil_print(zValueFormat, "Send:", | |
| 1121 | + fossil_print(zValueFormat, "Sent:", | |
| 1064 | 1122 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 1065 | 1123 | xfer.nFileSent, xfer.nDeltaSent); |
| 1066 | 1124 | nCardSent = 0; |
| 1067 | 1125 | nCardRcvd = 0; |
| 1068 | 1126 | xfer.nFileSent = 0; |
| 1069 | 1127 | xfer.nDeltaSent = 0; |
| 1070 | 1128 | xfer.nGimmeSent = 0; |
| 1071 | 1129 | xfer.nIGotSent = 0; |
| 1130 | + if( !g.cgiOutput && !g.fQuiet ){ | |
| 1131 | + printf("waiting for server..."); | |
| 1132 | + } | |
| 1072 | 1133 | fflush(stdout); |
| 1073 | 1134 | http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); |
| 1135 | + lastPctDone = -1; | |
| 1074 | 1136 | blob_reset(&send); |
| 1137 | + rArrivalTime = db_double(0.0, "SELECT julianday('now')"); | |
| 1075 | 1138 | |
| 1076 | 1139 | /* Begin constructing the next message (which might never be |
| 1077 | 1140 | ** sent) by beginning with the pull or push cards |
| 1078 | 1141 | */ |
| 1079 | 1142 | if( pullFlag ){ |
| @@ -1086,18 +1149,22 @@ | ||
| 1086 | 1149 | } |
| 1087 | 1150 | go = 0; |
| 1088 | 1151 | |
| 1089 | 1152 | /* Process the reply that came back from the server */ |
| 1090 | 1153 | while( blob_line(&recv, &xfer.line) ){ |
| 1154 | + if( g.fHttpTrace ){ | |
| 1155 | + printf("\rGOT: %.*s", (int)blob_size(&xfer.line), | |
| 1156 | + blob_buffer(&xfer.line)); | |
| 1157 | + } | |
| 1091 | 1158 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| 1092 | 1159 | const char *zLine = blob_buffer(&xfer.line); |
| 1093 | 1160 | if( memcmp(zLine, "# timestamp ", 12)==0 ){ |
| 1094 | 1161 | char zTime[20]; |
| 1095 | 1162 | double rDiff; |
| 1096 | 1163 | sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); |
| 1097 | - rDiff = db_double(9e99, "SELECT julianday('%q') - julianday('now')", | |
| 1098 | - zTime); | |
| 1164 | + rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", | |
| 1165 | + zTime, rArrivalTime); | |
| 1099 | 1166 | if( rDiff<0.0 ) rDiff = -rDiff; |
| 1100 | 1167 | if( rDiff>9e98 ) rDiff = 0.0; |
| 1101 | 1168 | if( (rDiff*24.0*3600.0)>=60.0 ){ |
| 1102 | 1169 | fossil_warning("*** time skew *** server time differs by %s", |
| 1103 | 1170 | db_timespan_name(rDiff)); |
| @@ -1106,22 +1173,26 @@ | ||
| 1106 | 1173 | } |
| 1107 | 1174 | continue; |
| 1108 | 1175 | } |
| 1109 | 1176 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1110 | 1177 | nCardRcvd++; |
| 1111 | - if( !g.cgiOutput && !g.fQuiet ){ | |
| 1112 | - printf("\r%d", nCardRcvd); | |
| 1113 | - fflush(stdout); | |
| 1178 | + if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){ | |
| 1179 | + pctDone = (recv.iCursor*100)/recv.nUsed; | |
| 1180 | + if( pctDone!=lastPctDone ){ | |
| 1181 | + printf("\rprocessed: %d%% ", pctDone); | |
| 1182 | + lastPctDone = pctDone; | |
| 1183 | + fflush(stdout); | |
| 1184 | + } | |
| 1114 | 1185 | } |
| 1115 | 1186 | |
| 1116 | 1187 | /* file UUID SIZE \n CONTENT |
| 1117 | 1188 | ** file UUID DELTASRC SIZE \n CONTENT |
| 1118 | 1189 | ** |
| 1119 | 1190 | ** Receive a file transmitted from the server. |
| 1120 | 1191 | */ |
| 1121 | 1192 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 1122 | - xfer_accept_file(&xfer); | |
| 1193 | + xfer_accept_file(&xfer, cloneFlag); | |
| 1123 | 1194 | }else |
| 1124 | 1195 | |
| 1125 | 1196 | /* gimme UUID |
| 1126 | 1197 | ** |
| 1127 | 1198 | ** Server is requesting a file. If the file is a manifest, assume |
| @@ -1177,11 +1248,11 @@ | ||
| 1177 | 1248 | } |
| 1178 | 1249 | if( zPCode==0 ){ |
| 1179 | 1250 | zPCode = mprintf("%b", &xfer.aToken[2]); |
| 1180 | 1251 | db_set("project-code", zPCode, 0); |
| 1181 | 1252 | } |
| 1182 | - blob_appendf(&send, "clone\n"); | |
| 1253 | + blob_appendf(&send, "clone 2 %d\n", cloneSeqno); | |
| 1183 | 1254 | nCardSent++; |
| 1184 | 1255 | }else |
| 1185 | 1256 | |
| 1186 | 1257 | /* config NAME SIZE \n CONTENT |
| 1187 | 1258 | ** |
| @@ -1236,10 +1307,21 @@ | ||
| 1236 | 1307 | ** same server. |
| 1237 | 1308 | */ |
| 1238 | 1309 | if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ |
| 1239 | 1310 | db_set("cookie", blob_str(&xfer.aToken[1]), 0); |
| 1240 | 1311 | }else |
| 1312 | + | |
| 1313 | + /* clone_seqno N | |
| 1314 | + ** | |
| 1315 | + ** When doing a clone, the server tries to send all of its artifacts | |
| 1316 | + ** in sequence. This card indicates the sequence number of the next | |
| 1317 | + ** blob that needs to be sent. If N<=0 that indicates that all blobs | |
| 1318 | + ** have been sent. | |
| 1319 | + */ | |
| 1320 | + if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ | |
| 1321 | + blob_is_int(&xfer.aToken[1], &cloneSeqno); | |
| 1322 | + }else | |
| 1241 | 1323 | |
| 1242 | 1324 | /* message MESSAGE |
| 1243 | 1325 | ** |
| 1244 | 1326 | ** Print a message. Similar to "error" but does not stop processing. |
| 1245 | 1327 | ** |
| @@ -1314,10 +1396,12 @@ | ||
| 1314 | 1396 | nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; |
| 1315 | 1397 | if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ |
| 1316 | 1398 | go = 1; |
| 1317 | 1399 | mxPhantomReq = nFileRecv*2; |
| 1318 | 1400 | if( mxPhantomReq<200 ) mxPhantomReq = 200; |
| 1401 | + }else if( cloneFlag && nFileRecv>0 ){ | |
| 1402 | + go = 1; | |
| 1319 | 1403 | } |
| 1320 | 1404 | nCardRcvd = 0; |
| 1321 | 1405 | xfer.nFileRcvd = 0; |
| 1322 | 1406 | xfer.nDeltaRcvd = 0; |
| 1323 | 1407 | xfer.nDanglingFile = 0; |
| @@ -1329,15 +1413,19 @@ | ||
| 1329 | 1413 | go = 1; |
| 1330 | 1414 | } |
| 1331 | 1415 | |
| 1332 | 1416 | /* If this is a clone, the go at least two rounds */ |
| 1333 | 1417 | if( cloneFlag && nCycle==1 ) go = 1; |
| 1418 | + | |
| 1419 | + /* Stop the cycle if the server sends a "clone_seqno 0" card */ | |
| 1420 | + if( cloneSeqno<=0 ) go = 0; | |
| 1334 | 1421 | }; |
| 1335 | 1422 | transport_stats(&nSent, &nRcvd, 1); |
| 1336 | 1423 | fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", |
| 1337 | 1424 | nSent, nRcvd); |
| 1338 | 1425 | transport_close(); |
| 1339 | 1426 | transport_global_shutdown(); |
| 1340 | 1427 | db_multi_exec("DROP TABLE onremote"); |
| 1341 | 1428 | manifest_crosslink_end(); |
| 1429 | + content_enable_dephantomize(1); | |
| 1342 | 1430 | db_end_transaction(0); |
| 1343 | 1431 | } |
| 1344 | 1432 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -72,11 +72,17 @@ | |
| 72 | /* |
| 73 | ** Remember that the other side of the connection already has a copy |
| 74 | ** of the file rid. |
| 75 | */ |
| 76 | static void remote_has(int rid){ |
| 77 | if( rid ) db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid); |
| 78 | } |
| 79 | |
| 80 | /* |
| 81 | ** The aToken[0..nToken-1] blob array is a parse of a "file" line |
| 82 | ** message. This routine finishes parsing that message and does |
| @@ -95,11 +101,11 @@ | |
| 95 | ** be initialized to an empty string. |
| 96 | ** |
| 97 | ** Any artifact successfully received by this routine is considered to |
| 98 | ** be public and is therefore removed from the "private" table. |
| 99 | */ |
| 100 | static void xfer_accept_file(Xfer *pXfer){ |
| 101 | int n; |
| 102 | int rid; |
| 103 | int srcid = 0; |
| 104 | Blob content, hash; |
| 105 | |
| @@ -114,13 +120,26 @@ | |
| 114 | return; |
| 115 | } |
| 116 | blob_zero(&content); |
| 117 | blob_zero(&hash); |
| 118 | blob_extract(pXfer->pIn, n, &content); |
| 119 | if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 120 | /* Ignore files that have been shunned */ |
| 121 | return; |
| 122 | } |
| 123 | if( pXfer->nToken==4 ){ |
| 124 | Blob src, next; |
| 125 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 126 | if( content_get(srcid, &src)==0 ){ |
| @@ -467,14 +486,15 @@ | |
| 467 | ** Check to see if the number of unclustered entries is greater than |
| 468 | ** 100 and if it is, form a new cluster. Unclustered phantoms do not |
| 469 | ** count toward the 100 total. And phantoms are never added to a new |
| 470 | ** cluster. |
| 471 | */ |
| 472 | static void create_cluster(void){ |
| 473 | Blob cluster, cksum; |
| 474 | Stmt q; |
| 475 | int nUncl; |
| 476 | |
| 477 | /* We should not ever get any private artifacts in the unclustered table. |
| 478 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 479 | db_multi_exec( |
| 480 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -481,30 +501,41 @@ | |
| 481 | ); |
| 482 | |
| 483 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 484 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 485 | " WHERE rid=unclustered.rid)"); |
| 486 | if( nUncl<100 ){ |
| 487 | return; |
| 488 | } |
| 489 | blob_zero(&cluster); |
| 490 | db_prepare(&q, "SELECT uuid FROM unclustered, blob" |
| 491 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 492 | " WHERE rid=unclustered.rid)" |
| 493 | " AND unclustered.rid=blob.rid" |
| 494 | " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 495 | " ORDER BY 1"); |
| 496 | while( db_step(&q)==SQLITE_ROW ){ |
| 497 | blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); |
| 498 | } |
| 499 | db_finalize(&q); |
| 500 | md5sum_blob(&cluster, &cksum); |
| 501 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 502 | blob_reset(&cksum); |
| 503 | db_multi_exec("DELETE FROM unclustered"); |
| 504 | content_put(&cluster, 0, 0); |
| 505 | blob_reset(&cluster); |
| 506 | } |
| 507 | |
| 508 | /* |
| 509 | ** Send an igot message for every entry in unclustered table. |
| 510 | ** Return the number of cards sent. |
| @@ -606,19 +637,17 @@ | |
| 606 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 607 | cgi_set_content_type(g.zContentType); |
| 608 | blob_zero(&xfer.err); |
| 609 | xfer.pIn = &g.cgiIn; |
| 610 | xfer.pOut = cgi_output_blob(); |
| 611 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 612 | g.xferPanic = 1; |
| 613 | |
| 614 | db_begin_transaction(); |
| 615 | db_multi_exec( |
| 616 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 617 | ); |
| 618 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 619 | @ # timestamp %s(zNow) |
| 620 | manifest_crosslink_begin(); |
| 621 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 622 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 623 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 624 | |
| @@ -632,11 +661,11 @@ | |
| 632 | cgi_reset_content(); |
| 633 | @ error not\sauthorized\sto\swrite |
| 634 | nErr++; |
| 635 | break; |
| 636 | } |
| 637 | xfer_accept_file(&xfer); |
| 638 | if( blob_size(&xfer.err) ){ |
| 639 | cgi_reset_content(); |
| 640 | @ error %T(blob_str(&xfer.err)) |
| 641 | nErr++; |
| 642 | break; |
| @@ -718,26 +747,42 @@ | |
| 718 | isPush = 1; |
| 719 | } |
| 720 | } |
| 721 | }else |
| 722 | |
| 723 | /* clone |
| 724 | ** |
| 725 | ** The client knows nothing. Tell all. |
| 726 | */ |
| 727 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 728 | login_check_credentials(); |
| 729 | if( !g.okClone ){ |
| 730 | cgi_reset_content(); |
| 731 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 732 | @ error not\sauthorized\sto\sclone |
| 733 | nErr++; |
| 734 | break; |
| 735 | } |
| 736 | isClone = 1; |
| 737 | isPull = 1; |
| 738 | deltaFlag = 1; |
| 739 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 740 | }else |
| 741 | |
| 742 | /* login USER NONCE SIGNATURE |
| 743 | ** |
| @@ -874,10 +919,18 @@ | |
| 874 | } |
| 875 | if( recvConfig ){ |
| 876 | configure_finalize_receive(); |
| 877 | } |
| 878 | manifest_crosslink_end(); |
| 879 | db_end_transaction(0); |
| 880 | } |
| 881 | |
| 882 | /* |
| 883 | ** COMMAND: test-xfer |
| @@ -943,13 +996,17 @@ | |
| 943 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 944 | int nFileRecv; /* Number of files received */ |
| 945 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 946 | const char *zCookie; /* Server cookie */ |
| 947 | int nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 948 | Blob send; /* Text we are sending to the server */ |
| 949 | Blob recv; /* Reply we got back from the server */ |
| 950 | Xfer xfer; /* Transfer data */ |
| 951 | const char *zSCode = db_get("server-code", "x"); |
| 952 | const char *zPCode = db_get("project-code", 0); |
| 953 | |
| 954 | if( db_get_boolean("dont-push", 0) ) pushFlag = 0; |
| 955 | if( pushFlag + pullFlag + cloneFlag == 0 |
| @@ -977,15 +1034,16 @@ | |
| 977 | |
| 978 | /* |
| 979 | ** Always begin with a clone, pull, or push message |
| 980 | */ |
| 981 | if( cloneFlag ){ |
| 982 | blob_appendf(&send, "clone\n"); |
| 983 | pushFlag = 0; |
| 984 | pullFlag = 0; |
| 985 | nCardSent++; |
| 986 | /* TBD: Request all transferable configuration values */ |
| 987 | }else if( pullFlag ){ |
| 988 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 989 | nCardSent++; |
| 990 | } |
| 991 | if( pushFlag ){ |
| @@ -1009,11 +1067,11 @@ | |
| 1009 | } |
| 1010 | |
| 1011 | /* Generate gimme cards for phantoms and leaf cards |
| 1012 | ** for all leaves. |
| 1013 | */ |
| 1014 | if( pullFlag || cloneFlag ){ |
| 1015 | request_phantoms(&xfer, mxPhantomReq); |
| 1016 | } |
| 1017 | if( pushFlag ){ |
| 1018 | send_unsent(&xfer); |
| 1019 | nCardSent += send_unclustered(&xfer); |
| @@ -1058,22 +1116,27 @@ | |
| 1058 | blob_appendf(&send, "# %s\n", zRandomness); |
| 1059 | free(zRandomness); |
| 1060 | |
| 1061 | /* Exchange messages with the server */ |
| 1062 | nFileSend = xfer.nFileSent + xfer.nDeltaSent; |
| 1063 | fossil_print(zValueFormat, "Send:", |
| 1064 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 1065 | xfer.nFileSent, xfer.nDeltaSent); |
| 1066 | nCardSent = 0; |
| 1067 | nCardRcvd = 0; |
| 1068 | xfer.nFileSent = 0; |
| 1069 | xfer.nDeltaSent = 0; |
| 1070 | xfer.nGimmeSent = 0; |
| 1071 | xfer.nIGotSent = 0; |
| 1072 | fflush(stdout); |
| 1073 | http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); |
| 1074 | blob_reset(&send); |
| 1075 | |
| 1076 | /* Begin constructing the next message (which might never be |
| 1077 | ** sent) by beginning with the pull or push cards |
| 1078 | */ |
| 1079 | if( pullFlag ){ |
| @@ -1086,18 +1149,22 @@ | |
| 1086 | } |
| 1087 | go = 0; |
| 1088 | |
| 1089 | /* Process the reply that came back from the server */ |
| 1090 | while( blob_line(&recv, &xfer.line) ){ |
| 1091 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| 1092 | const char *zLine = blob_buffer(&xfer.line); |
| 1093 | if( memcmp(zLine, "# timestamp ", 12)==0 ){ |
| 1094 | char zTime[20]; |
| 1095 | double rDiff; |
| 1096 | sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); |
| 1097 | rDiff = db_double(9e99, "SELECT julianday('%q') - julianday('now')", |
| 1098 | zTime); |
| 1099 | if( rDiff<0.0 ) rDiff = -rDiff; |
| 1100 | if( rDiff>9e98 ) rDiff = 0.0; |
| 1101 | if( (rDiff*24.0*3600.0)>=60.0 ){ |
| 1102 | fossil_warning("*** time skew *** server time differs by %s", |
| 1103 | db_timespan_name(rDiff)); |
| @@ -1106,22 +1173,26 @@ | |
| 1106 | } |
| 1107 | continue; |
| 1108 | } |
| 1109 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1110 | nCardRcvd++; |
| 1111 | if( !g.cgiOutput && !g.fQuiet ){ |
| 1112 | printf("\r%d", nCardRcvd); |
| 1113 | fflush(stdout); |
| 1114 | } |
| 1115 | |
| 1116 | /* file UUID SIZE \n CONTENT |
| 1117 | ** file UUID DELTASRC SIZE \n CONTENT |
| 1118 | ** |
| 1119 | ** Receive a file transmitted from the server. |
| 1120 | */ |
| 1121 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 1122 | xfer_accept_file(&xfer); |
| 1123 | }else |
| 1124 | |
| 1125 | /* gimme UUID |
| 1126 | ** |
| 1127 | ** Server is requesting a file. If the file is a manifest, assume |
| @@ -1177,11 +1248,11 @@ | |
| 1177 | } |
| 1178 | if( zPCode==0 ){ |
| 1179 | zPCode = mprintf("%b", &xfer.aToken[2]); |
| 1180 | db_set("project-code", zPCode, 0); |
| 1181 | } |
| 1182 | blob_appendf(&send, "clone\n"); |
| 1183 | nCardSent++; |
| 1184 | }else |
| 1185 | |
| 1186 | /* config NAME SIZE \n CONTENT |
| 1187 | ** |
| @@ -1236,10 +1307,21 @@ | |
| 1236 | ** same server. |
| 1237 | */ |
| 1238 | if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ |
| 1239 | db_set("cookie", blob_str(&xfer.aToken[1]), 0); |
| 1240 | }else |
| 1241 | |
| 1242 | /* message MESSAGE |
| 1243 | ** |
| 1244 | ** Print a message. Similar to "error" but does not stop processing. |
| 1245 | ** |
| @@ -1314,10 +1396,12 @@ | |
| 1314 | nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; |
| 1315 | if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ |
| 1316 | go = 1; |
| 1317 | mxPhantomReq = nFileRecv*2; |
| 1318 | if( mxPhantomReq<200 ) mxPhantomReq = 200; |
| 1319 | } |
| 1320 | nCardRcvd = 0; |
| 1321 | xfer.nFileRcvd = 0; |
| 1322 | xfer.nDeltaRcvd = 0; |
| 1323 | xfer.nDanglingFile = 0; |
| @@ -1329,15 +1413,19 @@ | |
| 1329 | go = 1; |
| 1330 | } |
| 1331 | |
| 1332 | /* If this is a clone, the go at least two rounds */ |
| 1333 | if( cloneFlag && nCycle==1 ) go = 1; |
| 1334 | }; |
| 1335 | transport_stats(&nSent, &nRcvd, 1); |
| 1336 | fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", |
| 1337 | nSent, nRcvd); |
| 1338 | transport_close(); |
| 1339 | transport_global_shutdown(); |
| 1340 | db_multi_exec("DROP TABLE onremote"); |
| 1341 | manifest_crosslink_end(); |
| 1342 | db_end_transaction(0); |
| 1343 | } |
| 1344 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -72,11 +72,17 @@ | |
| 72 | /* |
| 73 | ** Remember that the other side of the connection already has a copy |
| 74 | ** of the file rid. |
| 75 | */ |
| 76 | static void remote_has(int rid){ |
| 77 | if( rid ){ |
| 78 | static Stmt q; |
| 79 | db_static_prepare(&q, "INSERT OR IGNORE INTO onremote VALUES(:r)"); |
| 80 | db_bind_int(&q, ":r", rid); |
| 81 | db_step(&q); |
| 82 | db_reset(&q); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /* |
| 87 | ** The aToken[0..nToken-1] blob array is a parse of a "file" line |
| 88 | ** message. This routine finishes parsing that message and does |
| @@ -95,11 +101,11 @@ | |
| 101 | ** be initialized to an empty string. |
| 102 | ** |
| 103 | ** Any artifact successfully received by this routine is considered to |
| 104 | ** be public and is therefore removed from the "private" table. |
| 105 | */ |
| 106 | static void xfer_accept_file(Xfer *pXfer, int cloneFlag){ |
| 107 | int n; |
| 108 | int rid; |
| 109 | int srcid = 0; |
| 110 | Blob content, hash; |
| 111 | |
| @@ -114,13 +120,26 @@ | |
| 120 | return; |
| 121 | } |
| 122 | blob_zero(&content); |
| 123 | blob_zero(&hash); |
| 124 | blob_extract(pXfer->pIn, n, &content); |
| 125 | if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 126 | /* Ignore files that have been shunned */ |
| 127 | return; |
| 128 | } |
| 129 | if( cloneFlag ){ |
| 130 | if( pXfer->nToken==4 ){ |
| 131 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 132 | pXfer->nDeltaRcvd++; |
| 133 | }else{ |
| 134 | srcid = 0; |
| 135 | pXfer->nFileRcvd++; |
| 136 | } |
| 137 | rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); |
| 138 | remote_has(rid); |
| 139 | blob_reset(&content); |
| 140 | return; |
| 141 | } |
| 142 | if( pXfer->nToken==4 ){ |
| 143 | Blob src, next; |
| 144 | srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 145 | if( content_get(srcid, &src)==0 ){ |
| @@ -467,14 +486,15 @@ | |
| 486 | ** Check to see if the number of unclustered entries is greater than |
| 487 | ** 100 and if it is, form a new cluster. Unclustered phantoms do not |
| 488 | ** count toward the 100 total. And phantoms are never added to a new |
| 489 | ** cluster. |
| 490 | */ |
| 491 | void create_cluster(void){ |
| 492 | Blob cluster, cksum; |
| 493 | Stmt q; |
| 494 | int nUncl; |
| 495 | int nRow = 0; |
| 496 | |
| 497 | /* We should not ever get any private artifacts in the unclustered table. |
| 498 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 499 | db_multi_exec( |
| 500 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -481,30 +501,41 @@ | |
| 501 | ); |
| 502 | |
| 503 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 504 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 505 | " WHERE rid=unclustered.rid)"); |
| 506 | if( nUncl>=100 ){ |
| 507 | blob_zero(&cluster); |
| 508 | db_prepare(&q, "SELECT uuid FROM unclustered, blob" |
| 509 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 510 | " WHERE rid=unclustered.rid)" |
| 511 | " AND unclustered.rid=blob.rid" |
| 512 | " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 513 | " ORDER BY 1"); |
| 514 | while( db_step(&q)==SQLITE_ROW ){ |
| 515 | blob_appendf(&cluster, "M %s\n", db_column_text(&q, 0)); |
| 516 | nRow++; |
| 517 | if( nRow>=800 && nUncl>nRow+100 ){ |
| 518 | md5sum_blob(&cluster, &cksum); |
| 519 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 520 | blob_reset(&cksum); |
| 521 | content_put(&cluster, 0, 0); |
| 522 | blob_reset(&cluster); |
| 523 | nUncl -= nRow; |
| 524 | nRow = 0; |
| 525 | } |
| 526 | } |
| 527 | db_finalize(&q); |
| 528 | db_multi_exec("DELETE FROM unclustered"); |
| 529 | if( nRow>0 ){ |
| 530 | md5sum_blob(&cluster, &cksum); |
| 531 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 532 | blob_reset(&cksum); |
| 533 | content_put(&cluster, 0, 0); |
| 534 | blob_reset(&cluster); |
| 535 | } |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | /* |
| 540 | ** Send an igot message for every entry in unclustered table. |
| 541 | ** Return the number of cards sent. |
| @@ -606,19 +637,17 @@ | |
| 637 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 638 | cgi_set_content_type(g.zContentType); |
| 639 | blob_zero(&xfer.err); |
| 640 | xfer.pIn = &g.cgiIn; |
| 641 | xfer.pOut = cgi_output_blob(); |
| 642 | xfer.mxSend = db_get_int("max-download", 20000000); |
| 643 | g.xferPanic = 1; |
| 644 | |
| 645 | db_begin_transaction(); |
| 646 | db_multi_exec( |
| 647 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 648 | ); |
| 649 | manifest_crosslink_begin(); |
| 650 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 651 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 652 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 653 | |
| @@ -632,11 +661,11 @@ | |
| 661 | cgi_reset_content(); |
| 662 | @ error not\sauthorized\sto\swrite |
| 663 | nErr++; |
| 664 | break; |
| 665 | } |
| 666 | xfer_accept_file(&xfer, 0); |
| 667 | if( blob_size(&xfer.err) ){ |
| 668 | cgi_reset_content(); |
| 669 | @ error %T(blob_str(&xfer.err)) |
| 670 | nErr++; |
| 671 | break; |
| @@ -718,26 +747,42 @@ | |
| 747 | isPush = 1; |
| 748 | } |
| 749 | } |
| 750 | }else |
| 751 | |
| 752 | /* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER? |
| 753 | ** |
| 754 | ** The client knows nothing. Tell all. |
| 755 | */ |
| 756 | if( blob_eq(&xfer.aToken[0], "clone") ){ |
| 757 | int iVers; |
| 758 | login_check_credentials(); |
| 759 | if( !g.okClone ){ |
| 760 | cgi_reset_content(); |
| 761 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 762 | @ error not\sauthorized\sto\sclone |
| 763 | nErr++; |
| 764 | break; |
| 765 | } |
| 766 | if( xfer.nToken==3 |
| 767 | && blob_is_int(&xfer.aToken[1], &iVers) |
| 768 | && iVers>=2 |
| 769 | ){ |
| 770 | int seqno, max; |
| 771 | blob_is_int(&xfer.aToken[2], &seqno); |
| 772 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 773 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ |
| 774 | send_file(&xfer, seqno, 0, 1); |
| 775 | seqno++; |
| 776 | } |
| 777 | if( seqno>=max ) seqno = 0; |
| 778 | @ clone_seqno %d(seqno) |
| 779 | }else{ |
| 780 | isClone = 1; |
| 781 | isPull = 1; |
| 782 | deltaFlag = 1; |
| 783 | } |
| 784 | @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) |
| 785 | }else |
| 786 | |
| 787 | /* login USER NONCE SIGNATURE |
| 788 | ** |
| @@ -874,10 +919,18 @@ | |
| 919 | } |
| 920 | if( recvConfig ){ |
| 921 | configure_finalize_receive(); |
| 922 | } |
| 923 | manifest_crosslink_end(); |
| 924 | |
| 925 | /* Send the server timestamp last, in case prior processing happened |
| 926 | ** to use up a significant fraction of our time window. |
| 927 | */ |
| 928 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| 929 | @ # timestamp %s(zNow) |
| 930 | free(zNow); |
| 931 | |
| 932 | db_end_transaction(0); |
| 933 | } |
| 934 | |
| 935 | /* |
| 936 | ** COMMAND: test-xfer |
| @@ -943,13 +996,17 @@ | |
| 996 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 997 | int nFileRecv; /* Number of files received */ |
| 998 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 999 | const char *zCookie; /* Server cookie */ |
| 1000 | int nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 1001 | int cloneSeqno = 1; /* Sequence number for clones */ |
| 1002 | Blob send; /* Text we are sending to the server */ |
| 1003 | Blob recv; /* Reply we got back from the server */ |
| 1004 | Xfer xfer; /* Transfer data */ |
| 1005 | int pctDone; /* Percentage done with a message */ |
| 1006 | int lastPctDone = -1; /* Last displayed pctDone */ |
| 1007 | double rArrivalTime; /* Time at which a message arrived */ |
| 1008 | const char *zSCode = db_get("server-code", "x"); |
| 1009 | const char *zPCode = db_get("project-code", 0); |
| 1010 | |
| 1011 | if( db_get_boolean("dont-push", 0) ) pushFlag = 0; |
| 1012 | if( pushFlag + pullFlag + cloneFlag == 0 |
| @@ -977,15 +1034,16 @@ | |
| 1034 | |
| 1035 | /* |
| 1036 | ** Always begin with a clone, pull, or push message |
| 1037 | */ |
| 1038 | if( cloneFlag ){ |
| 1039 | blob_appendf(&send, "clone 2 %d\n", cloneSeqno); |
| 1040 | pushFlag = 0; |
| 1041 | pullFlag = 0; |
| 1042 | nCardSent++; |
| 1043 | /* TBD: Request all transferable configuration values */ |
| 1044 | content_enable_dephantomize(0); |
| 1045 | }else if( pullFlag ){ |
| 1046 | blob_appendf(&send, "pull %s %s\n", zSCode, zPCode); |
| 1047 | nCardSent++; |
| 1048 | } |
| 1049 | if( pushFlag ){ |
| @@ -1009,11 +1067,11 @@ | |
| 1067 | } |
| 1068 | |
| 1069 | /* Generate gimme cards for phantoms and leaf cards |
| 1070 | ** for all leaves. |
| 1071 | */ |
| 1072 | if( pullFlag || (cloneFlag && cloneSeqno==1) ){ |
| 1073 | request_phantoms(&xfer, mxPhantomReq); |
| 1074 | } |
| 1075 | if( pushFlag ){ |
| 1076 | send_unsent(&xfer); |
| 1077 | nCardSent += send_unclustered(&xfer); |
| @@ -1058,22 +1116,27 @@ | |
| 1116 | blob_appendf(&send, "# %s\n", zRandomness); |
| 1117 | free(zRandomness); |
| 1118 | |
| 1119 | /* Exchange messages with the server */ |
| 1120 | nFileSend = xfer.nFileSent + xfer.nDeltaSent; |
| 1121 | fossil_print(zValueFormat, "Sent:", |
| 1122 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 1123 | xfer.nFileSent, xfer.nDeltaSent); |
| 1124 | nCardSent = 0; |
| 1125 | nCardRcvd = 0; |
| 1126 | xfer.nFileSent = 0; |
| 1127 | xfer.nDeltaSent = 0; |
| 1128 | xfer.nGimmeSent = 0; |
| 1129 | xfer.nIGotSent = 0; |
| 1130 | if( !g.cgiOutput && !g.fQuiet ){ |
| 1131 | printf("waiting for server..."); |
| 1132 | } |
| 1133 | fflush(stdout); |
| 1134 | http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); |
| 1135 | lastPctDone = -1; |
| 1136 | blob_reset(&send); |
| 1137 | rArrivalTime = db_double(0.0, "SELECT julianday('now')"); |
| 1138 | |
| 1139 | /* Begin constructing the next message (which might never be |
| 1140 | ** sent) by beginning with the pull or push cards |
| 1141 | */ |
| 1142 | if( pullFlag ){ |
| @@ -1086,18 +1149,22 @@ | |
| 1149 | } |
| 1150 | go = 0; |
| 1151 | |
| 1152 | /* Process the reply that came back from the server */ |
| 1153 | while( blob_line(&recv, &xfer.line) ){ |
| 1154 | if( g.fHttpTrace ){ |
| 1155 | printf("\rGOT: %.*s", (int)blob_size(&xfer.line), |
| 1156 | blob_buffer(&xfer.line)); |
| 1157 | } |
| 1158 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| 1159 | const char *zLine = blob_buffer(&xfer.line); |
| 1160 | if( memcmp(zLine, "# timestamp ", 12)==0 ){ |
| 1161 | char zTime[20]; |
| 1162 | double rDiff; |
| 1163 | sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); |
| 1164 | rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", |
| 1165 | zTime, rArrivalTime); |
| 1166 | if( rDiff<0.0 ) rDiff = -rDiff; |
| 1167 | if( rDiff>9e98 ) rDiff = 0.0; |
| 1168 | if( (rDiff*24.0*3600.0)>=60.0 ){ |
| 1169 | fossil_warning("*** time skew *** server time differs by %s", |
| 1170 | db_timespan_name(rDiff)); |
| @@ -1106,22 +1173,26 @@ | |
| 1173 | } |
| 1174 | continue; |
| 1175 | } |
| 1176 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 1177 | nCardRcvd++; |
| 1178 | if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){ |
| 1179 | pctDone = (recv.iCursor*100)/recv.nUsed; |
| 1180 | if( pctDone!=lastPctDone ){ |
| 1181 | printf("\rprocessed: %d%% ", pctDone); |
| 1182 | lastPctDone = pctDone; |
| 1183 | fflush(stdout); |
| 1184 | } |
| 1185 | } |
| 1186 | |
| 1187 | /* file UUID SIZE \n CONTENT |
| 1188 | ** file UUID DELTASRC SIZE \n CONTENT |
| 1189 | ** |
| 1190 | ** Receive a file transmitted from the server. |
| 1191 | */ |
| 1192 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 1193 | xfer_accept_file(&xfer, cloneFlag); |
| 1194 | }else |
| 1195 | |
| 1196 | /* gimme UUID |
| 1197 | ** |
| 1198 | ** Server is requesting a file. If the file is a manifest, assume |
| @@ -1177,11 +1248,11 @@ | |
| 1248 | } |
| 1249 | if( zPCode==0 ){ |
| 1250 | zPCode = mprintf("%b", &xfer.aToken[2]); |
| 1251 | db_set("project-code", zPCode, 0); |
| 1252 | } |
| 1253 | blob_appendf(&send, "clone 2 %d\n", cloneSeqno); |
| 1254 | nCardSent++; |
| 1255 | }else |
| 1256 | |
| 1257 | /* config NAME SIZE \n CONTENT |
| 1258 | ** |
| @@ -1236,10 +1307,21 @@ | |
| 1307 | ** same server. |
| 1308 | */ |
| 1309 | if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ |
| 1310 | db_set("cookie", blob_str(&xfer.aToken[1]), 0); |
| 1311 | }else |
| 1312 | |
| 1313 | /* clone_seqno N |
| 1314 | ** |
| 1315 | ** When doing a clone, the server tries to send all of its artifacts |
| 1316 | ** in sequence. This card indicates the sequence number of the next |
| 1317 | ** blob that needs to be sent. If N<=0 that indicates that all blobs |
| 1318 | ** have been sent. |
| 1319 | */ |
| 1320 | if( blob_eq(&xfer.aToken[0], "clone_seqno") && xfer.nToken==2 ){ |
| 1321 | blob_is_int(&xfer.aToken[1], &cloneSeqno); |
| 1322 | }else |
| 1323 | |
| 1324 | /* message MESSAGE |
| 1325 | ** |
| 1326 | ** Print a message. Similar to "error" but does not stop processing. |
| 1327 | ** |
| @@ -1314,10 +1396,12 @@ | |
| 1396 | nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile; |
| 1397 | if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){ |
| 1398 | go = 1; |
| 1399 | mxPhantomReq = nFileRecv*2; |
| 1400 | if( mxPhantomReq<200 ) mxPhantomReq = 200; |
| 1401 | }else if( cloneFlag && nFileRecv>0 ){ |
| 1402 | go = 1; |
| 1403 | } |
| 1404 | nCardRcvd = 0; |
| 1405 | xfer.nFileRcvd = 0; |
| 1406 | xfer.nDeltaRcvd = 0; |
| 1407 | xfer.nDanglingFile = 0; |
| @@ -1329,15 +1413,19 @@ | |
| 1413 | go = 1; |
| 1414 | } |
| 1415 | |
| 1416 | /* If this is a clone, the go at least two rounds */ |
| 1417 | if( cloneFlag && nCycle==1 ) go = 1; |
| 1418 | |
| 1419 | /* Stop the cycle if the server sends a "clone_seqno 0" card */ |
| 1420 | if( cloneSeqno<=0 ) go = 0; |
| 1421 | }; |
| 1422 | transport_stats(&nSent, &nRcvd, 1); |
| 1423 | fossil_print("Total network traffic: %d bytes sent, %d bytes received\n", |
| 1424 | nSent, nRcvd); |
| 1425 | transport_close(); |
| 1426 | transport_global_shutdown(); |
| 1427 | db_multi_exec("DROP TABLE onremote"); |
| 1428 | manifest_crosslink_end(); |
| 1429 | content_enable_dephantomize(1); |
| 1430 | db_end_transaction(0); |
| 1431 | } |
| 1432 |
+26
-25
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -102,11 +102,11 @@ | ||
| 102 | 102 | for(j=0; j<nDir; j++){ |
| 103 | 103 | if( strcmp(zName, azDir[j])==0 ) break; |
| 104 | 104 | } |
| 105 | 105 | if( j>=nDir ){ |
| 106 | 106 | nDir++; |
| 107 | - azDir = realloc(azDir, sizeof(azDir[0])*nDir); | |
| 107 | + azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); | |
| 108 | 108 | azDir[j] = mprintf("%s", zName); |
| 109 | 109 | zip_add_file(zName, 0); |
| 110 | 110 | } |
| 111 | 111 | zName[i+1] = c; |
| 112 | 112 | } |
| @@ -313,64 +313,65 @@ | ||
| 313 | 313 | ** politely expands into a subdir instead of filling your current dir |
| 314 | 314 | ** with source files. For example, pass a UUID or "ProjectName". |
| 315 | 315 | ** |
| 316 | 316 | */ |
| 317 | 317 | void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ |
| 318 | - int i; | |
| 319 | - Blob mfile, file, hash; | |
| 320 | - Manifest m; | |
| 318 | + Blob mfile, hash, file; | |
| 319 | + Manifest *pManifest; | |
| 320 | + ManifestFile *pFile; | |
| 321 | 321 | Blob filename; |
| 322 | 322 | int nPrefix; |
| 323 | 323 | |
| 324 | 324 | content_get(rid, &mfile); |
| 325 | 325 | if( blob_size(&mfile)==0 ){ |
| 326 | 326 | blob_zero(pZip); |
| 327 | 327 | return; |
| 328 | 328 | } |
| 329 | - blob_zero(&file); | |
| 330 | 329 | blob_zero(&hash); |
| 331 | - blob_copy(&file, &mfile); | |
| 332 | 330 | blob_zero(&filename); |
| 333 | 331 | zip_open(); |
| 334 | 332 | |
| 335 | 333 | if( zDir && zDir[0] ){ |
| 336 | 334 | blob_appendf(&filename, "%s/", zDir); |
| 337 | 335 | } |
| 338 | 336 | nPrefix = blob_size(&filename); |
| 339 | 337 | |
| 340 | - if( manifest_parse(&m, &mfile) ){ | |
| 338 | + pManifest = manifest_get(rid, CFTYPE_MANIFEST); | |
| 339 | + if( pManifest ){ | |
| 341 | 340 | char *zName; |
| 342 | - zip_set_timedate(m.rDate); | |
| 343 | - blob_append(&filename, "manifest", -1); | |
| 344 | - zName = blob_str(&filename); | |
| 345 | - zip_add_folders(zName); | |
| 346 | - zip_add_file(zName, &file); | |
| 347 | - sha1sum_blob(&file, &hash); | |
| 348 | - blob_reset(&file); | |
| 349 | - blob_append(&hash, "\n", 1); | |
| 350 | - blob_resize(&filename, nPrefix); | |
| 351 | - blob_append(&filename, "manifest.uuid", -1); | |
| 352 | - zName = blob_str(&filename); | |
| 353 | - zip_add_file(zName, &hash); | |
| 354 | - blob_reset(&hash); | |
| 355 | - for(i=0; i<m.nFile; i++){ | |
| 356 | - int fid = uuid_to_rid(m.aFile[i].zUuid, 0); | |
| 341 | + zip_set_timedate(pManifest->rDate); | |
| 342 | + if( db_get_boolean("manifest", 0) ){ | |
| 343 | + blob_append(&filename, "manifest", -1); | |
| 344 | + zName = blob_str(&filename); | |
| 345 | + zip_add_folders(zName); | |
| 346 | + zip_add_file(zName, &mfile); | |
| 347 | + sha1sum_blob(&mfile, &hash); | |
| 348 | + blob_reset(&mfile); | |
| 349 | + blob_append(&hash, "\n", 1); | |
| 350 | + blob_resize(&filename, nPrefix); | |
| 351 | + blob_append(&filename, "manifest.uuid", -1); | |
| 352 | + zName = blob_str(&filename); | |
| 353 | + zip_add_file(zName, &hash); | |
| 354 | + blob_reset(&hash); | |
| 355 | + } | |
| 356 | + manifest_file_rewind(pManifest); | |
| 357 | + while( (pFile = manifest_file_next(pManifest,0))!=0 ){ | |
| 358 | + int fid = uuid_to_rid(pFile->zUuid, 0); | |
| 357 | 359 | if( fid ){ |
| 358 | 360 | content_get(fid, &file); |
| 359 | 361 | blob_resize(&filename, nPrefix); |
| 360 | - blob_append(&filename, m.aFile[i].zName, -1); | |
| 362 | + blob_append(&filename, pFile->zName, -1); | |
| 361 | 363 | zName = blob_str(&filename); |
| 362 | 364 | zip_add_folders(zName); |
| 363 | 365 | zip_add_file(zName, &file); |
| 364 | 366 | blob_reset(&file); |
| 365 | 367 | } |
| 366 | 368 | } |
| 367 | - manifest_clear(&m); | |
| 368 | 369 | }else{ |
| 369 | 370 | blob_reset(&mfile); |
| 370 | - blob_reset(&file); | |
| 371 | 371 | } |
| 372 | + manifest_destroy(pManifest); | |
| 372 | 373 | blob_reset(&filename); |
| 373 | 374 | zip_close(pZip); |
| 374 | 375 | } |
| 375 | 376 | |
| 376 | 377 | /* |
| 377 | 378 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -102,11 +102,11 @@ | |
| 102 | for(j=0; j<nDir; j++){ |
| 103 | if( strcmp(zName, azDir[j])==0 ) break; |
| 104 | } |
| 105 | if( j>=nDir ){ |
| 106 | nDir++; |
| 107 | azDir = realloc(azDir, sizeof(azDir[0])*nDir); |
| 108 | azDir[j] = mprintf("%s", zName); |
| 109 | zip_add_file(zName, 0); |
| 110 | } |
| 111 | zName[i+1] = c; |
| 112 | } |
| @@ -313,64 +313,65 @@ | |
| 313 | ** politely expands into a subdir instead of filling your current dir |
| 314 | ** with source files. For example, pass a UUID or "ProjectName". |
| 315 | ** |
| 316 | */ |
| 317 | void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ |
| 318 | int i; |
| 319 | Blob mfile, file, hash; |
| 320 | Manifest m; |
| 321 | Blob filename; |
| 322 | int nPrefix; |
| 323 | |
| 324 | content_get(rid, &mfile); |
| 325 | if( blob_size(&mfile)==0 ){ |
| 326 | blob_zero(pZip); |
| 327 | return; |
| 328 | } |
| 329 | blob_zero(&file); |
| 330 | blob_zero(&hash); |
| 331 | blob_copy(&file, &mfile); |
| 332 | blob_zero(&filename); |
| 333 | zip_open(); |
| 334 | |
| 335 | if( zDir && zDir[0] ){ |
| 336 | blob_appendf(&filename, "%s/", zDir); |
| 337 | } |
| 338 | nPrefix = blob_size(&filename); |
| 339 | |
| 340 | if( manifest_parse(&m, &mfile) ){ |
| 341 | char *zName; |
| 342 | zip_set_timedate(m.rDate); |
| 343 | blob_append(&filename, "manifest", -1); |
| 344 | zName = blob_str(&filename); |
| 345 | zip_add_folders(zName); |
| 346 | zip_add_file(zName, &file); |
| 347 | sha1sum_blob(&file, &hash); |
| 348 | blob_reset(&file); |
| 349 | blob_append(&hash, "\n", 1); |
| 350 | blob_resize(&filename, nPrefix); |
| 351 | blob_append(&filename, "manifest.uuid", -1); |
| 352 | zName = blob_str(&filename); |
| 353 | zip_add_file(zName, &hash); |
| 354 | blob_reset(&hash); |
| 355 | for(i=0; i<m.nFile; i++){ |
| 356 | int fid = uuid_to_rid(m.aFile[i].zUuid, 0); |
| 357 | if( fid ){ |
| 358 | content_get(fid, &file); |
| 359 | blob_resize(&filename, nPrefix); |
| 360 | blob_append(&filename, m.aFile[i].zName, -1); |
| 361 | zName = blob_str(&filename); |
| 362 | zip_add_folders(zName); |
| 363 | zip_add_file(zName, &file); |
| 364 | blob_reset(&file); |
| 365 | } |
| 366 | } |
| 367 | manifest_clear(&m); |
| 368 | }else{ |
| 369 | blob_reset(&mfile); |
| 370 | blob_reset(&file); |
| 371 | } |
| 372 | blob_reset(&filename); |
| 373 | zip_close(pZip); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -102,11 +102,11 @@ | |
| 102 | for(j=0; j<nDir; j++){ |
| 103 | if( strcmp(zName, azDir[j])==0 ) break; |
| 104 | } |
| 105 | if( j>=nDir ){ |
| 106 | nDir++; |
| 107 | azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); |
| 108 | azDir[j] = mprintf("%s", zName); |
| 109 | zip_add_file(zName, 0); |
| 110 | } |
| 111 | zName[i+1] = c; |
| 112 | } |
| @@ -313,64 +313,65 @@ | |
| 313 | ** politely expands into a subdir instead of filling your current dir |
| 314 | ** with source files. For example, pass a UUID or "ProjectName". |
| 315 | ** |
| 316 | */ |
| 317 | void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ |
| 318 | Blob mfile, hash, file; |
| 319 | Manifest *pManifest; |
| 320 | ManifestFile *pFile; |
| 321 | Blob filename; |
| 322 | int nPrefix; |
| 323 | |
| 324 | content_get(rid, &mfile); |
| 325 | if( blob_size(&mfile)==0 ){ |
| 326 | blob_zero(pZip); |
| 327 | return; |
| 328 | } |
| 329 | blob_zero(&hash); |
| 330 | blob_zero(&filename); |
| 331 | zip_open(); |
| 332 | |
| 333 | if( zDir && zDir[0] ){ |
| 334 | blob_appendf(&filename, "%s/", zDir); |
| 335 | } |
| 336 | nPrefix = blob_size(&filename); |
| 337 | |
| 338 | pManifest = manifest_get(rid, CFTYPE_MANIFEST); |
| 339 | if( pManifest ){ |
| 340 | char *zName; |
| 341 | zip_set_timedate(pManifest->rDate); |
| 342 | if( db_get_boolean("manifest", 0) ){ |
| 343 | blob_append(&filename, "manifest", -1); |
| 344 | zName = blob_str(&filename); |
| 345 | zip_add_folders(zName); |
| 346 | zip_add_file(zName, &mfile); |
| 347 | sha1sum_blob(&mfile, &hash); |
| 348 | blob_reset(&mfile); |
| 349 | blob_append(&hash, "\n", 1); |
| 350 | blob_resize(&filename, nPrefix); |
| 351 | blob_append(&filename, "manifest.uuid", -1); |
| 352 | zName = blob_str(&filename); |
| 353 | zip_add_file(zName, &hash); |
| 354 | blob_reset(&hash); |
| 355 | } |
| 356 | manifest_file_rewind(pManifest); |
| 357 | while( (pFile = manifest_file_next(pManifest,0))!=0 ){ |
| 358 | int fid = uuid_to_rid(pFile->zUuid, 0); |
| 359 | if( fid ){ |
| 360 | content_get(fid, &file); |
| 361 | blob_resize(&filename, nPrefix); |
| 362 | blob_append(&filename, pFile->zName, -1); |
| 363 | zName = blob_str(&filename); |
| 364 | zip_add_folders(zName); |
| 365 | zip_add_file(zName, &file); |
| 366 | blob_reset(&file); |
| 367 | } |
| 368 | } |
| 369 | }else{ |
| 370 | blob_reset(&mfile); |
| 371 | } |
| 372 | manifest_destroy(pManifest); |
| 373 | blob_reset(&filename); |
| 374 | zip_close(pZip); |
| 375 | } |
| 376 | |
| 377 | /* |
| 378 |
+1
-1
| --- test/delta1.test | ||
| +++ test/delta1.test | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | # work properly. |
| 33 | 33 | # |
| 34 | 34 | set filelist [glob $testdir/*] |
| 35 | 35 | foreach f $filelist { |
| 36 | 36 | set base [file root [file tail $f]] |
| 37 | -puts "base=$base f=$f" | |
| 37 | +protOut "base=$base f=$f" | |
| 38 | 38 | set f1 [read_file $f] |
| 39 | 39 | write_file t1 $f1 |
| 40 | 40 | for {set i 0} {$i<100} {incr i} { |
| 41 | 41 | write_file t2 [random_changes $f1 1 1 0 0.1] |
| 42 | 42 | fossil test-delta t1 t2 |
| 43 | 43 |
| --- test/delta1.test | |
| +++ test/delta1.test | |
| @@ -32,11 +32,11 @@ | |
| 32 | # work properly. |
| 33 | # |
| 34 | set filelist [glob $testdir/*] |
| 35 | foreach f $filelist { |
| 36 | set base [file root [file tail $f]] |
| 37 | puts "base=$base f=$f" |
| 38 | set f1 [read_file $f] |
| 39 | write_file t1 $f1 |
| 40 | for {set i 0} {$i<100} {incr i} { |
| 41 | write_file t2 [random_changes $f1 1 1 0 0.1] |
| 42 | fossil test-delta t1 t2 |
| 43 |
| --- test/delta1.test | |
| +++ test/delta1.test | |
| @@ -32,11 +32,11 @@ | |
| 32 | # work properly. |
| 33 | # |
| 34 | set filelist [glob $testdir/*] |
| 35 | foreach f $filelist { |
| 36 | set base [file root [file tail $f]] |
| 37 | protOut "base=$base f=$f" |
| 38 | set f1 [read_file $f] |
| 39 | write_file t1 $f1 |
| 40 | for {set i 0} {$i<100} {incr i} { |
| 41 | write_file t2 [random_changes $f1 1 1 0 0.1] |
| 42 | fossil test-delta t1 t2 |
| 43 |
+12
-12
| --- test/merge1.test | ||
| +++ test/merge1.test | ||
| @@ -77,26 +77,26 @@ | ||
| 77 | 77 | 333 - This is a test of the merging algohm - 3333 |
| 78 | 78 | 444 - If all goes well, we will be pleased - 4444 |
| 79 | 79 | 555 - we think it well and other stuff too - 5555 |
| 80 | 80 | } |
| 81 | 81 | write_file_indented t23 { |
| 82 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 82 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 83 | 83 | 111 - This is line ONE of the demo program - 1111 |
| 84 | 84 | ============================ |
| 85 | 85 | 111 - This is line one OF the demo program - 1111 |
| 86 | - <<<<<<< END MERGE CONFLICT | |
| 86 | + >>>>>>> END MERGE CONFLICT | |
| 87 | 87 | 222 - The second line program line in code - 2222 |
| 88 | 88 | 333 - This is a test of the merging algohm - 3333 |
| 89 | 89 | 444 - If all goes well, we will be pleased - 4444 |
| 90 | 90 | 555 - we think it well and other stuff too - 5555 |
| 91 | 91 | } |
| 92 | 92 | write_file_indented t32 { |
| 93 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 93 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 94 | 94 | 111 - This is line one OF the demo program - 1111 |
| 95 | 95 | ============================ |
| 96 | 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | - <<<<<<< END MERGE CONFLICT | |
| 97 | + >>>>>>> END MERGE CONFLICT | |
| 98 | 98 | 222 - The second line program line in code - 2222 |
| 99 | 99 | 333 - This is a test of the merging algohm - 3333 |
| 100 | 100 | 444 - If all goes well, we will be pleased - 4444 |
| 101 | 101 | 555 - we think it well and other stuff too - 5555 |
| 102 | 102 | } |
| @@ -158,26 +158,26 @@ | ||
| 158 | 158 | 333 - This is a test of the merging algohm - 3333 |
| 159 | 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | 161 | } |
| 162 | 162 | write_file_indented t32 { |
| 163 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 163 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 164 | 164 | ============================ |
| 165 | 165 | 000 - Zero lines added to the beginning of - 0000 |
| 166 | 166 | 111 - This is line one of the demo program - 1111 |
| 167 | - <<<<<<< END MERGE CONFLICT | |
| 167 | + >>>>>>> END MERGE CONFLICT | |
| 168 | 168 | 222 - The second line program line in code - 2222 |
| 169 | 169 | 333 - This is a test of the merging algohm - 3333 |
| 170 | 170 | 444 - If all goes well, we will be pleased - 4444 |
| 171 | 171 | 555 - we think it well and other stuff too - 5555 |
| 172 | 172 | } |
| 173 | 173 | write_file_indented t23 { |
| 174 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 174 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 175 | 175 | 000 - Zero lines added to the beginning of - 0000 |
| 176 | 176 | 111 - This is line one of the demo program - 1111 |
| 177 | 177 | ============================ |
| 178 | - <<<<<<< END MERGE CONFLICT | |
| 178 | + >>>>>>> END MERGE CONFLICT | |
| 179 | 179 | 222 - The second line program line in code - 2222 |
| 180 | 180 | 333 - This is a test of the merging algohm - 3333 |
| 181 | 181 | 444 - If all goes well, we will be pleased - 4444 |
| 182 | 182 | 555 - we think it well and other stuff too - 5555 |
| 183 | 183 | } |
| @@ -293,11 +293,11 @@ | ||
| 293 | 293 | STUV |
| 294 | 294 | XYZ. |
| 295 | 295 | } |
| 296 | 296 | write_file_indented t23 { |
| 297 | 297 | abcd |
| 298 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 298 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 299 | 299 | efgh 2 |
| 300 | 300 | ijkl 2 |
| 301 | 301 | mnop 2 |
| 302 | 302 | qrst |
| 303 | 303 | uvwx |
| @@ -311,11 +311,11 @@ | ||
| 311 | 311 | qrst 3 |
| 312 | 312 | uvwx 3 |
| 313 | 313 | yzAB 3 |
| 314 | 314 | CDEF |
| 315 | 315 | GHIJ |
| 316 | - <<<<<<< END MERGE CONFLICT | |
| 316 | + >>>>>>> END MERGE CONFLICT | |
| 317 | 317 | KLMN |
| 318 | 318 | OPQR |
| 319 | 319 | STUV |
| 320 | 320 | XYZ. |
| 321 | 321 | } |
| @@ -354,11 +354,11 @@ | ||
| 354 | 354 | } |
| 355 | 355 | write_file_indented t23 { |
| 356 | 356 | abcd |
| 357 | 357 | efgh 2 |
| 358 | 358 | ijkl 2 |
| 359 | - >>>>>>> BEGIN MERGE CONFLICT | |
| 359 | + <<<<<<< BEGIN MERGE CONFLICT | |
| 360 | 360 | mnop |
| 361 | 361 | qrst |
| 362 | 362 | uvwx |
| 363 | 363 | yzAB 2 |
| 364 | 364 | CDEF 2 |
| @@ -368,13 +368,13 @@ | ||
| 368 | 368 | qrst 3 |
| 369 | 369 | uvwx 3 |
| 370 | 370 | yzAB 3 |
| 371 | 371 | CDEF |
| 372 | 372 | GHIJ |
| 373 | - <<<<<<< END MERGE CONFLICT | |
| 373 | + >>>>>>> END MERGE CONFLICT | |
| 374 | 374 | KLMN |
| 375 | 375 | OPQR |
| 376 | 376 | STUV |
| 377 | 377 | XYZ. |
| 378 | 378 | } |
| 379 | 379 | fossil test-3 t1 t2 t3 a23 |
| 380 | 380 | test merge1-7.2 {[same_file t23 a23]} |
| 381 | 381 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -77,26 +77,26 @@ | |
| 77 | 333 - This is a test of the merging algohm - 3333 |
| 78 | 444 - If all goes well, we will be pleased - 4444 |
| 79 | 555 - we think it well and other stuff too - 5555 |
| 80 | } |
| 81 | write_file_indented t23 { |
| 82 | >>>>>>> BEGIN MERGE CONFLICT |
| 83 | 111 - This is line ONE of the demo program - 1111 |
| 84 | ============================ |
| 85 | 111 - This is line one OF the demo program - 1111 |
| 86 | <<<<<<< END MERGE CONFLICT |
| 87 | 222 - The second line program line in code - 2222 |
| 88 | 333 - This is a test of the merging algohm - 3333 |
| 89 | 444 - If all goes well, we will be pleased - 4444 |
| 90 | 555 - we think it well and other stuff too - 5555 |
| 91 | } |
| 92 | write_file_indented t32 { |
| 93 | >>>>>>> BEGIN MERGE CONFLICT |
| 94 | 111 - This is line one OF the demo program - 1111 |
| 95 | ============================ |
| 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | <<<<<<< END MERGE CONFLICT |
| 98 | 222 - The second line program line in code - 2222 |
| 99 | 333 - This is a test of the merging algohm - 3333 |
| 100 | 444 - If all goes well, we will be pleased - 4444 |
| 101 | 555 - we think it well and other stuff too - 5555 |
| 102 | } |
| @@ -158,26 +158,26 @@ | |
| 158 | 333 - This is a test of the merging algohm - 3333 |
| 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | } |
| 162 | write_file_indented t32 { |
| 163 | >>>>>>> BEGIN MERGE CONFLICT |
| 164 | ============================ |
| 165 | 000 - Zero lines added to the beginning of - 0000 |
| 166 | 111 - This is line one of the demo program - 1111 |
| 167 | <<<<<<< END MERGE CONFLICT |
| 168 | 222 - The second line program line in code - 2222 |
| 169 | 333 - This is a test of the merging algohm - 3333 |
| 170 | 444 - If all goes well, we will be pleased - 4444 |
| 171 | 555 - we think it well and other stuff too - 5555 |
| 172 | } |
| 173 | write_file_indented t23 { |
| 174 | >>>>>>> BEGIN MERGE CONFLICT |
| 175 | 000 - Zero lines added to the beginning of - 0000 |
| 176 | 111 - This is line one of the demo program - 1111 |
| 177 | ============================ |
| 178 | <<<<<<< END MERGE CONFLICT |
| 179 | 222 - The second line program line in code - 2222 |
| 180 | 333 - This is a test of the merging algohm - 3333 |
| 181 | 444 - If all goes well, we will be pleased - 4444 |
| 182 | 555 - we think it well and other stuff too - 5555 |
| 183 | } |
| @@ -293,11 +293,11 @@ | |
| 293 | STUV |
| 294 | XYZ. |
| 295 | } |
| 296 | write_file_indented t23 { |
| 297 | abcd |
| 298 | >>>>>>> BEGIN MERGE CONFLICT |
| 299 | efgh 2 |
| 300 | ijkl 2 |
| 301 | mnop 2 |
| 302 | qrst |
| 303 | uvwx |
| @@ -311,11 +311,11 @@ | |
| 311 | qrst 3 |
| 312 | uvwx 3 |
| 313 | yzAB 3 |
| 314 | CDEF |
| 315 | GHIJ |
| 316 | <<<<<<< END MERGE CONFLICT |
| 317 | KLMN |
| 318 | OPQR |
| 319 | STUV |
| 320 | XYZ. |
| 321 | } |
| @@ -354,11 +354,11 @@ | |
| 354 | } |
| 355 | write_file_indented t23 { |
| 356 | abcd |
| 357 | efgh 2 |
| 358 | ijkl 2 |
| 359 | >>>>>>> BEGIN MERGE CONFLICT |
| 360 | mnop |
| 361 | qrst |
| 362 | uvwx |
| 363 | yzAB 2 |
| 364 | CDEF 2 |
| @@ -368,13 +368,13 @@ | |
| 368 | qrst 3 |
| 369 | uvwx 3 |
| 370 | yzAB 3 |
| 371 | CDEF |
| 372 | GHIJ |
| 373 | <<<<<<< END MERGE CONFLICT |
| 374 | KLMN |
| 375 | OPQR |
| 376 | STUV |
| 377 | XYZ. |
| 378 | } |
| 379 | fossil test-3 t1 t2 t3 a23 |
| 380 | test merge1-7.2 {[same_file t23 a23]} |
| 381 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -77,26 +77,26 @@ | |
| 77 | 333 - This is a test of the merging algohm - 3333 |
| 78 | 444 - If all goes well, we will be pleased - 4444 |
| 79 | 555 - we think it well and other stuff too - 5555 |
| 80 | } |
| 81 | write_file_indented t23 { |
| 82 | <<<<<<< BEGIN MERGE CONFLICT |
| 83 | 111 - This is line ONE of the demo program - 1111 |
| 84 | ============================ |
| 85 | 111 - This is line one OF the demo program - 1111 |
| 86 | >>>>>>> END MERGE CONFLICT |
| 87 | 222 - The second line program line in code - 2222 |
| 88 | 333 - This is a test of the merging algohm - 3333 |
| 89 | 444 - If all goes well, we will be pleased - 4444 |
| 90 | 555 - we think it well and other stuff too - 5555 |
| 91 | } |
| 92 | write_file_indented t32 { |
| 93 | <<<<<<< BEGIN MERGE CONFLICT |
| 94 | 111 - This is line one OF the demo program - 1111 |
| 95 | ============================ |
| 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | >>>>>>> END MERGE CONFLICT |
| 98 | 222 - The second line program line in code - 2222 |
| 99 | 333 - This is a test of the merging algohm - 3333 |
| 100 | 444 - If all goes well, we will be pleased - 4444 |
| 101 | 555 - we think it well and other stuff too - 5555 |
| 102 | } |
| @@ -158,26 +158,26 @@ | |
| 158 | 333 - This is a test of the merging algohm - 3333 |
| 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | } |
| 162 | write_file_indented t32 { |
| 163 | <<<<<<< BEGIN MERGE CONFLICT |
| 164 | ============================ |
| 165 | 000 - Zero lines added to the beginning of - 0000 |
| 166 | 111 - This is line one of the demo program - 1111 |
| 167 | >>>>>>> END MERGE CONFLICT |
| 168 | 222 - The second line program line in code - 2222 |
| 169 | 333 - This is a test of the merging algohm - 3333 |
| 170 | 444 - If all goes well, we will be pleased - 4444 |
| 171 | 555 - we think it well and other stuff too - 5555 |
| 172 | } |
| 173 | write_file_indented t23 { |
| 174 | <<<<<<< BEGIN MERGE CONFLICT |
| 175 | 000 - Zero lines added to the beginning of - 0000 |
| 176 | 111 - This is line one of the demo program - 1111 |
| 177 | ============================ |
| 178 | >>>>>>> END MERGE CONFLICT |
| 179 | 222 - The second line program line in code - 2222 |
| 180 | 333 - This is a test of the merging algohm - 3333 |
| 181 | 444 - If all goes well, we will be pleased - 4444 |
| 182 | 555 - we think it well and other stuff too - 5555 |
| 183 | } |
| @@ -293,11 +293,11 @@ | |
| 293 | STUV |
| 294 | XYZ. |
| 295 | } |
| 296 | write_file_indented t23 { |
| 297 | abcd |
| 298 | <<<<<<< BEGIN MERGE CONFLICT |
| 299 | efgh 2 |
| 300 | ijkl 2 |
| 301 | mnop 2 |
| 302 | qrst |
| 303 | uvwx |
| @@ -311,11 +311,11 @@ | |
| 311 | qrst 3 |
| 312 | uvwx 3 |
| 313 | yzAB 3 |
| 314 | CDEF |
| 315 | GHIJ |
| 316 | >>>>>>> END MERGE CONFLICT |
| 317 | KLMN |
| 318 | OPQR |
| 319 | STUV |
| 320 | XYZ. |
| 321 | } |
| @@ -354,11 +354,11 @@ | |
| 354 | } |
| 355 | write_file_indented t23 { |
| 356 | abcd |
| 357 | efgh 2 |
| 358 | ijkl 2 |
| 359 | <<<<<<< BEGIN MERGE CONFLICT |
| 360 | mnop |
| 361 | qrst |
| 362 | uvwx |
| 363 | yzAB 2 |
| 364 | CDEF 2 |
| @@ -368,13 +368,13 @@ | |
| 368 | qrst 3 |
| 369 | uvwx 3 |
| 370 | yzAB 3 |
| 371 | CDEF |
| 372 | GHIJ |
| 373 | >>>>>>> END MERGE CONFLICT |
| 374 | KLMN |
| 375 | OPQR |
| 376 | STUV |
| 377 | XYZ. |
| 378 | } |
| 379 | fossil test-3 t1 t2 t3 a23 |
| 380 | test merge1-7.2 {[same_file t23 a23]} |
| 381 |
+4
-4
| --- test/merge3.test | ||
| +++ test/merge3.test | ||
| @@ -28,18 +28,18 @@ | ||
| 28 | 28 | write_file t1 [join [string trim $basis] \n]\n |
| 29 | 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | 32 | set x [read_file t4] |
| 33 | - regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x | |
| 33 | + regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x | |
| 34 | 34 | regsub -all {============================} $x {=} x |
| 35 | - regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x | |
| 35 | + regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x | |
| 36 | 36 | set x [split [string trim $x] \n] |
| 37 | 37 | set result [string trim $result] |
| 38 | 38 | if {$x!=$result} { |
| 39 | - puts " Expected \[$result\]" | |
| 40 | - puts " Got \[$x\]" | |
| 39 | + protOut " Expected \[$result\]" | |
| 40 | + protOut " Got \[$x\]" | |
| 41 | 41 | test merge3-$testid 0 |
| 42 | 42 | } else { |
| 43 | 43 | test merge3-$testid 1 |
| 44 | 44 | } |
| 45 | 45 | } |
| 46 | 46 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -28,18 +28,18 @@ | |
| 28 | write_file t1 [join [string trim $basis] \n]\n |
| 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | set x [read_file t4] |
| 33 | regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x |
| 34 | regsub -all {============================} $x {=} x |
| 35 | regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x |
| 36 | set x [split [string trim $x] \n] |
| 37 | set result [string trim $result] |
| 38 | if {$x!=$result} { |
| 39 | puts " Expected \[$result\]" |
| 40 | puts " Got \[$x\]" |
| 41 | test merge3-$testid 0 |
| 42 | } else { |
| 43 | test merge3-$testid 1 |
| 44 | } |
| 45 | } |
| 46 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -28,18 +28,18 @@ | |
| 28 | write_file t1 [join [string trim $basis] \n]\n |
| 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | set x [read_file t4] |
| 33 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x |
| 34 | regsub -all {============================} $x {=} x |
| 35 | regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x |
| 36 | set x [split [string trim $x] \n] |
| 37 | set result [string trim $result] |
| 38 | if {$x!=$result} { |
| 39 | protOut " Expected \[$result\]" |
| 40 | protOut " Got \[$x\]" |
| 41 | test merge3-$testid 0 |
| 42 | } else { |
| 43 | test merge3-$testid 1 |
| 44 | } |
| 45 | } |
| 46 |
+8
-8
| --- test/merge4.test | ||
| +++ test/merge4.test | ||
| @@ -29,29 +29,29 @@ | ||
| 29 | 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | 32 | fossil test-3-way-merge t1 t3 t2 t5 |
| 33 | 33 | set x [read_file t4] |
| 34 | - regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x | |
| 34 | + regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x | |
| 35 | 35 | regsub -all {============================} $x {=} x |
| 36 | - regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x | |
| 36 | + regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x | |
| 37 | 37 | set x [split [string trim $x] \n] |
| 38 | 38 | set y [read_file t5] |
| 39 | - regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $y {>} y | |
| 39 | + regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $y {>} y | |
| 40 | 40 | regsub -all {============================} $y {=} y |
| 41 | - regsub -all {<<<<<<< END MERGE CONFLICT} $y {<} y | |
| 41 | + regsub -all {>>>>>>> END MERGE CONFLICT} $y {<} y | |
| 42 | 42 | set y [split [string trim $y] \n] |
| 43 | 43 | set result1 [string trim $result1] |
| 44 | 44 | if {$x!=$result1} { |
| 45 | - puts " Expected \[$result1\]" | |
| 46 | - puts " Got \[$x\]" | |
| 45 | + protOut " Expected \[$result1\]" | |
| 46 | + protOut " Got \[$x\]" | |
| 47 | 47 | test merge3-$testid 0 |
| 48 | 48 | } else { |
| 49 | 49 | set result2 [string trim $result2] |
| 50 | 50 | if {$y!=$result2} { |
| 51 | - puts " Expected \[$result2\]" | |
| 52 | - puts " Got \[$y\]" | |
| 51 | + protOut " Expected \[$result2\]" | |
| 52 | + protOut " Got \[$y\]" | |
| 53 | 53 | test merge3-$testid 0 |
| 54 | 54 | } else { |
| 55 | 55 | test merge3-$testid 1 |
| 56 | 56 | } |
| 57 | 57 | } |
| 58 | 58 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -29,29 +29,29 @@ | |
| 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | fossil test-3-way-merge t1 t3 t2 t5 |
| 33 | set x [read_file t4] |
| 34 | regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x |
| 35 | regsub -all {============================} $x {=} x |
| 36 | regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x |
| 37 | set x [split [string trim $x] \n] |
| 38 | set y [read_file t5] |
| 39 | regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $y {>} y |
| 40 | regsub -all {============================} $y {=} y |
| 41 | regsub -all {<<<<<<< END MERGE CONFLICT} $y {<} y |
| 42 | set y [split [string trim $y] \n] |
| 43 | set result1 [string trim $result1] |
| 44 | if {$x!=$result1} { |
| 45 | puts " Expected \[$result1\]" |
| 46 | puts " Got \[$x\]" |
| 47 | test merge3-$testid 0 |
| 48 | } else { |
| 49 | set result2 [string trim $result2] |
| 50 | if {$y!=$result2} { |
| 51 | puts " Expected \[$result2\]" |
| 52 | puts " Got \[$y\]" |
| 53 | test merge3-$testid 0 |
| 54 | } else { |
| 55 | test merge3-$testid 1 |
| 56 | } |
| 57 | } |
| 58 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -29,29 +29,29 @@ | |
| 29 | write_file t2 [join [string trim $v1] \n]\n |
| 30 | write_file t3 [join [string trim $v2] \n]\n |
| 31 | fossil test-3-way-merge t1 t2 t3 t4 |
| 32 | fossil test-3-way-merge t1 t3 t2 t5 |
| 33 | set x [read_file t4] |
| 34 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x |
| 35 | regsub -all {============================} $x {=} x |
| 36 | regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x |
| 37 | set x [split [string trim $x] \n] |
| 38 | set y [read_file t5] |
| 39 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $y {>} y |
| 40 | regsub -all {============================} $y {=} y |
| 41 | regsub -all {>>>>>>> END MERGE CONFLICT} $y {<} y |
| 42 | set y [split [string trim $y] \n] |
| 43 | set result1 [string trim $result1] |
| 44 | if {$x!=$result1} { |
| 45 | protOut " Expected \[$result1\]" |
| 46 | protOut " Got \[$x\]" |
| 47 | test merge3-$testid 0 |
| 48 | } else { |
| 49 | set result2 [string trim $result2] |
| 50 | if {$y!=$result2} { |
| 51 | protOut " Expected \[$result2\]" |
| 52 | protOut " Got \[$y\]" |
| 53 | test merge3-$testid 0 |
| 54 | } else { |
| 55 | test merge3-$testid 1 |
| 56 | } |
| 57 | } |
| 58 |
+38
-5
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -38,27 +38,59 @@ | ||
| 38 | 38 | set HALT 1 |
| 39 | 39 | set argv [lreplace $argv $i $i] |
| 40 | 40 | } else { |
| 41 | 41 | set HALT 0 |
| 42 | 42 | } |
| 43 | + | |
| 44 | +set i [lsearch $argv -prot] | |
| 45 | +if {$i>=0} { | |
| 46 | + set PROT 1 | |
| 47 | + set argv [lreplace $argv $i $i] | |
| 48 | +} else { | |
| 49 | + set PROT 0 | |
| 50 | +} | |
| 43 | 51 | |
| 44 | 52 | if {[llength $argv]==0} { |
| 45 | 53 | foreach f [lsort [glob $testdir/*.test]] { |
| 46 | 54 | set base [file root [file tail $f]] |
| 47 | 55 | lappend argv $base |
| 48 | 56 | } |
| 49 | 57 | } |
| 58 | + | |
| 59 | +# start protocol | |
| 60 | +# | |
| 61 | +proc protInit {cmd} { | |
| 62 | + if {$::PROT} { | |
| 63 | + set out [open "prot" w] | |
| 64 | + fconfigure $out -translation platform | |
| 65 | + puts $out "starting tests with:$cmd" | |
| 66 | + close $out | |
| 67 | + } | |
| 68 | +} | |
| 69 | + | |
| 70 | +# write protocol | |
| 71 | +# | |
| 72 | +proc protOut {msg} { | |
| 73 | + puts "$msg" | |
| 74 | + if {$::PROT} { | |
| 75 | + set out [open "prot" a] | |
| 76 | + fconfigure $out -translation platform | |
| 77 | + puts $out "$msg" | |
| 78 | + close $out | |
| 79 | + } | |
| 80 | +} | |
| 50 | 81 | |
| 51 | 82 | # Run the fossil program |
| 52 | 83 | # |
| 53 | 84 | proc fossil {args} { |
| 54 | 85 | global fossilexe |
| 55 | 86 | set cmd $fossilexe |
| 56 | 87 | foreach a $args { |
| 57 | 88 | lappend cmd $a |
| 58 | 89 | } |
| 59 | - puts $cmd | |
| 90 | + protOut $cmd | |
| 91 | + | |
| 60 | 92 | flush stdout |
| 61 | 93 | set rc [catch {eval exec $cmd} result] |
| 62 | 94 | global RESULT CODE |
| 63 | 95 | set CODE $rc |
| 64 | 96 | set RESULT $result |
| @@ -100,13 +132,13 @@ | ||
| 100 | 132 | # |
| 101 | 133 | proc test {name expr} { |
| 102 | 134 | global bad_test |
| 103 | 135 | set r [uplevel 1 [list expr $expr]] |
| 104 | 136 | if {$r} { |
| 105 | - puts "test $name OK" | |
| 137 | + protOut "test $name OK" | |
| 106 | 138 | } else { |
| 107 | - puts "test $name FAILED!" | |
| 139 | + protOut "test $name FAILED!" | |
| 108 | 140 | lappend bad_test $name |
| 109 | 141 | if {$::HALT} exit |
| 110 | 142 | } |
| 111 | 143 | } |
| 112 | 144 | set bad_test {} |
| @@ -167,10 +199,11 @@ | ||
| 167 | 199 | append out \n$line |
| 168 | 200 | } |
| 169 | 201 | return [string range $out 1 end] |
| 170 | 202 | } |
| 171 | 203 | |
| 204 | +protInit $fossilexe | |
| 172 | 205 | foreach testfile $argv { |
| 173 | - puts "***** $testfile ******" | |
| 206 | + protOut "***** $testfile ******" | |
| 174 | 207 | source $testdir/$testfile.test |
| 175 | 208 | } |
| 176 | -puts "[llength $bad_test] errors: $bad_test" | |
| 209 | +protOut "[llength $bad_test] errors: $bad_test" | |
| 177 | 210 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -38,27 +38,59 @@ | |
| 38 | set HALT 1 |
| 39 | set argv [lreplace $argv $i $i] |
| 40 | } else { |
| 41 | set HALT 0 |
| 42 | } |
| 43 | |
| 44 | if {[llength $argv]==0} { |
| 45 | foreach f [lsort [glob $testdir/*.test]] { |
| 46 | set base [file root [file tail $f]] |
| 47 | lappend argv $base |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | # Run the fossil program |
| 52 | # |
| 53 | proc fossil {args} { |
| 54 | global fossilexe |
| 55 | set cmd $fossilexe |
| 56 | foreach a $args { |
| 57 | lappend cmd $a |
| 58 | } |
| 59 | puts $cmd |
| 60 | flush stdout |
| 61 | set rc [catch {eval exec $cmd} result] |
| 62 | global RESULT CODE |
| 63 | set CODE $rc |
| 64 | set RESULT $result |
| @@ -100,13 +132,13 @@ | |
| 100 | # |
| 101 | proc test {name expr} { |
| 102 | global bad_test |
| 103 | set r [uplevel 1 [list expr $expr]] |
| 104 | if {$r} { |
| 105 | puts "test $name OK" |
| 106 | } else { |
| 107 | puts "test $name FAILED!" |
| 108 | lappend bad_test $name |
| 109 | if {$::HALT} exit |
| 110 | } |
| 111 | } |
| 112 | set bad_test {} |
| @@ -167,10 +199,11 @@ | |
| 167 | append out \n$line |
| 168 | } |
| 169 | return [string range $out 1 end] |
| 170 | } |
| 171 | |
| 172 | foreach testfile $argv { |
| 173 | puts "***** $testfile ******" |
| 174 | source $testdir/$testfile.test |
| 175 | } |
| 176 | puts "[llength $bad_test] errors: $bad_test" |
| 177 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -38,27 +38,59 @@ | |
| 38 | set HALT 1 |
| 39 | set argv [lreplace $argv $i $i] |
| 40 | } else { |
| 41 | set HALT 0 |
| 42 | } |
| 43 | |
| 44 | set i [lsearch $argv -prot] |
| 45 | if {$i>=0} { |
| 46 | set PROT 1 |
| 47 | set argv [lreplace $argv $i $i] |
| 48 | } else { |
| 49 | set PROT 0 |
| 50 | } |
| 51 | |
| 52 | if {[llength $argv]==0} { |
| 53 | foreach f [lsort [glob $testdir/*.test]] { |
| 54 | set base [file root [file tail $f]] |
| 55 | lappend argv $base |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | # start protocol |
| 60 | # |
| 61 | proc protInit {cmd} { |
| 62 | if {$::PROT} { |
| 63 | set out [open "prot" w] |
| 64 | fconfigure $out -translation platform |
| 65 | puts $out "starting tests with:$cmd" |
| 66 | close $out |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | # write protocol |
| 71 | # |
| 72 | proc protOut {msg} { |
| 73 | puts "$msg" |
| 74 | if {$::PROT} { |
| 75 | set out [open "prot" a] |
| 76 | fconfigure $out -translation platform |
| 77 | puts $out "$msg" |
| 78 | close $out |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | # Run the fossil program |
| 83 | # |
| 84 | proc fossil {args} { |
| 85 | global fossilexe |
| 86 | set cmd $fossilexe |
| 87 | foreach a $args { |
| 88 | lappend cmd $a |
| 89 | } |
| 90 | protOut $cmd |
| 91 | |
| 92 | flush stdout |
| 93 | set rc [catch {eval exec $cmd} result] |
| 94 | global RESULT CODE |
| 95 | set CODE $rc |
| 96 | set RESULT $result |
| @@ -100,13 +132,13 @@ | |
| 132 | # |
| 133 | proc test {name expr} { |
| 134 | global bad_test |
| 135 | set r [uplevel 1 [list expr $expr]] |
| 136 | if {$r} { |
| 137 | protOut "test $name OK" |
| 138 | } else { |
| 139 | protOut "test $name FAILED!" |
| 140 | lappend bad_test $name |
| 141 | if {$::HALT} exit |
| 142 | } |
| 143 | } |
| 144 | set bad_test {} |
| @@ -167,10 +199,11 @@ | |
| 199 | append out \n$line |
| 200 | } |
| 201 | return [string range $out 1 end] |
| 202 | } |
| 203 | |
| 204 | protInit $fossilexe |
| 205 | foreach testfile $argv { |
| 206 | protOut "***** $testfile ******" |
| 207 | source $testdir/$testfile.test |
| 208 | } |
| 209 | protOut "[llength $bad_test] errors: $bad_test" |
| 210 |
+69
-20
| --- win/Makefile.PellesCGMake | ||
| +++ win/Makefile.PellesCGMake | ||
| @@ -44,73 +44,111 @@ | ||
| 44 | 44 | TARGETMACHINE_CC=x86 |
| 45 | 45 | TARGETMACHINE_LN=ix86 |
| 46 | 46 | TARGETEXTEND= |
| 47 | 47 | endif |
| 48 | 48 | |
| 49 | +# define the project directories | |
| 49 | 50 | B=.. |
| 50 | 51 | SRCDIR=$(B)/src/ |
| 51 | 52 | WINDIR=$(B)/win/ |
| 52 | -ZLIBSRCDIR=E:/fossil-w32/zlib/ | |
| 53 | +ZLIBSRCDIR=../../zlib/ | |
| 53 | 54 | |
| 55 | +# define linker command and options | |
| 54 | 56 | LINK=$(PellesCDir)/bin/polink.exe |
| 55 | 57 | LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib |
| 56 | 58 | |
| 59 | +# define standard C-compiler and flags, used to compile | |
| 60 | +# the fossil binary. Some special definitions follow for | |
| 61 | +# special files follow | |
| 57 | 62 | CC=$(PellesCDir)\bin\pocc.exe |
| 58 | 63 | DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp |
| 59 | 64 | CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) |
| 60 | 65 | INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR) |
| 61 | 66 | |
| 67 | +# define commands for building the windows resource files | |
| 68 | +RESOURCE=fossil.res | |
| 69 | +RC=$(PellesCDir)\bin\porc.exe | |
| 70 | +RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) | |
| 71 | + | |
| 72 | +# define the special utilities files, needed to generate | |
| 73 | +# the automatically generated source files | |
| 62 | 74 | UTILS=translate.exe mkindex.exe makeheaders.exe |
| 63 | 75 | UTILS_OBJ=$(UTILS:.exe=.obj) |
| 76 | +UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c)) | |
| 64 | 77 | |
| 65 | -SRC=add.c allrepo.c attach.c bag.c blob.c branch.c browse.c captcha.c cgi.c checkin.c checkout.c clearsign.c clone.c comformat.c configure.c content.c db.c delta.c deltacmd.c descendants.c diff.c diffcmd.c doc.c encode.c event.c file.c finfo.c graph.c http.c http_socket.c http_ssl.c http_transport.c info.c login.c main.c manifest.c md5.c merge.c merge3.c name.c pivot.c popen.c pqueue.c printf.c rebuild.c report.c rss.c schema.c search.c setup.c sha1.c shun.c skins.c stat.c style.c sync.c tag.c th_main.c timeline.c tkt.c tktsetup.c undo.c update.c url.c user.c verify.c vfile.c wiki.c wikiformat.c winhttp.c xfer.c zip.c | |
| 66 | -ORIGSRC=$(foreach sf,$(SRC),$(SRCDIR)$(sf)) | |
| 67 | -TRANSLATEDSRC=$(SRC:.c=_.c) | |
| 68 | -TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) | |
| 69 | - | |
| 78 | +# define the sqlite files, which need special flags on compile | |
| 70 | 79 | SQLITESRC=sqlite3.c |
| 71 | 80 | ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) |
| 72 | 81 | SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) |
| 73 | 82 | SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 |
| 74 | 83 | |
| 84 | +# define the th scripting files, which need special flags on compile | |
| 75 | 85 | THSRC=th.c th_lang.c |
| 76 | 86 | ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) |
| 77 | 87 | THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) |
| 78 | 88 | |
| 89 | +# define the zlib files, needed by this compile | |
| 79 | 90 | ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c |
| 80 | 91 | ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf)) |
| 81 | 92 | ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj)) |
| 82 | 93 | |
| 94 | +# define all fossil sources, using the standard compile and | |
| 95 | +# source generation. These are all files in SRCDIR, which are not | |
| 96 | +# mentioned as special files above: | |
| 97 | +ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC),$(wildcard $(SRCDIR)*.c)) | |
| 98 | +SRC=$(subst $(SRCDIR),,$(ORIGSRC)) | |
| 99 | +TRANSLATEDSRC=$(SRC:.c=_.c) | |
| 100 | +TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) | |
| 101 | + | |
| 102 | +# main target file is the application | |
| 83 | 103 | APPLICATION=fossil.exe |
| 84 | 104 | |
| 105 | +# ########################################################################### | |
| 106 | +# define the standard make target | |
| 85 | 107 | .PHONY: default |
| 86 | 108 | default: page_index.h headers $(APPLICATION) |
| 87 | 109 | |
| 88 | -$(UTILS): %.exe: %.obj | |
| 110 | +# ########################################################################### | |
| 111 | +# symbolic target to generate the source generate utils | |
| 112 | +.PHONY: utils | |
| 113 | +utils: $(UTILS) | |
| 114 | + | |
| 115 | +# link utils | |
| 116 | +$(UTILS) version.exe: %.exe: %.obj | |
| 89 | 117 | $(LINK) $(LINKFLAGS) -out:"$@" $< |
| 90 | 118 | |
| 119 | +# compiling standard fossil utils | |
| 91 | 120 | $(UTILS_OBJ): %.obj: $(SRCDIR)%.c |
| 92 | 121 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 93 | 122 | |
| 94 | -$(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe | |
| 95 | - translate.exe $< >$@ | |
| 96 | - | |
| 97 | -page_index.h: $(TRANSLATEDSRC) mkindex.exe | |
| 98 | - mkindex.exe $(TRANSLATEDSRC) >$@ | |
| 99 | - | |
| 100 | -version.exe: version.obj | |
| 101 | - $(LINK) $(LINKFLAGS) -out:"$@" $< | |
| 102 | - | |
| 123 | +# compile special windows utils | |
| 103 | 124 | version.obj: $(WINDIR)version.c |
| 104 | 125 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 105 | 126 | |
| 127 | +# ########################################################################### | |
| 128 | +# generate the translated c-source files | |
| 129 | +$(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe | |
| 130 | + translate.exe $< >$@ | |
| 131 | + | |
| 132 | +# ########################################################################### | |
| 133 | +# generate the index source, containing all web references,.. | |
| 134 | +page_index.h: $(TRANSLATEDSRC) mkindex.exe | |
| 135 | + mkindex.exe $(TRANSLATEDSRC) >$@ | |
| 136 | + | |
| 137 | +# ########################################################################### | |
| 138 | +# extracting version info from manifest | |
| 106 | 139 | VERSION.h: version.exe ..\manifest.uuid ..\manifest |
| 107 | 140 | version.exe ..\manifest.uuid ..\manifest > $@ |
| 108 | 141 | |
| 142 | +# ########################################################################### | |
| 143 | +# generate the simplified headers | |
| 109 | 144 | headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h |
| 110 | 145 | makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h |
| 111 | 146 | echo Done >$@ |
| 147 | + | |
| 148 | +# ########################################################################### | |
| 149 | +# compile C sources with relevant options | |
| 112 | 150 | |
| 113 | 151 | $(TRANSLATEDOBJ): %_.obj: %_.c %.h |
| 114 | 152 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 115 | 153 | |
| 116 | 154 | $(SQLITEOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)%.h |
| @@ -120,18 +158,29 @@ | ||
| 120 | 158 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 121 | 159 | |
| 122 | 160 | $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c |
| 123 | 161 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 124 | 162 | |
| 125 | -$(APPLICATION): $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers | |
| 126 | - $(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) | |
| 163 | +# ########################################################################### | |
| 164 | +# create the windows resource with icon and version info | |
| 165 | +$(RESOURCE): %.res: ../win/%.rc ../win/*.ico | |
| 166 | + $(RC) $(RCFLAGS) $< -Fo"$@" | |
| 167 | + | |
| 168 | +# ########################################################################### | |
| 169 | +# link the application | |
| 170 | +$(APPLICATION): $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE) | |
| 171 | + $(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE) | |
| 172 | + | |
| 173 | +# ########################################################################### | |
| 174 | +# cleanup | |
| 127 | 175 | |
| 128 | 176 | .PHONY: clean |
| 129 | 177 | clean: |
| 130 | - del /F *.obj | |
| 131 | - del /F *.c | |
| 178 | + del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj | |
| 179 | + del /F $(TRANSLATEDSRC) | |
| 132 | 180 | del /F *.h headers |
| 181 | + del /F $(RESOURCE) | |
| 133 | 182 | |
| 134 | 183 | .PHONY: clobber |
| 135 | 184 | clobber: clean |
| 136 | 185 | del /F *.exe |
| 137 | 186 | |
| 138 | 187 |
| --- win/Makefile.PellesCGMake | |
| +++ win/Makefile.PellesCGMake | |
| @@ -44,73 +44,111 @@ | |
| 44 | TARGETMACHINE_CC=x86 |
| 45 | TARGETMACHINE_LN=ix86 |
| 46 | TARGETEXTEND= |
| 47 | endif |
| 48 | |
| 49 | B=.. |
| 50 | SRCDIR=$(B)/src/ |
| 51 | WINDIR=$(B)/win/ |
| 52 | ZLIBSRCDIR=E:/fossil-w32/zlib/ |
| 53 | |
| 54 | LINK=$(PellesCDir)/bin/polink.exe |
| 55 | LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib |
| 56 | |
| 57 | CC=$(PellesCDir)\bin\pocc.exe |
| 58 | DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp |
| 59 | CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) |
| 60 | INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR) |
| 61 | |
| 62 | UTILS=translate.exe mkindex.exe makeheaders.exe |
| 63 | UTILS_OBJ=$(UTILS:.exe=.obj) |
| 64 | |
| 65 | SRC=add.c allrepo.c attach.c bag.c blob.c branch.c browse.c captcha.c cgi.c checkin.c checkout.c clearsign.c clone.c comformat.c configure.c content.c db.c delta.c deltacmd.c descendants.c diff.c diffcmd.c doc.c encode.c event.c file.c finfo.c graph.c http.c http_socket.c http_ssl.c http_transport.c info.c login.c main.c manifest.c md5.c merge.c merge3.c name.c pivot.c popen.c pqueue.c printf.c rebuild.c report.c rss.c schema.c search.c setup.c sha1.c shun.c skins.c stat.c style.c sync.c tag.c th_main.c timeline.c tkt.c tktsetup.c undo.c update.c url.c user.c verify.c vfile.c wiki.c wikiformat.c winhttp.c xfer.c zip.c |
| 66 | ORIGSRC=$(foreach sf,$(SRC),$(SRCDIR)$(sf)) |
| 67 | TRANSLATEDSRC=$(SRC:.c=_.c) |
| 68 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) |
| 69 | |
| 70 | SQLITESRC=sqlite3.c |
| 71 | ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) |
| 72 | SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) |
| 73 | SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 |
| 74 | |
| 75 | THSRC=th.c th_lang.c |
| 76 | ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) |
| 77 | THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) |
| 78 | |
| 79 | ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c |
| 80 | ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf)) |
| 81 | ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj)) |
| 82 | |
| 83 | APPLICATION=fossil.exe |
| 84 | |
| 85 | .PHONY: default |
| 86 | default: page_index.h headers $(APPLICATION) |
| 87 | |
| 88 | $(UTILS): %.exe: %.obj |
| 89 | $(LINK) $(LINKFLAGS) -out:"$@" $< |
| 90 | |
| 91 | $(UTILS_OBJ): %.obj: $(SRCDIR)%.c |
| 92 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 93 | |
| 94 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe |
| 95 | translate.exe $< >$@ |
| 96 | |
| 97 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 98 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 99 | |
| 100 | version.exe: version.obj |
| 101 | $(LINK) $(LINKFLAGS) -out:"$@" $< |
| 102 | |
| 103 | version.obj: $(WINDIR)version.c |
| 104 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 105 | |
| 106 | VERSION.h: version.exe ..\manifest.uuid ..\manifest |
| 107 | version.exe ..\manifest.uuid ..\manifest > $@ |
| 108 | |
| 109 | headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h |
| 110 | makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h |
| 111 | echo Done >$@ |
| 112 | |
| 113 | $(TRANSLATEDOBJ): %_.obj: %_.c %.h |
| 114 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 115 | |
| 116 | $(SQLITEOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)%.h |
| @@ -120,18 +158,29 @@ | |
| 120 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 121 | |
| 122 | $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c |
| 123 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 124 | |
| 125 | $(APPLICATION): $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers |
| 126 | $(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) |
| 127 | |
| 128 | .PHONY: clean |
| 129 | clean: |
| 130 | del /F *.obj |
| 131 | del /F *.c |
| 132 | del /F *.h headers |
| 133 | |
| 134 | .PHONY: clobber |
| 135 | clobber: clean |
| 136 | del /F *.exe |
| 137 | |
| 138 |
| --- win/Makefile.PellesCGMake | |
| +++ win/Makefile.PellesCGMake | |
| @@ -44,73 +44,111 @@ | |
| 44 | TARGETMACHINE_CC=x86 |
| 45 | TARGETMACHINE_LN=ix86 |
| 46 | TARGETEXTEND= |
| 47 | endif |
| 48 | |
| 49 | # define the project directories |
| 50 | B=.. |
| 51 | SRCDIR=$(B)/src/ |
| 52 | WINDIR=$(B)/win/ |
| 53 | ZLIBSRCDIR=../../zlib/ |
| 54 | |
| 55 | # define linker command and options |
| 56 | LINK=$(PellesCDir)/bin/polink.exe |
| 57 | LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib Crtmt$(TARGETEXTEND).lib |
| 58 | |
| 59 | # define standard C-compiler and flags, used to compile |
| 60 | # the fossil binary. Some special definitions follow for |
| 61 | # special files follow |
| 62 | CC=$(PellesCDir)\bin\pocc.exe |
| 63 | DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp |
| 64 | CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) |
| 65 | INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR) |
| 66 | |
| 67 | # define commands for building the windows resource files |
| 68 | RESOURCE=fossil.res |
| 69 | RC=$(PellesCDir)\bin\porc.exe |
| 70 | RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) |
| 71 | |
| 72 | # define the special utilities files, needed to generate |
| 73 | # the automatically generated source files |
| 74 | UTILS=translate.exe mkindex.exe makeheaders.exe |
| 75 | UTILS_OBJ=$(UTILS:.exe=.obj) |
| 76 | UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c)) |
| 77 | |
| 78 | # define the sqlite files, which need special flags on compile |
| 79 | SQLITESRC=sqlite3.c |
| 80 | ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) |
| 81 | SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) |
| 82 | SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 |
| 83 | |
| 84 | # define the th scripting files, which need special flags on compile |
| 85 | THSRC=th.c th_lang.c |
| 86 | ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) |
| 87 | THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) |
| 88 | |
| 89 | # define the zlib files, needed by this compile |
| 90 | ZLIBSRC=adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c |
| 91 | ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf)) |
| 92 | ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj)) |
| 93 | |
| 94 | # define all fossil sources, using the standard compile and |
| 95 | # source generation. These are all files in SRCDIR, which are not |
| 96 | # mentioned as special files above: |
| 97 | ORIGSRC=$(filter-out $(UTILS_SRC) $(ORIGTHSRC) $(ORIGSQLITESRC),$(wildcard $(SRCDIR)*.c)) |
| 98 | SRC=$(subst $(SRCDIR),,$(ORIGSRC)) |
| 99 | TRANSLATEDSRC=$(SRC:.c=_.c) |
| 100 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) |
| 101 | |
| 102 | # main target file is the application |
| 103 | APPLICATION=fossil.exe |
| 104 | |
| 105 | # ########################################################################### |
| 106 | # define the standard make target |
| 107 | .PHONY: default |
| 108 | default: page_index.h headers $(APPLICATION) |
| 109 | |
| 110 | # ########################################################################### |
| 111 | # symbolic target to generate the source generate utils |
| 112 | .PHONY: utils |
| 113 | utils: $(UTILS) |
| 114 | |
| 115 | # link utils |
| 116 | $(UTILS) version.exe: %.exe: %.obj |
| 117 | $(LINK) $(LINKFLAGS) -out:"$@" $< |
| 118 | |
| 119 | # compiling standard fossil utils |
| 120 | $(UTILS_OBJ): %.obj: $(SRCDIR)%.c |
| 121 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 122 | |
| 123 | # compile special windows utils |
| 124 | version.obj: $(WINDIR)version.c |
| 125 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 126 | |
| 127 | # ########################################################################### |
| 128 | # generate the translated c-source files |
| 129 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe |
| 130 | translate.exe $< >$@ |
| 131 | |
| 132 | # ########################################################################### |
| 133 | # generate the index source, containing all web references,.. |
| 134 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 135 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 136 | |
| 137 | # ########################################################################### |
| 138 | # extracting version info from manifest |
| 139 | VERSION.h: version.exe ..\manifest.uuid ..\manifest |
| 140 | version.exe ..\manifest.uuid ..\manifest > $@ |
| 141 | |
| 142 | # ########################################################################### |
| 143 | # generate the simplified headers |
| 144 | headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h |
| 145 | makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h |
| 146 | echo Done >$@ |
| 147 | |
| 148 | # ########################################################################### |
| 149 | # compile C sources with relevant options |
| 150 | |
| 151 | $(TRANSLATEDOBJ): %_.obj: %_.c %.h |
| 152 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 153 | |
| 154 | $(SQLITEOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)%.h |
| @@ -120,18 +158,29 @@ | |
| 158 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 159 | |
| 160 | $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c |
| 161 | $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
| 162 | |
| 163 | # ########################################################################### |
| 164 | # create the windows resource with icon and version info |
| 165 | $(RESOURCE): %.res: ../win/%.rc ../win/*.ico |
| 166 | $(RC) $(RCFLAGS) $< -Fo"$@" |
| 167 | |
| 168 | # ########################################################################### |
| 169 | # link the application |
| 170 | $(APPLICATION): $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE) |
| 171 | $(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE) |
| 172 | |
| 173 | # ########################################################################### |
| 174 | # cleanup |
| 175 | |
| 176 | .PHONY: clean |
| 177 | clean: |
| 178 | del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj |
| 179 | del /F $(TRANSLATEDSRC) |
| 180 | del /F *.h headers |
| 181 | del /F $(RESOURCE) |
| 182 | |
| 183 | .PHONY: clobber |
| 184 | clobber: clean |
| 185 | del /F *.exe |
| 186 | |
| 187 |
+27
-5
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -24,28 +24,35 @@ | ||
| 24 | 24 | CFLAGS = -o |
| 25 | 25 | BCC = $(DMDIR)\bin\dmc $(CFLAGS) |
| 26 | 26 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) |
| 27 | 27 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 |
| 28 | 28 | |
| 29 | -SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c | |
| 29 | +SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c | |
| 30 | 30 | |
| 31 | -OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 31 | +OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\shell$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 32 | 32 | |
| 33 | +RC=$(DMDIR)\bin\rcc | |
| 34 | +RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ | |
| 33 | 35 | |
| 34 | 36 | APPNAME = $(OBJDIR)\fossil$(E) |
| 35 | 37 | |
| 36 | 38 | all: $(APPNAME) |
| 37 | 39 | |
| 38 | -$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link | |
| 40 | +$(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link | |
| 39 | 41 | cd $(OBJDIR) |
| 40 | 42 | $(DMDIR)\bin\link @link |
| 43 | + | |
| 44 | +fossil.res: $B\win\fossil.rc | |
| 45 | + $(RC) $(RCFLAGS) -o$@ $** | |
| 41 | 46 | |
| 42 | 47 | $(OBJDIR)\link: $B\win\Makefile.dmc |
| 43 | - +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ | |
| 48 | + +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 shell th th_lang > $@ | |
| 44 | 49 | +echo fossil >> $@ |
| 45 | 50 | +echo fossil >> $@ |
| 46 | 51 | +echo $(LIBS) >> $@ |
| 52 | + +echo. >> $@ | |
| 53 | + +echo fossil >> $@ | |
| 47 | 54 | |
| 48 | 55 | |
| 49 | 56 | |
| 50 | 57 | translate$E: $(SRCDIR)\translate.c |
| 51 | 58 | $(BCC) -o$@ $** |
| @@ -57,10 +64,13 @@ | ||
| 57 | 64 | $(BCC) -o$@ $** |
| 58 | 65 | |
| 59 | 66 | version$E: $B\win\version.c |
| 60 | 67 | $(BCC) -o$@ $** |
| 61 | 68 | |
| 69 | +$(OBJDIR)\shell$O : $(SRCDIR)\shell.c | |
| 70 | + $(TCC) -o$@ -c -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 $** | |
| 71 | + | |
| 62 | 72 | $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c |
| 63 | 73 | $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** |
| 64 | 74 | |
| 65 | 75 | $(OBJDIR)\th$O : $(SRCDIR)\th.c |
| 66 | 76 | $(TCC) -o$@ -c $** |
| @@ -217,16 +227,28 @@ | ||
| 217 | 227 | $(OBJDIR)\doc$O : doc_.c doc.h |
| 218 | 228 | $(TCC) -o$@ -c doc_.c |
| 219 | 229 | |
| 220 | 230 | doc_.c : $(SRCDIR)\doc.c |
| 221 | 231 | +translate$E $** > $@ |
| 232 | + | |
| 233 | +$(OBJDIR)\event$O : event_.c event.h | |
| 234 | + $(TCC) -o$@ -c event_.c | |
| 235 | + | |
| 236 | +event_.c : $(SRCDIR)\event.c | |
| 237 | + +translate$E $** > $@ | |
| 222 | 238 | |
| 223 | 239 | $(OBJDIR)\encode$O : encode_.c encode.h |
| 224 | 240 | $(TCC) -o$@ -c encode_.c |
| 225 | 241 | |
| 226 | 242 | encode_.c : $(SRCDIR)\encode.c |
| 227 | 243 | +translate$E $** > $@ |
| 244 | + | |
| 245 | +$(OBJDIR)\event$O : event_.c event.h | |
| 246 | + $(TCC) -o$@ -c event_.c | |
| 247 | + | |
| 248 | +event_.c : $(SRCDIR)\event.c | |
| 249 | + +translate$E $** > $@ | |
| 228 | 250 | |
| 229 | 251 | $(OBJDIR)\file$O : file_.c file.h |
| 230 | 252 | $(TCC) -o$@ -c file_.c |
| 231 | 253 | |
| 232 | 254 | file_.c : $(SRCDIR)\file.c |
| @@ -507,7 +529,7 @@ | ||
| 507 | 529 | |
| 508 | 530 | zip_.c : $(SRCDIR)\zip.c |
| 509 | 531 | +translate$E $** > $@ |
| 510 | 532 | |
| 511 | 533 | headers: makeheaders$E page_index.h VERSION.h |
| 512 | - +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h | |
| 534 | + +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h | |
| 513 | 535 | @copy /Y nul: headers |
| 514 | 536 | |
| 515 | 537 | ADDED win/fossil.ico |
| 516 | 538 | ADDED win/fossil.rc |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -24,28 +24,35 @@ | |
| 24 | CFLAGS = -o |
| 25 | BCC = $(DMDIR)\bin\dmc $(CFLAGS) |
| 26 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) |
| 27 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 |
| 28 | |
| 29 | SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c |
| 30 | |
| 31 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 32 | |
| 33 | |
| 34 | APPNAME = $(OBJDIR)\fossil$(E) |
| 35 | |
| 36 | all: $(APPNAME) |
| 37 | |
| 38 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link |
| 39 | cd $(OBJDIR) |
| 40 | $(DMDIR)\bin\link @link |
| 41 | |
| 42 | $(OBJDIR)\link: $B\win\Makefile.dmc |
| 43 | +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ |
| 44 | +echo fossil >> $@ |
| 45 | +echo fossil >> $@ |
| 46 | +echo $(LIBS) >> $@ |
| 47 | |
| 48 | |
| 49 | |
| 50 | translate$E: $(SRCDIR)\translate.c |
| 51 | $(BCC) -o$@ $** |
| @@ -57,10 +64,13 @@ | |
| 57 | $(BCC) -o$@ $** |
| 58 | |
| 59 | version$E: $B\win\version.c |
| 60 | $(BCC) -o$@ $** |
| 61 | |
| 62 | $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c |
| 63 | $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** |
| 64 | |
| 65 | $(OBJDIR)\th$O : $(SRCDIR)\th.c |
| 66 | $(TCC) -o$@ -c $** |
| @@ -217,16 +227,28 @@ | |
| 217 | $(OBJDIR)\doc$O : doc_.c doc.h |
| 218 | $(TCC) -o$@ -c doc_.c |
| 219 | |
| 220 | doc_.c : $(SRCDIR)\doc.c |
| 221 | +translate$E $** > $@ |
| 222 | |
| 223 | $(OBJDIR)\encode$O : encode_.c encode.h |
| 224 | $(TCC) -o$@ -c encode_.c |
| 225 | |
| 226 | encode_.c : $(SRCDIR)\encode.c |
| 227 | +translate$E $** > $@ |
| 228 | |
| 229 | $(OBJDIR)\file$O : file_.c file.h |
| 230 | $(TCC) -o$@ -c file_.c |
| 231 | |
| 232 | file_.c : $(SRCDIR)\file.c |
| @@ -507,7 +529,7 @@ | |
| 507 | |
| 508 | zip_.c : $(SRCDIR)\zip.c |
| 509 | +translate$E $** > $@ |
| 510 | |
| 511 | headers: makeheaders$E page_index.h VERSION.h |
| 512 | +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h |
| 513 | @copy /Y nul: headers |
| 514 | |
| 515 | DDED win/fossil.ico |
| 516 | DDED win/fossil.rc |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -24,28 +24,35 @@ | |
| 24 | CFLAGS = -o |
| 25 | BCC = $(DMDIR)\bin\dmc $(CFLAGS) |
| 26 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) |
| 27 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 |
| 28 | |
| 29 | SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c |
| 30 | |
| 31 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\shell$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 32 | |
| 33 | RC=$(DMDIR)\bin\rcc |
| 34 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 35 | |
| 36 | APPNAME = $(OBJDIR)\fossil$(E) |
| 37 | |
| 38 | all: $(APPNAME) |
| 39 | |
| 40 | $(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link |
| 41 | cd $(OBJDIR) |
| 42 | $(DMDIR)\bin\link @link |
| 43 | |
| 44 | fossil.res: $B\win\fossil.rc |
| 45 | $(RC) $(RCFLAGS) -o$@ $** |
| 46 | |
| 47 | $(OBJDIR)\link: $B\win\Makefile.dmc |
| 48 | +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 shell th th_lang > $@ |
| 49 | +echo fossil >> $@ |
| 50 | +echo fossil >> $@ |
| 51 | +echo $(LIBS) >> $@ |
| 52 | +echo. >> $@ |
| 53 | +echo fossil >> $@ |
| 54 | |
| 55 | |
| 56 | |
| 57 | translate$E: $(SRCDIR)\translate.c |
| 58 | $(BCC) -o$@ $** |
| @@ -57,10 +64,13 @@ | |
| 64 | $(BCC) -o$@ $** |
| 65 | |
| 66 | version$E: $B\win\version.c |
| 67 | $(BCC) -o$@ $** |
| 68 | |
| 69 | $(OBJDIR)\shell$O : $(SRCDIR)\shell.c |
| 70 | $(TCC) -o$@ -c -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 $** |
| 71 | |
| 72 | $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c |
| 73 | $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** |
| 74 | |
| 75 | $(OBJDIR)\th$O : $(SRCDIR)\th.c |
| 76 | $(TCC) -o$@ -c $** |
| @@ -217,16 +227,28 @@ | |
| 227 | $(OBJDIR)\doc$O : doc_.c doc.h |
| 228 | $(TCC) -o$@ -c doc_.c |
| 229 | |
| 230 | doc_.c : $(SRCDIR)\doc.c |
| 231 | +translate$E $** > $@ |
| 232 | |
| 233 | $(OBJDIR)\event$O : event_.c event.h |
| 234 | $(TCC) -o$@ -c event_.c |
| 235 | |
| 236 | event_.c : $(SRCDIR)\event.c |
| 237 | +translate$E $** > $@ |
| 238 | |
| 239 | $(OBJDIR)\encode$O : encode_.c encode.h |
| 240 | $(TCC) -o$@ -c encode_.c |
| 241 | |
| 242 | encode_.c : $(SRCDIR)\encode.c |
| 243 | +translate$E $** > $@ |
| 244 | |
| 245 | $(OBJDIR)\event$O : event_.c event.h |
| 246 | $(TCC) -o$@ -c event_.c |
| 247 | |
| 248 | event_.c : $(SRCDIR)\event.c |
| 249 | +translate$E $** > $@ |
| 250 | |
| 251 | $(OBJDIR)\file$O : file_.c file.h |
| 252 | $(TCC) -o$@ -c file_.c |
| 253 | |
| 254 | file_.c : $(SRCDIR)\file.c |
| @@ -507,7 +529,7 @@ | |
| 529 | |
| 530 | zip_.c : $(SRCDIR)\zip.c |
| 531 | +translate$E $** > $@ |
| 532 | |
| 533 | headers: makeheaders$E page_index.h VERSION.h |
| 534 | +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h |
| 535 | @copy /Y nul: headers |
| 536 | |
| 537 | DDED win/fossil.ico |
| 538 | DDED win/fossil.rc |
Binary file
+29
| --- a/win/fossil.rc | ||
| +++ b/win/fossil.rc | ||
| @@ -0,0 +1,29 @@ | ||
| 1 | +#include <windows.h> | |
| 2 | + | |
| 3 | +#define _RC_COMPISUBLANG_ENGLISH_US | |
| 4 | + | |
| 5 | +VS | |
| 6 | +PRODUCTVERSION 1,0,0,0 | |
| 7 | + | |
| 8 | +FILEFLAGS 0x0 | |
| 9 | + | |
| 10 | + | |
| 11 | +FILESUBTYPE VFT2_UNKNOWN | |
| 12 | +BEGIN | |
| 13 | + BLOCK "StringFileInfo" | |
| 14 | + BEGIN | |
| 15 | + Bdistributed source code control system with integrated wiki and ticket-system\0" | |
| 16 | + VALUE "Comments", "compiler: "COMPILER_NAME"\0" | |
| 17 | + VALUE "FileVersion", MANIFEST_UUID"("COMPILER_NAME"\0" | |
| 18 | + | |
| 19 | +(c) "MANIFES | |
| 20 | + | |
| 21 | +Version", MANIFEST_VERSION" "MANIFEST_DATE" UTC\0" | |
| 22 | + END | |
| 23 | + END | |
| 24 | + BLOCK "VarFileInfo" | |
| 25 | + BEGIN | |
| 26 | + | |
| 27 | + END | |
| 28 | +END | |
| 29 | + |
| --- a/win/fossil.rc | |
| +++ b/win/fossil.rc | |
| @@ -0,0 +1,29 @@ | |
| --- a/win/fossil.rc | |
| +++ b/win/fossil.rc | |
| @@ -0,0 +1,29 @@ | |
| 1 | #include <windows.h> |
| 2 | |
| 3 | #define _RC_COMPISUBLANG_ENGLISH_US |
| 4 | |
| 5 | VS |
| 6 | PRODUCTVERSION 1,0,0,0 |
| 7 | |
| 8 | FILEFLAGS 0x0 |
| 9 | |
| 10 | |
| 11 | FILESUBTYPE VFT2_UNKNOWN |
| 12 | BEGIN |
| 13 | BLOCK "StringFileInfo" |
| 14 | BEGIN |
| 15 | Bdistributed source code control system with integrated wiki and ticket-system\0" |
| 16 | VALUE "Comments", "compiler: "COMPILER_NAME"\0" |
| 17 | VALUE "FileVersion", MANIFEST_UUID"("COMPILER_NAME"\0" |
| 18 | |
| 19 | (c) "MANIFES |
| 20 | |
| 21 | Version", MANIFEST_VERSION" "MANIFEST_DATE" UTC\0" |
| 22 | END |
| 23 | END |
| 24 | BLOCK "VarFileInfo" |
| 25 | BEGIN |
| 26 | |
| 27 | END |
| 28 | END |
| 29 |
+1
| --- win/version.c | ||
| +++ win/version.c | ||
| @@ -10,10 +10,11 @@ | ||
| 10 | 10 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 11 | 11 | m = fopen(argv[2],"r"); |
| 12 | 12 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 13 | 13 | if(0 == strncmp("D ",b,2)){ |
| 14 | 14 | printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); |
| 15 | + printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); | |
| 15 | 16 | return 0; |
| 16 | 17 | } |
| 17 | 18 | } |
| 18 | 19 | return 1; |
| 19 | 20 | } |
| 20 | 21 |
| --- win/version.c | |
| +++ win/version.c | |
| @@ -10,10 +10,11 @@ | |
| 10 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 11 | m = fopen(argv[2],"r"); |
| 12 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 13 | if(0 == strncmp("D ",b,2)){ |
| 14 | printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); |
| 15 | return 0; |
| 16 | } |
| 17 | } |
| 18 | return 1; |
| 19 | } |
| 20 |
| --- win/version.c | |
| +++ win/version.c | |
| @@ -10,10 +10,11 @@ | |
| 10 | printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); |
| 11 | m = fopen(argv[2],"r"); |
| 12 | while(b == fgets(b, sizeof(b)-1,m)){ |
| 13 | if(0 == strncmp("D ",b,2)){ |
| 14 | printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); |
| 15 | printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); |
| 16 | return 0; |
| 17 | } |
| 18 | } |
| 19 | return 1; |
| 20 | } |
| 21 |
+28
-6
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -95,26 +95,35 @@ | ||
| 95 | 95 | may contain no additional text or data beyond what is described here. |
| 96 | 96 | |
| 97 | 97 | Allowed cards in the manifest are as follows: |
| 98 | 98 | |
| 99 | 99 | <blockquote> |
| 100 | +<b>B</b> <i>baseline-manifest</i><br> | |
| 100 | 101 | <b>C</b> <i>checkin-comment</i><br> |
| 101 | 102 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 102 | 103 | <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> |
| 103 | 104 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 104 | 105 | <b>R</b> <i>repository-checksum</i><br> |
| 105 | 106 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> |
| 106 | 107 | <b>U</b> <i>user-login</i><br> |
| 107 | 108 | <b>Z</b> <i>manifest-checksum</i> |
| 108 | 109 | </blockquote> |
| 110 | + | |
| 111 | +A manifest may optionally have a single B-card. The B-card specifies | |
| 112 | +another manifest that serves as the "baseline" for this manifest. A | |
| 113 | +manifest that has a B-card is called a delta-manifest and a manifest | |
| 114 | +that omits the B-card is a baseline-manifest. The other manifest | |
| 115 | +identified by the argument of the B-card must be a baseline-manifest. | |
| 116 | +A baseline-manifest records the complete contents of a checkin. | |
| 117 | +A delta-manifest records only changes from its baseline. | |
| 109 | 118 | |
| 110 | 119 | A manifest must have exactly one C-card. The sole argument to |
| 111 | 120 | the C-card is a check-in comment that describes the check-in that |
| 112 | 121 | the manifest defines. The check-in comment is text. The following |
| 113 | 122 | escape sequences are applied to the text: |
| 114 | 123 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 115 | -newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash | |
| 124 | +newline (ASCII 0x0a) is "\n" (ASCII 0x5C, x6E). A backslash | |
| 116 | 125 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 117 | 126 | space and newline, no other whitespace characters are allowed in |
| 118 | 127 | the check-in comment. Nor are any unprintable characters allowed |
| 119 | 128 | in the comment. |
| 120 | 129 | |
| @@ -125,26 +134,29 @@ | ||
| 125 | 134 | |
| 126 | 135 | <blockquote> |
| 127 | 136 | <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i> |
| 128 | 137 | </blockquote> |
| 129 | 138 | |
| 130 | -A manifest has zero or more F-cards. Each F-card defines a file | |
| 131 | -(other than the manifest itself) which is part of the check-in that | |
| 132 | -the manifest defines. There are two, three, or four arguments. | |
| 139 | +A manifest has zero or more F-cards. Each F-card identifies a file | |
| 140 | +that is part of the check-in. There are one, two, three, or four arguments. | |
| 133 | 141 | The first argument |
| 134 | 142 | is the pathname of the file in the check-in relative to the root |
| 135 | 143 | of the project file hierarchy. No ".." or "." directories are allowed |
| 136 | 144 | within the filename. Space characters are escaped as in C-card |
| 137 | 145 | comment text. Backslash characters and newlines are not allowed |
| 138 | 146 | within filenames. The directory separator character is a forward |
| 139 | 147 | slash (ASCII 0x2F). The second argument to the F-card is the |
| 140 | 148 | full 40-character lower-case hexadecimal SHA1 hash of the content |
| 141 | -artifact. The optional 3rd argument defines any special access | |
| 149 | +artifact. The second argument is required for baseline manifests | |
| 150 | +but is optional for delta manifests. When the second argument to the | |
| 151 | +F-card is omitted, it means that the file has been deleted relative | |
| 152 | +to the baseline. The optional 3rd argument defines any special access | |
| 142 | 153 | permissions associated with the file. The only special code currently |
| 143 | 154 | defined is "x" which means that the file is executable. All files are |
| 144 | 155 | always readable and writable. This can be expressed by "w" permission |
| 145 | -if desired but is optional. | |
| 156 | +if desired but is optional. The file format might be extended with | |
| 157 | +new permission letters in the future. | |
| 146 | 158 | The optional 4th argument is the name of the same file as it existed in |
| 147 | 159 | the parent check-in. If the name of the file is unchanged from its |
| 148 | 160 | parent, then the 4th argument is omitted. |
| 149 | 161 | |
| 150 | 162 | A manifest has zero or one P-cards. Most manifests have one P-card. |
| @@ -504,10 +516,20 @@ | ||
| 504 | 516 | <td> </td> |
| 505 | 517 | <td> </td> |
| 506 | 518 | <td align=center><b>X</b></td> |
| 507 | 519 | <td> </td> |
| 508 | 520 | </tr> |
| 521 | +<tr> | |
| 522 | +<td><b>B</b> <i>baseline</i></td> | |
| 523 | +<td align=center><b>X</b></td> | |
| 524 | +<td> </td> | |
| 525 | +<td> </td> | |
| 526 | +<td> </td> | |
| 527 | +<td> </td> | |
| 528 | +<td> </td> | |
| 529 | +<td> </td> | |
| 530 | +</tr> | |
| 509 | 531 | <tr> |
| 510 | 532 | <td><b>C</b> <i>comment-text</i></td> |
| 511 | 533 | <td align=center><b>X</b></td> |
| 512 | 534 | <td> </td> |
| 513 | 535 | <td> </td> |
| 514 | 536 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -95,26 +95,35 @@ | |
| 95 | may contain no additional text or data beyond what is described here. |
| 96 | |
| 97 | Allowed cards in the manifest are as follows: |
| 98 | |
| 99 | <blockquote> |
| 100 | <b>C</b> <i>checkin-comment</i><br> |
| 101 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 102 | <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> |
| 103 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 104 | <b>R</b> <i>repository-checksum</i><br> |
| 105 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> |
| 106 | <b>U</b> <i>user-login</i><br> |
| 107 | <b>Z</b> <i>manifest-checksum</i> |
| 108 | </blockquote> |
| 109 | |
| 110 | A manifest must have exactly one C-card. The sole argument to |
| 111 | the C-card is a check-in comment that describes the check-in that |
| 112 | the manifest defines. The check-in comment is text. The following |
| 113 | escape sequences are applied to the text: |
| 114 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 115 | newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash |
| 116 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 117 | space and newline, no other whitespace characters are allowed in |
| 118 | the check-in comment. Nor are any unprintable characters allowed |
| 119 | in the comment. |
| 120 | |
| @@ -125,26 +134,29 @@ | |
| 125 | |
| 126 | <blockquote> |
| 127 | <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i> |
| 128 | </blockquote> |
| 129 | |
| 130 | A manifest has zero or more F-cards. Each F-card defines a file |
| 131 | (other than the manifest itself) which is part of the check-in that |
| 132 | the manifest defines. There are two, three, or four arguments. |
| 133 | The first argument |
| 134 | is the pathname of the file in the check-in relative to the root |
| 135 | of the project file hierarchy. No ".." or "." directories are allowed |
| 136 | within the filename. Space characters are escaped as in C-card |
| 137 | comment text. Backslash characters and newlines are not allowed |
| 138 | within filenames. The directory separator character is a forward |
| 139 | slash (ASCII 0x2F). The second argument to the F-card is the |
| 140 | full 40-character lower-case hexadecimal SHA1 hash of the content |
| 141 | artifact. The optional 3rd argument defines any special access |
| 142 | permissions associated with the file. The only special code currently |
| 143 | defined is "x" which means that the file is executable. All files are |
| 144 | always readable and writable. This can be expressed by "w" permission |
| 145 | if desired but is optional. |
| 146 | The optional 4th argument is the name of the same file as it existed in |
| 147 | the parent check-in. If the name of the file is unchanged from its |
| 148 | parent, then the 4th argument is omitted. |
| 149 | |
| 150 | A manifest has zero or one P-cards. Most manifests have one P-card. |
| @@ -504,10 +516,20 @@ | |
| 504 | <td> </td> |
| 505 | <td> </td> |
| 506 | <td align=center><b>X</b></td> |
| 507 | <td> </td> |
| 508 | </tr> |
| 509 | <tr> |
| 510 | <td><b>C</b> <i>comment-text</i></td> |
| 511 | <td align=center><b>X</b></td> |
| 512 | <td> </td> |
| 513 | <td> </td> |
| 514 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -95,26 +95,35 @@ | |
| 95 | may contain no additional text or data beyond what is described here. |
| 96 | |
| 97 | Allowed cards in the manifest are as follows: |
| 98 | |
| 99 | <blockquote> |
| 100 | <b>B</b> <i>baseline-manifest</i><br> |
| 101 | <b>C</b> <i>checkin-comment</i><br> |
| 102 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 103 | <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> |
| 104 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 105 | <b>R</b> <i>repository-checksum</i><br> |
| 106 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> |
| 107 | <b>U</b> <i>user-login</i><br> |
| 108 | <b>Z</b> <i>manifest-checksum</i> |
| 109 | </blockquote> |
| 110 | |
| 111 | A manifest may optionally have a single B-card. The B-card specifies |
| 112 | another manifest that serves as the "baseline" for this manifest. A |
| 113 | manifest that has a B-card is called a delta-manifest and a manifest |
| 114 | that omits the B-card is a baseline-manifest. The other manifest |
| 115 | identified by the argument of the B-card must be a baseline-manifest. |
| 116 | A baseline-manifest records the complete contents of a checkin. |
| 117 | A delta-manifest records only changes from its baseline. |
| 118 | |
| 119 | A manifest must have exactly one C-card. The sole argument to |
| 120 | the C-card is a check-in comment that describes the check-in that |
| 121 | the manifest defines. The check-in comment is text. The following |
| 122 | escape sequences are applied to the text: |
| 123 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 124 | newline (ASCII 0x0a) is "\n" (ASCII 0x5C, x6E). A backslash |
| 125 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 126 | space and newline, no other whitespace characters are allowed in |
| 127 | the check-in comment. Nor are any unprintable characters allowed |
| 128 | in the comment. |
| 129 | |
| @@ -125,26 +134,29 @@ | |
| 134 | |
| 135 | <blockquote> |
| 136 | <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i> |
| 137 | </blockquote> |
| 138 | |
| 139 | A manifest has zero or more F-cards. Each F-card identifies a file |
| 140 | that is part of the check-in. There are one, two, three, or four arguments. |
| 141 | The first argument |
| 142 | is the pathname of the file in the check-in relative to the root |
| 143 | of the project file hierarchy. No ".." or "." directories are allowed |
| 144 | within the filename. Space characters are escaped as in C-card |
| 145 | comment text. Backslash characters and newlines are not allowed |
| 146 | within filenames. The directory separator character is a forward |
| 147 | slash (ASCII 0x2F). The second argument to the F-card is the |
| 148 | full 40-character lower-case hexadecimal SHA1 hash of the content |
| 149 | artifact. The second argument is required for baseline manifests |
| 150 | but is optional for delta manifests. When the second argument to the |
| 151 | F-card is omitted, it means that the file has been deleted relative |
| 152 | to the baseline. The optional 3rd argument defines any special access |
| 153 | permissions associated with the file. The only special code currently |
| 154 | defined is "x" which means that the file is executable. All files are |
| 155 | always readable and writable. This can be expressed by "w" permission |
| 156 | if desired but is optional. The file format might be extended with |
| 157 | new permission letters in the future. |
| 158 | The optional 4th argument is the name of the same file as it existed in |
| 159 | the parent check-in. If the name of the file is unchanged from its |
| 160 | parent, then the 4th argument is omitted. |
| 161 | |
| 162 | A manifest has zero or one P-cards. Most manifests have one P-card. |
| @@ -504,10 +516,20 @@ | |
| 516 | <td> </td> |
| 517 | <td> </td> |
| 518 | <td align=center><b>X</b></td> |
| 519 | <td> </td> |
| 520 | </tr> |
| 521 | <tr> |
| 522 | <td><b>B</b> <i>baseline</i></td> |
| 523 | <td align=center><b>X</b></td> |
| 524 | <td> </td> |
| 525 | <td> </td> |
| 526 | <td> </td> |
| 527 | <td> </td> |
| 528 | <td> </td> |
| 529 | <td> </td> |
| 530 | </tr> |
| 531 | <tr> |
| 532 | <td><b>C</b> <i>comment-text</i></td> |
| 533 | <td align=center><b>X</b></td> |
| 534 | <td> </td> |
| 535 | <td> </td> |
| 536 |