Fossil SCM

Merge from trunk.

michael 2010-11-07 10:23 ttmrichter-skins merge
Commit 1a6876db8decace4d1869c93a3c0a3efb1595964
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
+38 -1
--- Makefile
+++ Makefile
@@ -16,13 +16,50 @@
1616
-include config.mk # Configure if present.
1717
ifndef CONFIG_MK_COMPLETE
1818
include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc.
1919
endif
2020
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.
2246
#
2347
TCLSH = tclsh
2448
2549
# You should not need to change anything below this line
2650
###############################################################################
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
+
2764
include $(SRCDIR)/main.mk
2865
2966
--- 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
+38 -1
--- Makefile
+++ Makefile
@@ -16,13 +16,50 @@
1616
-include config.mk # Configure if present.
1717
ifndef CONFIG_MK_COMPLETE
1818
include $(MAKEDIR)/linux-gcc-config.mk # Default to linux-gcc.
1919
endif
2020
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.
2246
#
2347
TCLSH = tclsh
2448
2549
# You should not need to change anything below this line
2650
###############################################################################
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
+
2764
include $(SRCDIR)/main.mk
2865
2966
--- 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 @@
3434
static char *quoteFilename(const char *zFilename){
3535
int i, c;
3636
int needQuote = 0;
3737
for(i=0; (c = zFilename[i])!=0; i++){
3838
if( c=='"' ) return 0;
39
- if( isspace(c) ) needQuote = 1;
39
+ if( fossil_isspace(c) ) needQuote = 1;
4040
if( c=='\\' && zFilename[i+1]==0 ) return 0;
4141
if( c=='$' ) return 0;
4242
}
4343
if( needQuote ){
4444
return mprintf("\"%s\"", zFilename);
@@ -60,10 +60,13 @@
6060
** On Win32 systems, the file is named "_fossil" and is located in
6161
** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%.
6262
**
6363
** Available operations are:
6464
**
65
+** ignore Arguments are repositories that should be ignored
66
+** by subsequent list, pull, push, rebuild, and sync.
67
+**
6568
** list | ls Display the location of all repositories
6669
**
6770
** pull Run a "pull" operation on all repositories
6871
**
6972
** push Run a "push" on all repositories
@@ -72,11 +75,12 @@
7275
**
7376
** sync Run a "sync" on all repositories
7477
**
7578
** Respositories are automatically added to the set of known repositories
7679
** 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.
7882
*/
7983
void all_cmd(void){
8084
int n;
8185
Stmt q;
8286
const char *zCmd;
@@ -99,13 +103,22 @@
99103
zCmd = "pull -autourl -R";
100104
}else if( strncmp(zCmd, "rebuild", n)==0 ){
101105
zCmd = "rebuild";
102106
}else if( strncmp(zCmd, "sync", n)==0 ){
103107
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;
104117
}else{
105118
fossil_fatal("\"all\" subcommand should be one of: "
106
- "list ls push pull rebuild sync");
119
+ "ignore list ls push pull rebuild sync");
107120
}
108121
zFossil = quoteFilename(g.argv[0]);
109122
nMissing = 0;
110123
db_prepare(&q,
111124
"SELECT DISTINCT substr(name, 6) COLLATE nocase"
@@ -125,11 +138,11 @@
125138
}
126139
zQFilename = quoteFilename(zFilename);
127140
zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename);
128141
printf("%s\n", zSyscmd);
129142
fflush(stdout);
130
- portable_system(zSyscmd);
143
+ fossil_system(zSyscmd);
131144
free(zSyscmd);
132145
free(zQFilename);
133146
}
134147
135148
/* If any repositories whose names appear in the ~/.fossil file could not
136149
--- 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 @@
8282
zUrlTail = mprintf("page=%t&amp;file=%t", zTarget, zFilename);
8383
}
8484
@
8585
@ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
8686
@ [<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++;
8888
if( zComment && zComment[0] ){
8989
@ %w(zComment)<br />
9090
}
9191
if( zPage==0 && zTkt==0 ){
9292
if( zSrc==0 || zSrc[0]==0 ){
@@ -251,13 +251,13 @@
251251
}
252252
zName += n;
253253
if( zName[0]==0 ) zName = "unknown";
254254
blob_appendf(&manifest, "A %F %F %s\n", zName, zTarget, zUUID);
255255
zComment = PD("comment", "");
256
- while( isspace(zComment[0]) ) zComment++;
256
+ while( fossil_isspace(zComment[0]) ) zComment++;
257257
n = strlen(zComment);
258
- while( n>0 && isspace(zComment[n-1]) ){ n--; }
258
+ while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; }
259259
if( n>0 ){
260260
blob_appendf(&manifest, "C %F\n", zComment);
261261
}
262262
zDate = db_text(0, "SELECT datetime('now')");
263263
zDate[10] = 'T';
264264
--- src/attach.c
+++ src/attach.c
@@ -82,11 +82,11 @@
82 zUrlTail = mprintf("page=%t&amp;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&amp;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 @@
8686
int nDel = 0; /* Number of deleted entries */
8787
int nLive = 0; /* Number of live entries */
8888
8989
old = *p;
9090
assert( newSize>old.cnt );
91
- p->a = malloc( sizeof(p->a[0])*newSize );
91
+ p->a = fossil_malloc( sizeof(p->a[0])*newSize );
9292
p->sz = newSize;
9393
memset(p->a, 0, sizeof(p->a[0])*newSize );
9494
for(i=0; i<old.sz; i++){
9595
int e = old.a[i];
9696
if( e>0 ){
9797
--- 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 @@
7272
7373
/*
7474
** We find that the built-in isspace() function does not work for
7575
** some international character sets. So here is a substitute.
7676
*/
77
-int blob_isspace(char c){
77
+int fossil_isspace(char c){
7878
return c==' ' || (c<='\r' && c>='\t');
7979
}
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
+
8097
8198
/*
8299
** COMMAND: test-isspace
83100
*/
84101
void isspace_cmd(void){
85102
int i;
86103
for(i=0; i<=255; i++){
87104
if( i==' ' || i=='\n' || i=='\t' || i=='\v'
88105
|| i=='\f' || i=='\r' ){
89
- assert( blob_isspace((char)i) );
106
+ assert( fossil_isspace((char)i) );
90107
}else{
91
- assert( !blob_isspace((char)i) );
108
+ assert( !fossil_isspace((char)i) );
92109
}
93110
}
94111
printf("All 256 characters OK\n");
95112
}
96113
@@ -119,12 +136,11 @@
119136
pBlob->aData = 0;
120137
pBlob->nAlloc = 0;
121138
pBlob->nUsed = 0;
122139
pBlob->iCursor = 0;
123140
}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);
126142
pBlob->aData = pNew;
127143
pBlob->nAlloc = newSize;
128144
if( pBlob->nUsed>pBlob->nAlloc ){
129145
pBlob->nUsed = pBlob->nAlloc;
130146
}
@@ -145,12 +161,11 @@
145161
*/
146162
static void blobReallocStatic(Blob *pBlob, unsigned int newSize){
147163
if( newSize==0 ){
148164
*pBlob = empty_blob;
149165
}else{
150
- char *pNew = malloc( newSize );
151
- if( pNew==0 ) blob_panic();
166
+ char *pNew = fossil_malloc( newSize );
152167
if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
153168
memcpy(pNew, pBlob->aData, pBlob->nUsed);
154169
pBlob->aData = pNew;
155170
pBlob->xRealloc = blobReallocMalloc;
156171
pBlob->nAlloc = newSize;
@@ -429,11 +444,11 @@
429444
** not insert a new zero terminator.
430445
*/
431446
int blob_trim(Blob *p){
432447
char *z = p->aData;
433448
int n = p->nUsed;
434
- while( n>0 && blob_isspace(z[n-1]) ){ n--; }
449
+ while( n>0 && fossil_isspace(z[n-1]) ){ n--; }
435450
p->nUsed = n;
436451
return n;
437452
}
438453
439454
/*
@@ -452,15 +467,15 @@
452467
*/
453468
int blob_token(Blob *pFrom, Blob *pTo){
454469
char *aData = pFrom->aData;
455470
int n = pFrom->nUsed;
456471
int i = pFrom->iCursor;
457
- while( i<n && blob_isspace(aData[i]) ){ i++; }
472
+ while( i<n && fossil_isspace(aData[i]) ){ i++; }
458473
pFrom->iCursor = i;
459
- while( i<n && !blob_isspace(aData[i]) ){ i++; }
474
+ while( i<n && !fossil_isspace(aData[i]) ){ i++; }
460475
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++; }
462477
pFrom->iCursor = i;
463478
return pTo->nUsed;
464479
}
465480
466481
/*
@@ -523,11 +538,11 @@
523538
int blob_is_int(Blob *pBlob, int *pValue){
524539
const char *z = blob_buffer(pBlob);
525540
int i, n, c, v;
526541
n = blob_size(pBlob);
527542
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++){
529544
v = v*10 + c - '0';
530545
}
531546
if( i==n ){
532547
*pValue = v;
533548
return 1;
@@ -612,11 +627,11 @@
612627
return blob_read_from_channel(pBlob, stdin, -1);
613628
}
614629
size = file_size(zFilename);
615630
blob_zero(pBlob);
616631
if( size<0 ){
617
- fossil_panic("no such file: %s", zFilename);
632
+ fossil_fatal("no such file: %s", zFilename);
618633
}
619634
if( size==0 ){
620635
return 0;
621636
}
622637
blob_resize(pBlob, size);
@@ -906,11 +921,11 @@
906921
int n = blob_size(pBlob);
907922
int k = strlen(zIn);
908923
int i, c;
909924
char *z;
910925
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) ){
912927
blob_appendf(pBlob, "\"%s\"", zIn);
913928
z = blob_buffer(pBlob);
914929
for(i=n+1; i<=n+k; i++){
915930
if( z[i]=='"' ) z[i] = '_';
916931
}
917932
--- 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 @@
3535
const char *zBranch; /* Name of the new branch */
3636
char *zDate; /* Date that branch was created */
3737
char *zComment; /* Check-in comment for the new branch */
3838
const char *zColor; /* Color of the new branch */
3939
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 */
4241
Blob mcksum; /* Self-checksum on the manifest */
4342
const char *zDateOvrd; /* Override date string */
4443
const char *zUserOvrd; /* Override user name */
4544
4645
noSign = find_option("nosign","",0)!=0;
@@ -71,41 +70,42 @@
7170
db_begin_transaction();
7271
rootid = name_to_rid(g.argv[4]);
7372
if( rootid==0 ){
7473
fossil_fatal("unable to locate check-in off of which to branch");
7574
}
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
+ }
7680
7781
/* Create a manifest for the new branch */
7882
blob_zero(&branch);
83
+ if( pParent->zBaseline ){
84
+ blob_appendf(&branch, "B %s\n", pParent->zBaseline);
85
+ }
7986
zComment = mprintf("Create new branch named \"%h\"", zBranch);
8087
blob_appendf(&branch, "C %F\n", zComment);
8188
zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
8289
zDate[10] = 'T';
8390
blob_appendf(&branch, "D %s\n", zDate);
8491
8592
/* 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);
102102
}
103103
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rootid);
104104
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);
107107
108108
/* Add the symbolic branch name and the "branch" tag to identify
109109
** this as a new branch */
110110
if( zColor!=0 ){
111111
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
112112
--- 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 @@
7171
** There is no hyperlink on the file element of the path.
7272
**
7373
** The computed string is appended to the pOut blob. pOut should
7474
** have already been initialized.
7575
*/
76
-void hyperlinked_path(const char *zPath, Blob *pOut){
76
+void hyperlinked_path(const char *zPath, Blob *pOut, const char *zCI){
7777
int i, j;
7878
char *zSep = "";
7979
8080
for(i=0; zPath[i]; i=j){
8181
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
8282
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&amp;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
+ }
8590
}else{
8691
blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
8792
}
8893
zSep = "/";
8994
while( zPath[j]=='/' ){ j++; }
@@ -99,21 +104,21 @@
99104
** name=PATH Directory to display. Required.
100105
** ci=LABEL Show only files in this check-in. Optional.
101106
*/
102107
void page_dir(void){
103108
const char *zD = P("name");
109
+ int nD = zD ? strlen(zD)+1 : 0;
104110
int mxLen;
105111
int nCol, nRow;
106112
int cnt, i;
107113
char *zPrefix;
108114
Stmt q;
109115
const char *zCI = P("ci");
110116
int rid = 0;
111117
char *zUuid = 0;
112
- Blob content;
113118
Blob dirname;
114
- Manifest m;
119
+ Manifest *pM = 0;
115120
const char *zSubdirLink;
116121
117122
login_check_credentials();
118123
if( !g.okHistory ){ login_needed(); return; }
119124
style_header("File List");
@@ -126,25 +131,24 @@
126131
/* If a specific check-in is requested, fetch and parse it. If the
127132
** specific check-in does not exist, clear zCI. zCI==0 will cause all
128133
** files from all check-ins to be displayed.
129134
*/
130135
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);
135139
}else{
136
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
140
+ zCI = 0;
137141
}
138142
}
139143
140144
141145
/* Compute the title of the page */
142146
blob_zero(&dirname);
143147
if( zD ){
144148
blob_append(&dirname, "in directory ", -1);
145
- hyperlinked_path(zD, &dirname);
149
+ hyperlinked_path(zD, &dirname, zCI);
146150
zPrefix = mprintf("%h/", zD);
147151
}else{
148152
blob_append(&dirname, "in the top-level directory", -1);
149153
zPrefix = "";
150154
}
@@ -160,22 +164,30 @@
160164
style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
161165
}else{
162166
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
163167
}
164168
}else{
169
+ int hasTrunk;
165170
@ <h2>The union of all files from all check-ins
166171
@ %s(blob_str(&dirname))</h2>
172
+ hasTrunk = db_exists(
173
+ "SELECT 1 FROM tagxref WHERE tagid=%d AND value='trunk'",
174
+ TAG_BRANCH);
167175
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
168176
if( zD ){
169177
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
170178
style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
171179
g.zBaseURL, zD);
172
- style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
173
- g.zBaseURL,zD);
180
+ if( hasTrunk ){
181
+ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
182
+ g.zBaseURL,zD);
183
+ }
174184
}else{
175185
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
+ }
177189
}
178190
}
179191
180192
/* Compute the temporary table "localfiles" containing the names
181193
** of all files and subdirectories in the zD[] directory.
@@ -184,39 +196,47 @@
184196
** first and it also gives us an easy way to distinguish files
185197
** from directories in the loop that follows.
186198
*/
187199
db_multi_exec(
188200
"CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);"
189
- "CREATE TEMP TABLE allfiles(x UNIQUE NOT NULL, u);"
190201
);
191202
if( zCI ){
192203
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"
218238
);
219239
}
220240
221241
/* Generate a multi-column table listing the contents of zD[]
222242
** directory.
@@ -246,8 +266,9 @@
246266
@ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
247267
@ </a></li>
248268
}
249269
}
250270
db_finalize(&q);
271
+ manifest_destroy(pM);
251272
@ </ul></td></tr></table>
252273
style_footer();
253274
}
254275
--- 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&amp;ci=tip",
171 g.zBaseURL, zD);
172 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;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&amp;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&amp;ci=tip",
179 g.zBaseURL, zD);
180 if( hasTrunk ){
181 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;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 @@
6969
** Render an 8-character hexadecimal string as ascii art.
7070
** Space to hold the result is obtained from malloc() and should be freed
7171
** by the caller.
7272
*/
7373
char *captcha_render(const char *zPw){
74
- char *z = malloc( 500 );
74
+ char *z = fossil_malloc( 500 );
7575
int i, j, k, m;
7676
7777
k = 0;
7878
for(i=0; i<6; i++){
7979
for(j=0; j<8; j++){
@@ -202,11 +202,11 @@
202202
** Render an 8-digit hexadecimal string as ascii arg.
203203
** Space to hold the result is obtained from malloc() and should be freed
204204
** by the caller.
205205
*/
206206
char *captcha_render(const char *zPw){
207
- char *z = malloc( 300 );
207
+ char *z = fossil_malloc( 300 );
208208
int i, j, k, m;
209209
const char *zChar;
210210
211211
k = 0;
212212
for(i=0; i<4; i++){
@@ -359,11 +359,11 @@
359359
** Render an 8-digit hexadecimal string as ascii arg.
360360
** Space to hold the result is obtained from malloc() and should be freed
361361
** by the caller.
362362
*/
363363
char *captcha_render(const char *zPw){
364
- char *z = malloc( 600 );
364
+ char *z = fossil_malloc( 600 );
365365
int i, j, k, m;
366366
const char *zChar;
367367
368368
k = 0;
369369
for(i=0; i<6; i++){
370370
--- 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 @@
387387
** deallocated after this routine returns.
388388
*/
389389
void cgi_set_parameter_nocopy(const char *zName, const char *zValue){
390390
if( nAllocQP<=nUsedQP ){
391391
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]) );
394393
}
395394
aParamQP[nUsedQP].zName = zName;
396395
aParamQP[nUsedQP].zValue = zValue;
397396
if( g.fHttpTrace ){
398397
fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue);
@@ -463,11 +462,11 @@
463462
*/
464463
static void add_param_list(char *z, int terminator){
465464
while( *z ){
466465
char *zName;
467466
char *zValue;
468
- while( isspace(*z) ){ z++; }
467
+ while( fossil_isspace(*z) ){ z++; }
469468
zName = z;
470469
while( *z && *z!='=' && *z!=terminator ){ z++; }
471470
if( *z=='=' ){
472471
*z = 0;
473472
z++;
@@ -480,11 +479,11 @@
480479
dehttpize(zValue);
481480
}else{
482481
if( *z ){ *z++ = 0; }
483482
zValue = "";
484483
}
485
- if( islower(zName[0]) ){
484
+ if( fossil_islower(zName[0]) ){
486485
cgi_set_parameter_nocopy(zName, zValue);
487486
}
488487
}
489488
}
490489
@@ -575,11 +574,11 @@
575574
** in the example above.
576575
*/
577576
static int tokenize_line(char *z, int mxArg, char **azArg){
578577
int i = 0;
579578
while( *z ){
580
- while( isspace(*z) || *z==';' ){ z++; }
579
+ while( fossil_isspace(*z) || *z==';' ){ z++; }
581580
if( *z=='"' && z[1] ){
582581
*z = 0;
583582
z++;
584583
if( i<mxArg-1 ){ azArg[i++] = z; }
585584
while( *z && *z!='"' ){ z++; }
@@ -586,11 +585,11 @@
586585
if( *z==0 ) break;
587586
*z = 0;
588587
z++;
589588
}else{
590589
if( i<mxArg-1 ){ azArg[i++] = z; }
591
- while( *z && !isspace(*z) && *z!=';' && *z!='"' ){ z++; }
590
+ while( *z && !fossil_isspace(*z) && *z!=';' && *z!='"' ){ z++; }
592591
if( *z && *z!='"' ){
593592
*z = 0;
594593
z++;
595594
}
596595
}
@@ -621,11 +620,11 @@
621620
if( zBoundry==0 ) return;
622621
while( (zLine = get_line_from_string(&z, &len))!=0 ){
623622
if( zLine[0]==0 ){
624623
int nContent = 0;
625624
zValue = get_bounded_content(&z, &len, zBoundry, &nContent);
626
- if( zName && zValue && islower(zName[0]) ){
625
+ if( zName && zValue && fossil_islower(zName[0]) ){
627626
cgi_set_parameter_nocopy(zName, zValue);
628627
if( showBytes ){
629628
cgi_set_parameter_nocopy(mprintf("%s:bytes", zName),
630629
mprintf("%d",nContent));
631630
}
@@ -633,25 +632,25 @@
633632
zName = 0;
634633
showBytes = 0;
635634
}else{
636635
nArg = tokenize_line(zLine, sizeof(azArg)/sizeof(azArg[0]), azArg);
637636
for(i=0; i<nArg; i++){
638
- int c = tolower(azArg[i][0]);
637
+ int c = fossil_tolower(azArg[i][0]);
639638
int n = strlen(azArg[i]);
640639
if( c=='c' && sqlite3_strnicmp(azArg[i],"content-disposition:",n)==0 ){
641640
i++;
642641
}else if( c=='n' && sqlite3_strnicmp(azArg[i],"name=",n)==0 ){
643642
zName = azArg[++i];
644643
}else if( c=='f' && sqlite3_strnicmp(azArg[i],"filename=",n)==0 ){
645644
char *z = azArg[++i];
646
- if( zName && z && islower(zName[0]) ){
645
+ if( zName && z && fossil_islower(zName[0]) ){
647646
cgi_set_parameter_nocopy(mprintf("%s:filename",zName), z);
648647
}
649648
showBytes = 1;
650649
}else if( c=='c' && sqlite3_strnicmp(azArg[i],"content-type:",n)==0 ){
651650
char *z = azArg[++i];
652
- if( zName && z && islower(zName[0]) ){
651
+ if( zName && z && fossil_islower(zName[0]) ){
653652
cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z);
654653
}
655654
}
656655
}
657656
}
@@ -681,12 +680,11 @@
681680
g.zContentType = zType = P("CONTENT_TYPE");
682681
if( len>0 && zType ){
683682
blob_zero(&g.cgiIn);
684683
if( strcmp(zType,"application/x-www-form-urlencoded")==0
685684
|| 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 );
688686
len = fread(z, 1, len, g.httpIn);
689687
z[len] = 0;
690688
if( zType[0]=='a' ){
691689
add_param_list(z, '&');
692690
}else{
@@ -773,11 +771,11 @@
773771
774772
/* If no match is found and the name begins with an upper-case
775773
** letter, then check to see if there is an environment variable
776774
** with the given name.
777775
*/
778
- if( isupper(zName[0]) ){
776
+ if( fossil_isupper(zName[0]) ){
779777
const char *zValue = getenv(zName);
780778
if( zValue ){
781779
cgi_set_parameter_nocopy(zName, zValue);
782780
CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
783781
return zValue;
@@ -916,17 +914,17 @@
916914
char *zResult = 0;
917915
if( zInput==0 ){
918916
if( zLeftOver ) *zLeftOver = 0;
919917
return 0;
920918
}
921
- while( isspace(*zInput) ){ zInput++; }
919
+ while( fossil_isspace(*zInput) ){ zInput++; }
922920
zResult = zInput;
923
- while( *zInput && !isspace(*zInput) ){ zInput++; }
921
+ while( *zInput && !fossil_isspace(*zInput) ){ zInput++; }
924922
if( *zInput ){
925923
*zInput = 0;
926924
zInput++;
927
- while( isspace(*zInput) ){ zInput++; }
925
+ while( fossil_isspace(*zInput) ){ zInput++; }
928926
}
929927
if( zLeftOver ){ *zLeftOver = zInput; }
930928
return zResult;
931929
}
932930
@@ -941,11 +939,11 @@
941939
*/
942940
void cgi_handle_http_request(const char *zIpAddr){
943941
char *z, *zToken;
944942
int i;
945943
struct sockaddr_in remoteName;
946
- size_t size = sizeof(struct sockaddr_in);
944
+ socklen_t size = sizeof(struct sockaddr_in);
947945
char zLine[2000]; /* A single line of input. */
948946
949947
g.fullHttpReply = 1;
950948
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
951949
malformed_request();
@@ -969,11 +967,11 @@
969967
if( zToken[i] ) zToken[i++] = 0;
970968
cgi_setenv("PATH_INFO", zToken);
971969
cgi_setenv("QUERY_STRING", &zToken[i]);
972970
if( zIpAddr==0 &&
973971
getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName,
974
- (socklen_t*)&size)>=0
972
+ &size)>=0
975973
){
976974
zIpAddr = inet_ntoa(remoteName.sin_addr);
977975
}
978976
if( zIpAddr ){
979977
cgi_setenv("REMOTE_ADDR", zIpAddr);
@@ -986,15 +984,17 @@
986984
char *zFieldName;
987985
char *zVal;
988986
989987
zFieldName = extract_token(zLine,&zVal);
990988
if( zFieldName==0 || *zFieldName==0 ) break;
991
- while( isspace(*zVal) ){ zVal++; }
989
+ while( fossil_isspace(*zVal) ){ zVal++; }
992990
i = strlen(zVal);
993
- while( i>0 && isspace(zVal[i-1]) ){ i--; }
991
+ while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
994992
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
+ }
996996
if( strcmp(zFieldName,"content-length:")==0 ){
997997
cgi_setenv("CONTENT_LENGTH", zVal);
998998
}else if( strcmp(zFieldName,"content-type:")==0 ){
999999
cgi_setenv("CONTENT_TYPE", zVal);
10001000
}else if( strcmp(zFieldName,"cookie:")==0 ){
@@ -1050,11 +1050,11 @@
10501050
fossil_exit(1);
10511051
#else
10521052
int listener = -1; /* The server socket */
10531053
int connection; /* A socket for each individual connection */
10541054
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 */
10561056
int child; /* PID of the child process */
10571057
int nchildren = 0; /* Number of child processes */
10581058
struct timeval delay; /* How long to wait inside select() */
10591059
struct sockaddr_in inaddr; /* The socket address */
10601060
int opt = 1; /* setsockopt flag */
@@ -1113,12 +1113,11 @@
11131113
FD_ZERO(&readfds);
11141114
FD_SET( listener, &readfds);
11151115
select( listener+1, &readfds, 0, 0, &delay);
11161116
if( FD_ISSET(listener, &readfds) ){
11171117
lenaddr = sizeof(inaddr);
1118
- connection = accept(listener, (struct sockaddr*)&inaddr,
1119
- (socklen_t*) &lenaddr);
1118
+ connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
11201119
if( connection>=0 ){
11211120
child = fork();
11221121
if( child!=0 ){
11231122
if( child>0 ) nchildren++;
11241123
close(connection);
11251124
--- 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 @@
215215
int cTerm;
216216
217217
if( zGlobList==0 || zGlobList[0]==0 ) return "0";
218218
blob_zero(&expr);
219219
while( zGlobList[0] ){
220
- while( isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
220
+ while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ) zGlobList++;
221221
if( zGlobList[0]==0 ) break;
222222
if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){
223223
cTerm = zGlobList[0];
224224
zGlobList++;
225225
}else{
226226
cTerm = ',';
227227
}
228228
for(i=0; zGlobList[i] && zGlobList[i]!=cTerm; i++){}
229229
if( cTerm==',' ){
230
- while( i>0 && isspace(zGlobList[i-1]) ){ i--; }
230
+ while( i>0 && fossil_isspace(zGlobList[i-1]) ){ i--; }
231231
}
232232
blob_appendf(&expr, "%s%s GLOB '%.*q'", zSep, zVal, i, zGlobList);
233233
zSep = " OR ";
234234
if( cTerm!=',' && zGlobList[i] ) i++;
235235
zGlobList += i;
@@ -259,10 +259,11 @@
259259
Blob repo;
260260
Stmt q;
261261
int n;
262262
const char *zIgnoreFlag = find_option("ignore",0,1);
263263
int allFlag = find_option("dotfiles",0,0)!=0;
264
+ int outputManifest = db_get_boolean("manifest",0);
264265
265266
db_must_be_within_tree();
266267
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
267268
n = strlen(g.zLocalRoot);
268269
blob_init(&path, g.zLocalRoot, n-1);
@@ -270,16 +271,18 @@
270271
zIgnoreFlag = db_get("ignore-glob", 0);
271272
}
272273
vfile_scan(0, &path, blob_size(&path), allFlag);
273274
db_prepare(&q,
274275
"SELECT x FROM sfile"
275
- " WHERE x NOT IN ('manifest','manifest.uuid','_FOSSIL_',"
276
+ " WHERE x NOT IN ('%s','%s','_FOSSIL_',"
276277
"'_FOSSIL_-journal','.fos','.fos-journal',"
277278
"'_FOSSIL_-wal','_FOSSIL_-shm','.fos-wal',"
278279
"'.fos-shm')"
279280
" AND NOT %s"
280281
" ORDER BY 1",
282
+ outputManifest ? "manifest" : "_FOSSIL_",
283
+ outputManifest ? "manifest.uuid" : "_FOSSIL_",
281284
glob_expr("x", zIgnoreFlag)
282285
);
283286
if( file_tree_name(g.zRepositoryName, &repo, 0) ){
284287
db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo);
285288
}
@@ -415,11 +418,11 @@
415418
blob_add_cr(&text);
416419
#endif
417420
blob_write_to_file(&text, zFile);
418421
zCmd = mprintf("%s \"%s\"", zEditor, zFile);
419422
printf("%s\n", zCmd);
420
- if( portable_system(zCmd) ){
423
+ if( fossil_system(zCmd) ){
421424
fossil_panic("editor aborted");
422425
}
423426
blob_reset(&text);
424427
blob_read_from_file(&text, zFile);
425428
blob_remove_cr(&text);
@@ -429,20 +432,20 @@
429432
while( blob_line(&text, &line) ){
430433
int i, n;
431434
char *z;
432435
n = blob_size(&line);
433436
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++){}
435438
if( i<n && z[i]=='#' ) continue;
436439
if( i<n || blob_size(pComment)>0 ){
437440
blob_appendf(pComment, "%b", &line);
438441
}
439442
}
440443
blob_reset(&text);
441444
zComment = blob_str(pComment);
442445
i = strlen(zComment);
443
- while( i>0 && isspace(zComment[i-1]) ){ i--; }
446
+ while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
444447
blob_resize(pComment, i);
445448
}
446449
447450
/*
448451
** Populate the Global.aCommitFile[] based on the command line arguments
@@ -461,11 +464,11 @@
461464
void select_commit_files(void){
462465
if( g.argc>2 ){
463466
int ii;
464467
Blob b;
465468
blob_zero(&b);
466
- g.aCommitFile = malloc(sizeof(int)*(g.argc-1));
469
+ g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1));
467470
468471
for(ii=2; ii<g.argc; ii++){
469472
int iId;
470473
file_tree_name(g.argv[ii], &b, 1);
471474
iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b));
@@ -513,11 +516,11 @@
513516
" WHERE datetime(mtime)>=%Q"
514517
" AND type='ci' AND objid=%d",
515518
zDate, rid
516519
);
517520
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?)"
519522
" Use -f to override.", zUuid, zDate);
520523
}
521524
#endif
522525
}
523526
@@ -537,41 +540,177 @@
537540
zDate[10] = 'T';
538541
return zDate;
539542
}
540543
541544
/*
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.
548546
*/
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;
552691
}
692
+
553693
554694
/*
555695
** COMMAND: ci
556696
** COMMAND: commit
557697
**
558698
** Usage: %fossil commit ?OPTIONS? ?FILE...?
559699
**
560700
** Create a new version containing all of the changes in the current
561701
** 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
573712
** to be placed in the named branch. The --bgcolor option can be followed
574713
** by a color name (ex: '#ffc0c0') to specify the background color of
575714
** entries in the new branch when shown in the web timeline interface.
576715
**
577716
** A check-in is not permitted to fork unless the --force or -f
@@ -581,44 +720,58 @@
581720
** Children of private check-ins are automatically private.
582721
**
583722
** Options:
584723
**
585724
** --comment|-m COMMENT-TEXT
725
+** --message-file|-M COMMENT-FILE
586726
** --branch NEW-BRANCH-NAME
587727
** --bgcolor COLOR
588728
** --nosign
589729
** --force|-f
590730
** --private
731
+** --baseline
732
+** --delta
591733
**
592734
*/
593735
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 */
601744
int noSign = 0; /* True to omit signing the manifest using GPG */
602745
int isAMerge = 0; /* True if checking in a merge */
603746
int forceFlag = 0; /* Force a fork */
747
+ int forceDelta = 0; /* Force a delta-manifest */
748
+ int forceBaseline = 0; /* Force a baseline-manifest */
604749
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 */
606753
const char *zBranch; /* Create a new branch with this name */
607754
const char *zBgColor; /* Set background color when branching */
608755
const char *zDateOvrd; /* Override date string */
609756
const char *zUserOvrd; /* Override user name */
610757
const char *zComFile; /* Read commit message from this file */
611
- Blob filename; /* complete filename */
612
- Blob manifest;
758
+ Blob manifest; /* Manifest in baseline form */
613759
Blob muuid; /* Manifest uuid */
614
- Blob mcksum; /* Self-checksum on the manifest */
615760
Blob cksum1, cksum2; /* Before and after commit checksums */
616761
Blob cksum1b; /* Checksum recorded in the manifest */
762
+ int szD; /* Size of the delta manifest */
763
+ int szB; /* Size of the baseline manifest */
617764
618765
url_proxy_options();
619766
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;
620773
zComment = find_option("comment","m",1);
621774
forceFlag = find_option("force", "f", 0)!=0;
622775
zBranch = find_option("branch","b",1);
623776
zBgColor = find_option("bgcolor",0,1);
624777
zComFile = find_option("message-file", "M", 1);
@@ -630,11 +783,23 @@
630783
zDateOvrd = find_option("date-override",0,1);
631784
zUserOvrd = find_option("user-override",0,1);
632785
db_must_be_within_tree();
633786
noSign = db_get_boolean("omitsign", 0)|noSign;
634787
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
788
+ useCksum = db_get_boolean("repo-cksum", 1);
789
+ outputManifest = db_get_boolean("manifest", 0);
635790
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
+ }
636801
637802
/* Get the ID of the parent manifest artifact */
638803
vid = db_lget_int("checkout", 0);
639804
if( content_is_private(vid) ){
640805
g.markPrivate = 1;
@@ -681,15 +846,15 @@
681846
*/
682847
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
683848
fossil_fatal("no such user: %s", g.zLogin);
684849
}
685850
851
+ hasChanges = unsaved_changes();
686852
db_begin_transaction();
687853
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");
691856
}
692857
693858
/* If one or more files that were named on the command line have not
694859
** been modified, bail out now.
695860
*/
@@ -720,11 +885,11 @@
720885
" WHERE tagid=%d AND rid=%d AND tagtype>0",
721886
TAG_CLOSED, vid) ){
722887
fossil_fatal("cannot commit against a closed leaf");
723888
}
724889
725
- vfile_aggregate_checksum_disk(vid, &cksum1);
890
+ if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
726891
if( zComment ){
727892
blob_zero(&comment);
728893
blob_append(&comment, zComment, -1);
729894
}else if( zComFile ){
730895
blob_zero(&comment);
@@ -775,113 +940,86 @@
775940
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
776941
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
777942
}
778943
db_finalize(&q);
779944
780
- /* Create the manifest */
781
- blob_zero(&manifest);
945
+ /* Create the new manifest */
782946
if( blob_size(&comment)==0 ){
783947
blob_append(&comment, "(no comment)", -1);
784948
}
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
+ }
8741004
if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
8751005
Blob ans;
8761006
blob_zero(&ans);
8771007
prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
8781008
if( blob_str(&ans)[0]!='y' ){
8791009
fossil_exit(1);
8801010
}
8811011
}
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 ){
8831021
zManifestFile = mprintf("%smanifest", g.zLocalRoot);
8841022
blob_write_to_file(&manifest, zManifestFile);
8851023
blob_reset(&manifest);
8861024
blob_read_from_file(&manifest, zManifestFile);
8871025
free(zManifestFile);
@@ -893,11 +1031,11 @@
8931031
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid);
8941032
manifest_crosslink(nvid, &manifest);
8951033
content_deltify(vid, nvid, 0);
8961034
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
8971035
printf("New_Version: %s\n", zUuid);
898
- if( !file_exists_in_checkout(vid, "manifest.uuid") ){
1036
+ if( outputManifest ){
8991037
zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
9001038
blob_zero(&muuid);
9011039
blob_appendf(&muuid, "%s\n", zUuid);
9021040
blob_write_to_file(&muuid, zManifestFile);
9031041
free(zManifestFile);
@@ -914,45 +1052,51 @@
9141052
" WHERE file_is_selected(id);"
9151053
, vid, nvid
9161054
);
9171055
db_lset_int("checkout", nvid);
9181056
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
+ }
9431083
}
9441084
9451085
/* Clear the undo/redo stack */
9461086
undo_reset();
9471087
9481088
/* Commit */
9491089
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
1090
+ if( testRun ){
1091
+ db_end_transaction(1);
1092
+ exit(1);
1093
+ }
9501094
db_end_transaction(0);
9511095
9521096
if( !g.markPrivate ){
9531097
autosync(AUTOSYNC_PUSH);
9541098
}
9551099
if( count_nonbranch_children(vid)>1 ){
9561100
printf("**** warning: a fork has occurred *****\n");
9571101
}
9581102
}
9591103
--- 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 @@
7878
7979
/*
8080
** Load a vfile from a record ID.
8181
*/
8282
void load_vfile_from_rid(int vid){
83
- Blob manifest;
84
-
8583
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
8684
return;
8785
}
88
- content_get(vid, &manifest);
89
- vfile_build(vid, &manifest);
90
- blob_reset(&manifest);
86
+ vfile_build(vid);
9187
}
9288
9389
/*
9490
** Set or clear the vfile.isexe flag for a file.
9591
*/
9692
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);
99103
}
100104
101105
/*
102106
** Set or clear the execute permission bit (as appropriate) for all
103107
** 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.
109140
*/
110141
void manifest_to_disk(int vid){
111142
char *zManFile;
112143
Blob manifest;
113144
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);
147149
zManFile = mprintf("%smanifest", g.zLocalRoot);
148150
blob_write_to_file(&manifest, zManFile);
149151
free(zManFile);
150
- }
151
- if( !seenManifestUuid ){
152152
blob_zero(&hash);
153153
sha1sum_blob(&manifest, &hash);
154154
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
155155
blob_append(&hash, "\n", 1);
156156
blob_write_to_file(&hash, zManFile);
157157
free(zManFile);
158158
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
+ }
159170
}
171
+
160172
}
161173
162174
/*
163175
** COMMAND: checkout
164176
** COMMAND: co
@@ -228,10 +240,11 @@
228240
}
229241
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
230242
if( !keepFlag ){
231243
vfile_to_disk(vid, 0, 1, promptFlag);
232244
}
245
+ checkout_set_all_exe(vid);
233246
manifest_to_disk(vid);
234247
db_lset_int("checkout", vid);
235248
undo_reset();
236249
db_multi_exec("DELETE FROM vmerge");
237250
if( !keepFlag ){
238251
--- 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 @@
3939
zRand = db_text(0, "SELECT hex(randomblob(10))");
4040
zOut = mprintf("out-%s", zRand);
4141
zIn = mprintf("in-%z", zRand);
4242
blob_write_to_file(pIn, zOut);
4343
zCmd = mprintf("%s %s %s", zBase, zIn, zOut);
44
- rc = portable_system(zCmd);
44
+ rc = fossil_system(zCmd);
4545
free(zCmd);
4646
if( rc==0 ){
4747
if( pOut==pIn ){
4848
blob_reset(pIn);
4949
}
5050
--- 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 @@
3838
int doIndent = 0;
3939
char zBuf[400];
4040
int lineCnt = 0;
4141
4242
for(;;){
43
- while( isspace(zText[0]) ){ zText++; }
43
+ while( fossil_isspace(zText[0]) ){ zText++; }
4444
if( zText[0]==0 ){
4545
if( doIndent==0 ){
4646
printf("\n");
4747
lineCnt = 1;
4848
}
4949
return lineCnt;
5050
}
5151
for(sk=si=i=k=0; zText[i] && k<tlen; i++){
5252
char c = zText[i];
53
- if( isspace(c) ){
53
+ if( fossil_isspace(c) ){
5454
si = i;
5555
sk = k;
5656
if( k==0 || zBuf[k-1]!=' ' ){
5757
zBuf[k++] = ' ';
5858
}
5959
}else{
6060
zBuf[k] = c;
61
- if( c=='-' && k>0 && isalpha(zBuf[k-1]) ){
61
+ if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){
6262
si = i+1;
6363
sk = k+1;
6464
}
6565
k++;
6666
}
6767
--- 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 @@
2525
#ifndef _FILE_OFFSET_BITS
2626
# define _FILE_OFFSET_BITS 64
2727
#endif
2828
#define _LARGEFILE_SOURCE 1
2929
30
+#ifndef _RC_COMPILE_
3031
3132
/*
3233
** System header files used by all modules
3334
*/
3435
#include <unistd.h>
3536
#include <stdio.h>
3637
#include <stdlib.h>
37
-#include <ctype.h>
38
+/* #include <ctype.h> // do not use - causes problems */
3839
#include <string.h>
3940
#include <stdarg.h>
4041
#include <assert.h>
42
+
43
+#endif
44
+
4145
#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
4246
# if defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
4347
typedef int socklen_t;
4448
# endif
4549
# ifndef _WIN32
@@ -49,71 +53,79 @@
4953
# include <sys/types.h>
5054
# include <signal.h>
5155
# include <pwd.h>
5256
#endif
5357
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
+
5485
#include "sqlite3.h"
5586
5687
/*
5788
** Typedef for a 64-bit integer
5889
*/
59
-typedef sqlite_int64 i64;
60
-typedef sqlite_uint64 u64;
90
+typedef sqlite3_int64 i64;
91
+typedef sqlite3_uint64 u64;
6192
6293
/*
6394
** Unsigned character type
6495
*/
6596
typedef unsigned char u8;
6697
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
-
10998
/* In the timeline, check-in messages are truncated at the first space
11099
** that is more than MX_CKIN_MSG from the beginning, or at the first
111100
** paragraph break that is more than MN_CKIN_MSG from the beginning.
112101
*/
113102
#define MN_CKIN_MSG 100
114103
#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
+
115127
116128
/* Unset the following to disable internationalization code. */
117129
#ifndef FOSSIL_I18N
118130
# define FOSSIL_I18N 1
119131
#endif
@@ -124,5 +136,7 @@
124136
#endif
125137
#ifndef CODESET
126138
# undef FOSSIL_I18N
127139
# define FOSSIL_I18N 0
128140
#endif
141
+
142
+#endif /* _RC_COMPILE_ */
129143
--- 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 @@
7474
{ "footer", CONFIGSET_SKIN },
7575
{ "logo-mimetype", CONFIGSET_SKIN },
7676
{ "logo-image", CONFIGSET_SKIN },
7777
{ "project-name", CONFIGSET_PROJ },
7878
{ "project-description", CONFIGSET_PROJ },
79
+ { "manifest", CONFIGSET_PROJ },
7980
{ "index-page", CONFIGSET_SKIN },
8081
{ "timeline-block-markup", CONFIGSET_SKIN },
8182
{ "timeline-max-comment", CONFIGSET_SKIN },
8283
{ "ticket-table", CONFIGSET_TKT },
8384
{ "ticket-common", CONFIGSET_TKT },
@@ -454,11 +455,11 @@
454455
}else{
455456
zServer = db_get("last-sync-url", 0);
456457
if( zServer==0 ){
457458
fossil_fatal("no server specified");
458459
}
459
- zPw = db_get("last-sync-pw", 0);
460
+ zPw = unobscure(db_get("last-sync-pw", 0));
460461
}
461462
url_parse(zServer);
462463
if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
463464
user_select();
464465
url_enable_proxy("via proxy: ");
465466
--- 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 @@
8080
if( contentCache.n>500 || contentCache.szTotal>50000000 ){
8181
content_cache_expire_oldest();
8282
}
8383
if( contentCache.n>=contentCache.nAlloc ){
8484
contentCache.nAlloc = contentCache.nAlloc*2 + 10;
85
- contentCache.a = realloc(contentCache.a,
85
+ contentCache.a = fossil_realloc(contentCache.a,
8686
contentCache.nAlloc*sizeof(contentCache.a[0]));
87
- if( contentCache.a==0 ) fossil_panic("out of memory");
8887
}
8988
p = &contentCache.a[contentCache.n++];
9089
p->rid = rid;
9190
p->age = contentCache.nextAge++;
9291
contentCache.szTotal += blob_size(pBlob);
@@ -240,22 +239,20 @@
240239
int nAlloc = 10;
241240
int *a = 0;
242241
int mx;
243242
Blob delta, next;
244243
245
- a = malloc( sizeof(a[0])*nAlloc );
246
- if( a==0 ) fossil_panic("out of memory");
244
+ a = fossil_malloc( sizeof(a[0])*nAlloc );
247245
a[0] = rid;
248246
a[1] = nextRid;
249247
n = 1;
250248
while( !bag_find(&contentCache.inCache, nextRid)
251249
&& (nextRid = findSrcid(nextRid))>0 ){
252250
n++;
253251
if( n>=nAlloc ){
254252
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]));
257254
}
258255
a[n] = nextRid;
259256
}
260257
mx = n;
261258
rc = content_get(a[n], pBlob);
@@ -284,25 +281,29 @@
284281
}
285282
return rc;
286283
}
287284
288285
/*
289
-** COMMAND: artifact
286
+** COMMAND: artifact
290287
**
291
-** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME?
288
+** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
292289
**
293290
** Extract an artifact by its SHA1 hash and write the results on
294291
** standard output, or if the optional 4th argument is given, in
295292
** the named output file.
293
+**
294
+** Options:
295
+**
296
+** -R|--repository FILE Extract artifacts from repository FILE
296297
*/
297298
void artifact_cmd(void){
298299
int rid;
299300
Blob content;
300301
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?");
302304
zFile = g.argc==4 ? g.argv[3] : "-";
303
- db_must_be_within_tree();
304305
rid = name_to_rid(g.argv[2]);
305306
content_get(rid, &content);
306307
blob_write_to_file(&content, zFile);
307308
}
308309
@@ -324,50 +325,100 @@
324325
db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
325326
blob_uncompress(&content, &content);
326327
blob_write_to_file(&content, zFile);
327328
}
328329
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
+
329339
/*
330340
** When a record is converted from a phantom to a real record,
331341
** if that record has other records that are derived by delta,
332342
** 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.
333348
**
334349
** Tail recursion is used to minimize stack depth.
335350
*/
336351
void after_dephantomize(int rid, int linkFlag){
337352
Stmt q;
338353
int nChildAlloc = 0;
339354
int *aChild = 0;
355
+ Blob content;
340356
357
+ if( ignoreDephantomizations ) return;
341358
while( rid ){
342359
int nChildUsed = 0;
343360
int i;
361
+
362
+ /* Parse the object rid itself */
344363
if( linkFlag ){
345
- Blob content;
346364
content_get(rid, &content);
347365
manifest_crosslink(rid, &content);
348366
blob_reset(&content);
349367
}
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;
350392
db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
351393
while( db_step(&q)==SQLITE_ROW ){
352394
int child = db_column_int(&q, 0);
353395
if( nChildUsed>=nChildAlloc ){
354396
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));
357398
}
358399
aChild[nChildUsed++] = child;
359400
}
360401
db_finalize(&q);
361402
for(i=1; i<nChildUsed; i++){
362403
after_dephantomize(aChild[i], 1);
363404
}
405
+
406
+ /* Tail recursion for the common case where only a single artifact
407
+ ** is derived by delta from rid... */
364408
rid = nChildUsed>0 ? aChild[0] : 0;
365409
linkFlag = 1;
366410
}
367411
free(aChild);
368412
}
413
+
414
+/*
415
+** Turn dephantomization processing on or off.
416
+*/
417
+void content_enable_dephantomize(int onoff){
418
+ ignoreDephantomizations = !onoff;
419
+}
369420
370421
/*
371422
** Write content into the database. Return the record ID. If the
372423
** content is already in the database, just return the record ID.
373424
**
374425
--- 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
+32 -10
--- src/db.c
+++ src/db.c
@@ -1510,25 +1510,27 @@
15101510
int width; /* Width of display. 0 for boolean values */
15111511
char const *def; /* Default value */
15121512
};
15131513
#endif /* INTERFACE */
15141514
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" },
15221522
{ "editor", 0, 16, "" },
15231523
{ "gdiff-command", 0, 16, "gdiff" },
15241524
{ "ignore-glob", 0, 40, "" },
15251525
{ "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" },
15281529
{ "pgp-command", 0, 32, "gpg --clearsign -o " },
15291530
{ "proxy", 0, 32, "off" },
1531
+ { "repo-cksum", 0, 0, "on" },
15301532
{ "ssh-command", 0, 32, "" },
15311533
{ "web-browser", 0, 32, "" },
15321534
{ 0,0,0,0 }
15331535
};
15341536
@@ -1548,23 +1550,25 @@
15481550
** auto-captcha If enabled, the Login page provides a button to
15491551
** fill in the captcha password. Default: on
15501552
**
15511553
** auto-shun If enabled, automatically pull the shunning list
15521554
** from a server to which the client autosyncs.
1555
+** Default: on
15531556
**
15541557
** autosync If enabled, automatically pull prior to commit
15551558
** or update and automatically push after commit or
15561559
** tag or branch creation. If the value is "pullonly"
15571560
** then only pull operations occur automatically.
1561
+** Default: on
15581562
**
15591563
** binary-glob The VALUE is a comma-separated list of GLOB patterns
15601564
** that should be treated as binary files for merging
15611565
** purposes. Example: *.xml
15621566
**
15631567
** clearsign When enabled, fossil will attempt to sign all commits
15641568
** with gpg. When disabled (the default), commits will
1565
-** be unsigned.
1569
+** be unsigned. Default: off
15661570
**
15671571
** diff-command External command to run when performing a diff.
15681572
** If undefined, the internal text diff will be used.
15691573
**
15701574
** dont-push Prevent this repository from pushing from client to
@@ -1585,10 +1589,14 @@
15851589
** localauth If enabled, require that HTTP connections from
15861590
** 127.0.0.1 be authenticated by password. If
15871591
** false, all HTTP requests from localhost have
15881592
** unrestricted access to the repository.
15891593
**
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
+**
15901598
** mtime-changes Use file modification times (mtimes) to detect when
15911599
** files have been modified. (Default "on".)
15921600
**
15931601
** pgp-command Command used to clear-sign manifests at check-in.
15941602
** The default is "gpg --clearsign -o ".
@@ -1595,10 +1603,15 @@
15951603
**
15961604
** proxy URL of the HTTP proxy. If undefined or "off" then
15971605
** the "http_proxy" environment variable is consulted.
15981606
** If the http_proxy environment variable is undefined
15991607
** 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.
16001613
**
16011614
** ssh-command Command used to talk to a remote machine with
16021615
** the "ssh://" protocol.
16031616
**
16041617
** web-browser A shell command used to launch your preferred
@@ -1622,24 +1635,33 @@
16221635
for(i=0; ctrlSettings[i].name; i++){
16231636
print_setting(ctrlSettings[i].name);
16241637
}
16251638
}else if( g.argc==3 || g.argc==4 ){
16261639
const char *zName = g.argv[2];
1640
+ int isManifest;
16271641
int n = strlen(zName);
16281642
for(i=0; ctrlSettings[i].name; i++){
16291643
if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
16301644
}
16311645
if( !ctrlSettings[i].name ){
16321646
fossil_fatal("no such setting: %s", zName);
16331647
}
1648
+ isManifest = strcmp(ctrlSettings[i].name, "manifest")==0;
1649
+ if( isManifest && globalFlag ){
1650
+ fossil_fatal("cannot set 'manifest' globally");
1651
+ }
16341652
if( unsetFlag ){
16351653
db_unset(ctrlSettings[i].name, globalFlag);
16361654
}else if( g.argc==4 ){
16371655
db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
16381656
}else{
1657
+ isManifest = 0;
16391658
print_setting(ctrlSettings[i].name);
16401659
}
1660
+ if( isManifest ){
1661
+ manifest_to_disk(db_lget_int("checkout", 0));
1662
+ }
16411663
}else{
16421664
usage("?PROPERTY? ?VALUE?");
16431665
}
16441666
}
16451667
16461668
--- 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 @@
195195
/*
196196
** Compute a 32-bit checksum on the N-byte buffer. Return the result.
197197
*/
198198
static unsigned int checksum(const char *zIn, size_t N){
199199
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;
201204
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]);
206209
z += 16;
207210
N -= 16;
208211
}
209212
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];
211217
z += 4;
212218
N -= 4;
213219
}
220
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
214221
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);
218225
default: ;
219226
}
220
- return sum;
227
+ return sum3;
221228
}
222229
223230
/*
224231
** Create a new delta.
225232
**
@@ -317,12 +324,11 @@
317324
318325
/* Compute the hash table used to locate matching sections in the
319326
** source file.
320327
*/
321328
nHash = lenSrc/NHASH;
322
- collide = malloc( nHash*2*sizeof(int) );
323
- if( collide==0 ) return -1;
329
+ collide = fossil_malloc( nHash*2*sizeof(int) );
324330
landmark = &collide[nHash];
325331
memset(landmark, -1, nHash*sizeof(int));
326332
memset(collide, -1, nHash*sizeof(int));
327333
for(i=0; i<lenSrc-NHASH; i+=NHASH){
328334
int hv;
@@ -513,11 +519,13 @@
513519
int lenDelta, /* Length of the delta */
514520
char *zOut /* Write the output into this preallocated buffer */
515521
){
516522
unsigned int limit;
517523
unsigned int total = 0;
524
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
518525
char *zOrigOut = zOut;
526
+#endif
519527
520528
limit = getInt(&zDelta, &lenDelta);
521529
if( *zDelta!='\n' ){
522530
/* ERROR: size integer not terminated by "\n" */
523531
return -1;
@@ -568,14 +576,16 @@
568576
break;
569577
}
570578
case ';': {
571579
zDelta++; lenDelta--;
572580
zOut[0] = 0;
581
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
573582
if( cnt!=checksum(zOrigOut, total) ){
574583
/* ERROR: bad checksum */
575584
return -1;
576585
}
586
+#endif
577587
if( total!=limit ){
578588
/* ERROR: generated size does not match predicted size */
579589
return -1;
580590
}
581591
return total;
582592
--- 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 @@
9696
}
9797
}
9898
if( j>LENGTH_MASK ){
9999
return 0;
100100
}
101
- a = malloc( nLine*sizeof(a[0]) );
102
- if( a==0 ) fossil_panic("out of memory");
101
+ a = fossil_malloc( nLine*sizeof(a[0]) );
103102
memset(a, 0, nLine*sizeof(a[0]) );
103
+ if( n==0 ){
104
+ *pnLine = 0;
105
+ return a;
106
+ }
104107
105108
/* Fill in the array */
106109
for(i=0; i<nLine; i++){
107110
a[i].z = z;
108111
for(j=0; z[j] && z[j]!='\n'; j++){}
109112
k = j;
110
- while( ignoreWS && k>0 && isspace(z[k-1]) ){ k--; }
113
+ while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
111114
for(h=0, x=0; x<k; x++){
112115
h = h ^ (h<<2) ^ z[x];
113116
}
114117
a[i].h = h = (h<<LENGTH_MASK_SZ) | k;;
115118
h2 = h % nLine;
@@ -141,18 +144,11 @@
141144
142145
/*
143146
** Expand the size of aEdit[] array to hold nEdit elements.
144147
*/
145148
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));
154150
p->nEditAlloc = nEdit;
155151
}
156152
157153
/*
158154
** Append a new COPY/DELETE/INSERT triple.
@@ -645,12 +641,11 @@
645641
memset(p, 0, sizeof(*p));
646642
p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
647643
if( p->c.aTo==0 ){
648644
return 1;
649645
}
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 );
652647
for(i=0; i<p->c.nTo; i++){
653648
p->aOrig[i].z = p->c.aTo[i].z;
654649
p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
655650
p->aOrig[i].zSrc = 0;
656651
}
657652
--- 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 @@
2020
#include "config.h"
2121
#include "diffcmd.h"
2222
#include <assert.h>
2323
2424
/*
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 */
4329
4430
/*
4531
** Show the difference between two files, one in memory and one on disk.
4632
**
4733
** The difference is the set of edits needed to transform pFile1 into
@@ -53,24 +39,30 @@
5339
static void diff_file(
5440
Blob *pFile1, /* In memory content to compare from */
5541
const char *zFile2, /* On disk content to compare to */
5642
const char *zName, /* Display name of the file */
5743
const char *zDiffCmd, /* Command for comparison */
58
- int ignoreEolWs /* Ignore whitespace at end of lines */
44
+ int ignoreEolWs /* Ignore whitespace at end of line */
5945
){
6046
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 */
6350
6451
/* Read content of zFile2 into memory */
6552
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
+ }
6759
6860
/* Compute and output the differences */
6961
blob_zero(&out);
7062
text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
71
- printf("--- %s\n+++ %s\n", zName, zName);
63
+ printf("--- %s\n+++ %s\n", zName, zName2);
7264
printf("%s\n", blob_str(&out));
7365
7466
/* Release memory resources */
7567
blob_reset(&file2);
7668
blob_reset(&out);
@@ -94,11 +86,11 @@
9486
shell_escape(&cmd, blob_str(&nameFile1));
9587
blob_append(&cmd, " ", 1);
9688
shell_escape(&cmd, zFile2);
9789
9890
/* Run the external diff command */
99
- portable_system(blob_str(&cmd));
91
+ fossil_system(blob_str(&cmd));
10092
10193
/* Delete the temporary file and clean up memory used */
10294
unlink(blob_str(&nameFile1));
10395
blob_reset(&nameFile1);
10496
blob_reset(&cmd);
@@ -148,11 +140,11 @@
148140
shell_escape(&cmd, zTemp1);
149141
blob_append(&cmd, " ", 1);
150142
shell_escape(&cmd, zTemp2);
151143
152144
/* Run the external diff command */
153
- portable_system(blob_str(&cmd));
145
+ fossil_system(blob_str(&cmd));
154146
155147
/* Delete the temporary file and clean up memory used */
156148
unlink(zTemp1);
157149
unlink(zTemp2);
158150
blob_reset(&cmd);
@@ -183,16 +175,20 @@
183175
** files on disk and the check-out on which they are based.
184176
*/
185177
static void diff_all_against_disk(
186178
const char *zFrom, /* Version to difference from */
187179
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 */
189181
){
190182
int vid;
191183
Blob sql;
192184
Stmt q;
185
+ int ignoreEolWs; /* Ignore end-of-line whitespace */
186
+ int asNewFile; /* Treat non-existant files as empty files */
193187
188
+ ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0;
189
+ asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
194190
vid = db_lget_int("checkout", 0);
195191
vfile_check_signature(vid, 1);
196192
blob_zero(&sql);
197193
db_begin_transaction();
198194
if( zFrom ){
@@ -235,31 +231,44 @@
235231
while( db_step(&q)==SQLITE_ROW ){
236232
const char *zPathname = db_column_text(&q,0);
237233
int isDeleted = db_column_int(&q, 1);
238234
int isChnged = db_column_int(&q,2);
239235
int isNew = db_column_int(&q,3);
236
+ int srcid = db_column_int(&q, 4);
240237
char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
238
+ char *zToFree = zFullName;
239
+ int showDiff = 1;
241240
if( isDeleted ){
242241
printf("DELETED %s\n", zPathname);
242
+ if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; }
243243
}else if( access(zFullName, 0) ){
244244
printf("MISSING %s\n", zPathname);
245
+ if( !asNewFile ){ showDiff = 0; }
245246
}else if( isNew ){
246247
printf("ADDED %s\n", zPathname);
248
+ srcid = 0;
249
+ if( !asNewFile ){ showDiff = 0; }
247250
}else if( isChnged==3 ){
248251
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 ){
251256
Blob content;
252
- content_get(srcid, &content);
257
+ if( srcid>0 ){
258
+ content_get(srcid, &content);
259
+ }else{
260
+ blob_zero(&content);
261
+ }
253262
printf("Index: %s\n======================================="
254263
"============================\n",
255264
zPathname
256265
);
257266
diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
258267
blob_reset(&content);
259268
}
260
- free(zFullName);
269
+ free(zToFree);
261270
}
262271
db_finalize(&q);
263272
db_end_transaction(1); /* ROLLBACK */
264273
}
265274
@@ -284,66 +293,98 @@
284293
diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
285294
blob_reset(&v1);
286295
blob_reset(&v2);
287296
blob_reset(&fname);
288297
}
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
+}
289330
290331
/*
291332
** Output the differences between two check-ins.
292333
*/
293334
static void diff_all_two_versions(
294335
const char *zFrom,
295336
const char *zTo,
296337
const char *zDiffCmd,
297
- int ignoreEolWs
338
+ int diffFlags
298339
){
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);
301351
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 ){
306353
int cmp;
307
- if( iFrom>=mFrom.nFile ){
354
+ if( pFromFile==0 ){
308355
cmp = +1;
309
- }else if( iTo>=mTo.nFile ){
356
+ }else if( pToFile==0 ){
310357
cmp = -1;
311358
}else{
312
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
359
+ cmp = strcmp(pFromFile->zName, pToFile->zName);
313360
}
314361
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);
345386
}
346387
347388
/*
348389
** COMMAND: diff
349390
** COMMAND: gdiff
@@ -366,33 +407,41 @@
366407
**
367408
** The "-i" command-line option forces the use of the internal diff logic
368409
** rather than any external diff program that might be configured using
369410
** the "setting" command. If no external diff program is configured, then
370411
** 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.
371415
*/
372416
void diff_cmd(void){
373417
int isGDiff; /* True for gdiff. False for normal diff */
374418
int isInternDiff; /* True for internal diff */
419
+ int hasNFlag; /* True if -N or --new-file flag is used */
375420
const char *zFrom; /* Source version number */
376421
const char *zTo; /* Target version number */
377422
const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
423
+ int diffFlags = 0; /* Flags to control the DIFF */
378424
379425
isGDiff = g.argv[1][0]=='g';
380426
isInternDiff = find_option("internal","i",0)!=0;
381427
zFrom = find_option("from", "r", 1);
382428
zTo = find_option("to", 0, 1);
429
+ hasNFlag = find_option("new-file","N",0)!=0;
430
+
383431
432
+ if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
384433
if( zTo==0 ){
385434
db_must_be_within_tree();
386435
verify_all_options();
387436
if( !isInternDiff ){
388437
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
389438
}
390439
if( g.argc==3 ){
391440
diff_one_against_disk(zFrom, zDiffCmd, 0);
392441
}else{
393
- diff_all_against_disk(zFrom, zDiffCmd, 0);
442
+ diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
394443
}
395444
}else if( zFrom==0 ){
396445
fossil_fatal("must use --from if --to is present");
397446
}else{
398447
db_find_and_open_repository(1);
@@ -401,9 +450,9 @@
401450
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
402451
}
403452
if( g.argc==3 ){
404453
diff_one_two_versions(zFrom, zTo, zDiffCmd, 0);
405454
}else{
406
- diff_all_two_versions(zFrom, zTo, zDiffCmd, 0);
455
+ diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
407456
}
408457
}
409458
}
410459
--- 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 @@
290290
if( zName[i]=='.' ) z = &zName[i+1];
291291
}
292292
len = strlen(z);
293293
if( len<sizeof(zSuffix)-1 ){
294294
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]);
296296
first = 0;
297297
last = sizeof(aMime)/sizeof(aMime[0]);
298298
while( first<=last ){
299299
int c;
300300
i = (first+last)/2;
@@ -388,37 +388,36 @@
388388
goto doc_not_found;
389389
}
390390
391391
if( rid==0 ){
392392
Stmt s;
393
- Blob baseline;
394
- Manifest m;
393
+ Manifest *pM;
394
+ ManifestFile *pFile;
395395
396396
/* Add the vid baseline to the cache */
397397
if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
398398
db_multi_exec("DELETE FROM vcache");
399399
}
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 ){
404402
goto doc_not_found;
405403
}
406404
db_prepare(&s,
407405
"INSERT INTO vcache(vid,fname,rid)"
408406
" SELECT %d, :fname, rid FROM blob"
409407
" WHERE uuid=:uuid",
410408
vid
411409
);
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);
415414
db_step(&s);
416415
db_reset(&s);
417416
}
418417
db_finalize(&s);
419
- manifest_clear(&m);
418
+ manifest_destroy(pM);
420419
421420
/* Try again to find the file */
422421
rid = db_int(0, "SELECT rid FROM vcache"
423422
" WHERE vid=%d AND fname=%Q", vid, zName);
424423
}
425424
--- 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 @@
4444
default: count++; break;
4545
}
4646
i++;
4747
}
4848
i = 0;
49
- zOut = malloc( count+1 );
50
- if( zOut==0 ) return 0;
49
+ zOut = fossil_malloc( count+1 );
5150
while( n-->0 && (c = *zIn)!=0 ){
5251
switch( c ){
5352
case '<':
5453
zOut[i++] = '&';
5554
zOut[i++] = 'l';
@@ -100,11 +99,11 @@
10099
int i = 0;
101100
int count = 0;
102101
char *zOut;
103102
int other;
104103
# define IsSafeChar(X) \
105
- (isalnum(X) || (X)=='.' || (X)=='$' \
104
+ (fossil_isalnum(X) || (X)=='.' || (X)=='$' \
106105
|| (X)=='~' || (X)=='-' || (X)=='_' || (X)==other)
107106
108107
if( zIn==0 ) return 0;
109108
if( n<0 ) n = strlen(zIn);
110109
other = encodeSlash ? 'a' : '/';
@@ -115,12 +114,11 @@
115114
count += 3;
116115
}
117116
i++;
118117
}
119118
i = 0;
120
- zOut = malloc( count+1 );
121
- if( zOut==0 ) return 0;
119
+ zOut = fossil_malloc( count+1 );
122120
while( n-->0 && (c = *zIn)!=0 ){
123121
if( IsSafeChar(c) ){
124122
zOut[i++] = c;
125123
}else if( c==' ' ){
126124
zOut[i++] = '+';
@@ -234,21 +232,21 @@
234232
c = zIn[i];
235233
if( c==0 || c==' ' || c=='\n' || c=='\t' || c=='\r' || c=='\f' || c=='\v'
236234
|| c=='\\' ) n++;
237235
}
238236
n += nIn;
239
- zOut = malloc( n+1 );
237
+ zOut = fossil_malloc( n+1 );
240238
if( zOut ){
241239
for(i=j=0; i<nIn; i++){
242240
int c = zIn[i];
243241
if( c==0 ){
244242
zOut[j++] = '\\';
245243
zOut[j++] = '0';
246244
}else if( c=='\\' ){
247245
zOut[j++] = '\\';
248246
zOut[j++] = '\\';
249
- }else if( isspace(c) ){
247
+ }else if( fossil_isspace(c) ){
250248
zOut[j++] = '\\';
251249
switch( c ){
252250
case '\n': c = 'n'; break;
253251
case ' ': c = 's'; break;
254252
case '\t': c = 't'; break;
@@ -269,12 +267,13 @@
269267
/*
270268
** Decode a fossilized string in-place.
271269
*/
272270
void defossilize(char *z){
273271
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++){
276275
if( c=='\\' && z[i+1] ){
277276
i++;
278277
switch( z[i] ){
279278
case 'n': c = '\n'; break;
280279
case 's': c = ' '; break;
@@ -310,11 +309,11 @@
310309
int i, n;
311310
312311
if( nData<=0 ){
313312
nData = strlen(zData);
314313
}
315
- z64 = malloc( (nData*4)/3 + 8 );
314
+ z64 = fossil_malloc( (nData*4)/3 + 8 );
316315
for(i=n=0; i+2<nData; i+=3){
317316
z64[n++] = zBase[ (zData[i]>>2) & 0x3f ];
318317
z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ];
319318
z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ];
320319
z64[n++] = zBase[ zData[i+2] & 0x3f ];
@@ -371,11 +370,11 @@
371370
for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; }
372371
isInit = 1;
373372
}
374373
n64 = strlen(z64);
375374
while( n64>0 && z64[n64-1]=='=' ) n64--;
376
- zData = malloc( (n64*3)/4 + 4 );
375
+ zData = fossil_malloc( (n64*3)/4 + 4 );
377376
for(i=j=0; i+3<n64; i+=4){
378377
a = trans[z64[i] & 0x7f];
379378
b = trans[z64[i+1] & 0x7f];
380379
c = trans[z64[i+2] & 0x7f];
381380
d = trans[z64[i+3] & 0x7f];
@@ -506,5 +505,87 @@
506505
while( *z && n-- ){
507506
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
508507
z++;
509508
}
510509
}
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
+}
511592
--- 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 @@
4646
free(zEventId);
4747
}
4848
4949
/*
5050
** 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.
5257
**
5358
** Display an existing event identified by EVENTID
5459
*/
5560
void event_page(void){
5661
int rid = 0; /* rid of the event artifact */
@@ -58,12 +63,11 @@
5863
const char *zEventId; /* Event identifier */
5964
char *zETime; /* Time of the event */
6065
char *zATime; /* Time the artifact was created */
6166
int specRid; /* rid specified by aid= parameter */
6267
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 */
6569
Blob fullbody; /* Complete content of the event body */
6670
Blob title; /* Title extracted from the event body */
6771
Blob tail; /* Event body that comes after the title */
6872
Stmt q1; /* Query to search for the event */
6973
int showDetail; /* True to show details */
@@ -82,11 +86,11 @@
8286
zUuid = (char*)P("aid");
8387
specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0;
8488
rid = nextRid = prevRid = 0;
8589
db_prepare(&q1,
8690
"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*')"
8892
" ORDER BY mtime DESC",
8993
zEventId
9094
);
9195
while( db_step(&q1)==SQLITE_ROW ){
9296
nextRid = rid;
@@ -108,18 +112,15 @@
108112
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
109113
showDetail = atoi(PD("detail","0"));
110114
111115
/* Extract the event content.
112116
*/
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 ){
118119
fossil_panic("Object #%d is not an event", rid);
119120
}
120
- blob_init(&fullbody, m.zWiki, -1);
121
+ blob_init(&fullbody, pEvent->zWiki, -1);
121122
if( wiki_find_title(&fullbody, &title, &tail) ){
122123
style_header(blob_str(&title));
123124
}else{
124125
style_header("Event %S", zEventId);
125126
tail = fullbody;
@@ -126,11 +127,11 @@
126127
}
127128
if( g.okWrWiki && g.okWrite && nextRid==0 ){
128129
style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
129130
g.zTop, zEventId);
130131
}
131
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
132
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
132133
style_submenu_element("Context", "Context", "%s/timeline?c=%T",
133134
g.zTop, zETime);
134135
if( g.okHistory ){
135136
if( showDetail ){
136137
style_submenu_element("Plain", "Plain", "%s/event?name=%s&amp;aid=%s",
@@ -161,37 +162,37 @@
161162
if( showDetail && g.okHistory ){
162163
int i;
163164
const char *zClr = 0;
164165
Blob comment;
165166
166
- zATime = db_text(0, "SELECT datetime(%.17g)", m.rDate);
167
+ zATime = db_text(0, "SELECT datetime(%.17g)", pEvent->rDate);
167168
@ <p>Event [<a href="%s(g.zTop)/artifact/%s(zUuid)">%S(zUuid)</a>] at
168169
@ [<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
170171
@ [<a href="%s(g.zTop)/timeline?c=%T(zATime)">%s(zATime)</a>]:</p>
171172
@ <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;
175176
}
176177
}
177178
if( zClr && zClr[0]==0 ) zClr = 0;
178179
if( zClr ){
179180
@ <div style="background-color: %h(zClr);">
180181
}else{
181182
@ <div>
182183
}
183
- blob_init(&comment, m.zComment, -1);
184
+ blob_init(&comment, pEvent->zComment, -1);
184185
wiki_convert(&comment, 0, WIKI_INLINE);
185186
blob_reset(&comment);
186187
@ </div>
187188
@ </blockquote><hr />
188189
}
189190
190191
wiki_convert(&tail, 0, 0);
191192
style_footer();
192
- manifest_clear(&m);
193
+ manifest_destroy(pEvent);
193194
}
194195
195196
/*
196197
** WEBPAGE: eventedit
197198
** URL: /eventedit?name=EVENTID
@@ -254,22 +255,18 @@
254255
255256
/* If editing an existing event, extract the key fields to use as
256257
** a starting point for the edit.
257258
*/
258259
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;
267264
if( zETime==0 ){
268
- zETime = db_text(0, "SELECT datetime(%.17g)", m.rEventDate);
265
+ zETime = db_text(0, "SELECT datetime(%.17g)", pEvent->rEventDate);
269266
}
270
- if( zComment==0 ) zComment = m.zComment;
267
+ if( zComment==0 ) zComment = pEvent->zComment;
271268
}
272269
if( zTags==0 ){
273270
zTags = db_text(0,
274271
"SELECT group_concat(substr(tagname,5),', ')"
275272
" FROM tagxref, tag"
@@ -316,12 +313,12 @@
316313
317314
/* Collapse all sequences of whitespace and "," characters into
318315
** a single space character */
319316
zBlob = blob_str(&tags);
320317
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++; }
323320
zBlob[j] = ' ';
324321
}else{
325322
zBlob[j] = zBlob[i];
326323
}
327324
}
328325
--- 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&amp;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&amp;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 @@
131131
TAG_BRANCH,
132132
zFilename
133133
);
134134
blob_zero(&title);
135135
blob_appendf(&title, "History of ");
136
- hyperlinked_path(zFilename, &title);
136
+ hyperlinked_path(zFilename, &title, 0);
137137
@ <h2>%b(&title)</h2>
138138
blob_reset(&title);
139139
pGraph = graph_init();
140140
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
141141
@ <table class="timelineTable">
142142
--- 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 @@
7272
/*
7373
** Malloc for zeroed space. Panic if unable to provide the
7474
** requested space.
7575
*/
7676
void *safeMalloc(int nByte){
77
- void *p = malloc(nByte);
78
- if( p==0 ) fossil_panic("out of memory");
77
+ void *p = fossil_malloc(nByte);
7978
memset(p, 0, nByte);
8079
return p;
8180
}
8281
8382
/*
@@ -148,12 +147,11 @@
148147
int i;
149148
for(i=0; i<p->nBranch; i++){
150149
if( strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
151150
}
152151
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);
155153
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
156154
return p->azBranch[p->nBranch-1];
157155
}
158156
159157
/*
160158
--- 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 @@
6060
zPw = 0;
6161
}else{
6262
/* Password failure while doing a sync from the command-line interface */
6363
url_prompt_for_password();
6464
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);
6666
}
6767
6868
/* The login card wants the SHA1 hash of the password, so convert the
6969
** password to its SHA1 hash it it isn't already a SHA1 hash.
7070
**
@@ -103,11 +103,11 @@
103103
if( i>0 && g.urlPath[i-1]=='/' ){
104104
zSep = "";
105105
}else{
106106
zSep = "/";
107107
}
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);
109109
if( g.urlProxyAuth ){
110110
blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth);
111111
}
112112
blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
113113
blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
@@ -210,15 +210,15 @@
210210
closeConnection = 1;
211211
}else{
212212
closeConnection = 0;
213213
}
214214
}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++){}
216216
iLength = atoi(&zLine[i]);
217217
}else if( strncasecmp(zLine, "connection:", 11)==0 ){
218218
char c;
219
- for(i=11; isspace(zLine[i]); i++){}
219
+ for(i=11; fossil_isspace(zLine[i]); i++){}
220220
c = zLine[i];
221221
if( c=='c' || c=='C' ){
222222
closeConnection = 1;
223223
}else if( c=='k' || c=='K' ){
224224
closeConnection = 0;
@@ -226,11 +226,14 @@
226226
}else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){
227227
int i, j;
228228
for(i=9; zLine[i] && zLine[i]==' '; i++){}
229229
if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine);
230230
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
+ }
232235
fossil_print("redirect to %s\n", &zLine[i]);
233236
url_parse(&zLine[i]);
234237
transport_close();
235238
http_exchange(pSend, pReply, useLogin);
236239
return;
@@ -267,11 +270,11 @@
267270
}
268271
z[j] = 0;
269272
fossil_fatal("server sends error: %s", z);
270273
}
271274
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));*/
273276
}else{
274277
blob_uncompress(pReply, pReply);
275278
}
276279
277280
/*
278281
--- 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
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -90,10 +90,11 @@
9090
SSL_library_init();
9191
SSL_load_error_strings();
9292
ERR_load_BIO_strings();
9393
OpenSSL_add_all_algorithms();
9494
sslCtx = SSL_CTX_new(SSLv23_client_method());
95
+ X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx));
9596
sslIsInit = 1;
9697
}
9798
}
9899
99100
/*
100101
--- 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
--- src/http_transport.c
+++ src/http_transport.c
@@ -83,11 +83,11 @@
8383
int got;
8484
zBuf[0] = 0;
8585
got = read(sshIn, zBuf, szBuf-1);
8686
while( got>=0 ){
8787
zBuf[got] = 0;
88
- if( got==0 || !isspace(zBuf[got-1]) ) break;
88
+ if( got==0 || !fossil_isspace(zBuf[got-1]) ) break;
8989
got--;
9090
}
9191
}
9292
9393
/*
@@ -296,11 +296,11 @@
296296
char *zCmd;
297297
fclose(transport.pFile);
298298
zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
299299
g.argv[0], g.urlName, transport.zOutFile, transport.zInFile
300300
);
301
- portable_system(zCmd);
301
+ fossil_system(zCmd);
302302
free(zCmd);
303303
transport.pFile = fopen(transport.zInFile, "rb");
304304
}
305305
}
306306
@@ -386,12 +386,11 @@
386386
*/
387387
static void transport_load_buffer(int N){
388388
int i, j;
389389
if( transport.nAlloc==0 ){
390390
transport.nAlloc = N;
391
- transport.pBuf = malloc( N );
392
- if( transport.pBuf==0 ) fossil_panic("out of memory");
391
+ transport.pBuf = fossil_malloc( N );
393392
transport.iCursor = 0;
394393
transport.nUsed = 0;
395394
}
396395
if( transport.iCursor>0 ){
397396
for(i=0, j=transport.iCursor; j<transport.nUsed; i++, j++){
@@ -401,12 +400,11 @@
401400
transport.iCursor = 0;
402401
}
403402
if( transport.nUsed + N > transport.nAlloc ){
404403
char *pNew;
405404
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);
408406
transport.pBuf = pNew;
409407
}
410408
if( N>0 ){
411409
i = transport_fetch(&transport.pBuf[transport.nUsed], N);
412410
if( i>0 ){
@@ -439,11 +437,11 @@
439437
break;
440438
}
441439
}
442440
if( transport.pBuf[i]=='\n' ){
443441
transport.iCursor = i+1;
444
- while( i>=iStart && isspace(transport.pBuf[i]) ){
442
+ while( i>=iStart && fossil_isspace(transport.pBuf[i]) ){
445443
transport.pBuf[i] = 0;
446444
i--;
447445
}
448446
break;
449447
}
450448
--- 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 @@
234234
235235
236236
/*
237237
** Append the difference between two RIDs to the output
238238
*/
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;
240242
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
+ }
243255
blob_zero(&out);
244256
text_diff(&from, &to, &out, 5, 1);
245257
@ %h(blob_str(&out))
246258
blob_reset(&from);
247259
blob_reset(&to);
@@ -263,39 +275,37 @@
263275
@ <p>Deleted %h(zName)</p>
264276
}else if( zOld==0 ){
265277
@ <p>Added %h(zName)</p>
266278
}else{
267279
@ <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 ){
281303
@ &nbsp;&nbsp;
282304
@ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;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>
289305
}
290306
@ </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>
297307
}
298308
}
299309
300310
301311
/*
@@ -533,24 +543,20 @@
533543
rid = 0;
534544
}
535545
db_finalize(&q);
536546
showTags(rid, "wiki-*");
537547
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 ){
545551
Blob wiki;
546
- blob_init(&wiki, m.zWiki, -1);
552
+ blob_init(&wiki, pWiki->zWiki, -1);
547553
@ <div class="section">Content</div>
548554
wiki_convert(&wiki, 0, 0);
549555
blob_reset(&wiki);
550556
}
551
- manifest_clear(&m);
557
+ manifest_destroy(pWiki);
552558
}
553559
style_footer();
554560
}
555561
556562
/*
@@ -570,26 +576,23 @@
570576
571577
/*
572578
** Find an checkin based on query parameter zParam and parse its
573579
** manifest. Return the number of errors.
574580
*/
575
-static int vdiff_parse_manifest(const char *zParam, int *pRid, Manifest *pM){
581
+static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
576582
int rid;
577
- Blob content;
578583
579584
*pRid = rid = name_to_rid_www(zParam);
580585
if( rid==0 ){
581586
webpage_error("Missing \"%s\" query parameter.", zParam);
582
- return 1;
587
+ return 0;
583588
}
584589
if( !is_a_version(rid) ){
585590
webpage_error("Artifact %s is not a checkin.", P(zParam));
586
- return 1;
591
+ return 0;
587592
}
588
- content_get(rid, &content);
589
- manifest_parse(pM, &content);
590
- return 0;
593
+ return manifest_get(rid, CFTYPE_MANIFEST);
591594
}
592595
593596
/*
594597
** Output a description of a check-in
595598
*/
@@ -625,59 +628,64 @@
625628
** Show all differences between two checkins.
626629
*/
627630
void vdiff_page(void){
628631
int ridFrom, ridTo;
629632
int showDetail = 0;
630
- int iFrom, iTo;
631
- Manifest mFrom, mTo;
633
+ Manifest *pFrom, *pTo;
634
+ ManifestFile *pFileFrom, *pFileTo;
632635
633636
login_check_credentials();
634637
if( !g.okRead ){ login_needed(); return; }
635638
login_anonymous_available();
636639
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;
639644
showDetail = atoi(PD("detail","0"));
640645
style_header("Check-in Differences");
641646
@ <h2>Difference From:</h2><blockquote>
642647
checkin_description(ridFrom);
643648
@ </blockquote><h2>To:</h2><blockquote>
644649
checkin_description(ridTo);
645650
@ </blockquote><hr /><p>
646651
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 ){
649657
int cmp;
650
- if( iFrom>=mFrom.nFile ){
658
+ if( pFileFrom==0 ){
651659
cmp = +1;
652
- }else if( iTo>=mTo.nFile ){
660
+ }else if( pFileTo==0 ){
653661
cmp = -1;
654662
}else{
655
- cmp = strcmp(mFrom.aFile[iFrom].zName, mTo.aFile[iTo].zName);
663
+ cmp = strcmp(pFileFrom->zName, pFileTo->zName);
656664
}
657665
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);
661669
}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 ){
666674
/* No changes */
667
- iFrom++;
668
- iTo++;
675
+ pFileFrom = manifest_file_next(pFrom, 0);
676
+ pFileTo = manifest_file_next(pTo, 0);
669677
}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);
675683
}
676684
}
677
- manifest_clear(&mFrom);
678
- manifest_clear(&mTo);
685
+ manifest_destroy(pFrom);
686
+ manifest_destroy(pTo);
679687
680688
style_footer();
681689
}
682690
683691
/*
@@ -1033,25 +1041,26 @@
10331041
*/
10341042
int artifact_from_ci_and_filename(void){
10351043
const char *zFilename;
10361044
const char *zCI;
10371045
int cirid;
1038
- Blob content;
1039
- Manifest m;
1040
- int i;
1046
+ Manifest *pManifest;
1047
+ ManifestFile *pFile;
10411048
10421049
zCI = P("ci");
10431050
if( zCI==0 ) return 0;
10441051
zFilename = P("filename");
10451052
if( zFilename==0 ) return 0;
10461053
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;
10531062
}
10541063
}
10551064
return 0;
10561065
}
10571066
@@ -1158,15 +1167,14 @@
11581167
**
11591168
** Show the details of a ticket change control artifact.
11601169
*/
11611170
void tinfo_page(void){
11621171
int rid;
1163
- Blob content;
11641172
char *zDate;
11651173
const char *zUuid;
11661174
char zTktName[20];
1167
- Manifest m;
1175
+ Manifest *pTktChng;
11681176
11691177
login_check_credentials();
11701178
if( !g.okRdTkt ){ login_needed(); return; }
11711179
rid = name_to_rid_www("name");
11721180
if( rid==0 ){ fossil_redirect_home(); }
@@ -1178,39 +1186,37 @@
11781186
}else{
11791187
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
11801188
g.zTop, zUuid);
11811189
}
11821190
}
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 ){
11881193
fossil_redirect_home();
11891194
}
11901195
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);
11931198
zTktName[10] = 0;
11941199
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>
11961202
@
1197
- @ <p>By %h(m.zUser) on %s(zDate). See also:
1203
+ @ <p>By %h(pTktChng->zUser) on %s(zDate). See also:
11981204
@ <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>
12011207
}else{
12021208
@ <h2>Changes to ticket %s(zTktName)</h2>
12031209
@
1204
- @ <p>By %h(m.zUser) on %s(zDate).
1210
+ @ <p>By %h(pTktChng->zUser) on %s(zDate).
12051211
@ </p>
12061212
}
12071213
@
12081214
@ <ol>
12091215
free(zDate);
1210
- ticket_output_change_artifact(&m);
1211
- manifest_clear(&m);
1216
+ ticket_output_change_artifact(pTktChng);
1217
+ manifest_destroy(pTktChng);
12121218
style_footer();
12131219
}
12141220
12151221
12161222
/*
12171223
--- 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 @ &nbsp;&nbsp;
282 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;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 @ &nbsp;&nbsp;
304 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;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 @@
5151
** Return the name of the login cookie
5252
*/
5353
static char *login_cookie_name(void){
5454
static char *zCookieName = 0;
5555
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);
6160
}
6261
return zCookieName;
6362
}
6463
6564
/*
@@ -353,11 +352,11 @@
353352
}
354353
355354
/* Check the login cookie to see if it matches a known valid user.
356355
*/
357356
if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
358
- if( isdigit(zCookie[0]) ){
357
+ if( fossil_isdigit(zCookie[0]) ){
359358
/* Cookies of the form "uid/randomness". There must be a
360359
** corresponding entry in the user table. */
361360
uid = db_int(0,
362361
"SELECT uid FROM user"
363362
" WHERE uid=%d"
364363
--- 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 @@
223223
*/
224224
int main(int argc, char **argv){
225225
const char *zCmdName = "unknown";
226226
int idx;
227227
int rc;
228
+ int mightBeCgi;
228229
229230
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
230231
g.now = time(0);
231232
g.argc = argc;
232233
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
+ }
241245
}else{
242246
g.fQuiet = find_option("quiet", 0, 0)!=0;
243247
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
244248
g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
245249
g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
246250
g.zLogin = find_option("user", "U", 1);
247251
zCmdName = argv[1];
248252
}
249253
rc = name_search(zCmdName, aCommand, count(aCommand), &idx);
254
+ if( rc==1 && mightBeCgi ){
255
+ rc = name_search("cgi", aCommand, count(aCommand), &idx);
256
+ }
250257
if( rc==1 ){
251258
fprintf(stderr,"%s: unknown command: %s\n"
252259
"%s: use \"help\" for more information\n",
253260
argv[0], zCmdName, argv[0]);
254261
fossil_exit(1);
@@ -359,10 +366,49 @@
359366
cgi_printf("<p class=\"generalError\">%h</p>", z);
360367
}else{
361368
fprintf(stderr, "%s: %s\n", g.argv[0], z);
362369
}
363370
}
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
+
364410
365411
/*
366412
** Return a name for an SQLite error code
367413
*/
368414
static const char *sqlite_error_code_name(int iCode){
@@ -591,10 +637,62 @@
591637
z++;
592638
}
593639
}
594640
putchar('\n');
595641
}
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
+}
596694
597695
/*
598696
** Set the g.zBaseURL value to the full URL for the toplevel of
599697
** the fossil tree. Set g.zTop to g.zBaseURL without the
600698
** leading "http://" and the host and port.
@@ -705,11 +803,11 @@
705803
706804
/* To avoid mischief, make sure the repository basename contains no
707805
** characters other than alphanumerics, "-", and "_".
708806
*/
709807
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] = '_';
711809
}
712810
if( zRepo[0]=='/' && zRepo[1]=='/' ) zRepo++;
713811
714812
if( file_size(zRepo)<1024 ){
715813
if( zNotFound ){
@@ -1076,5 +1174,33 @@
10761174
}
10771175
db_close();
10781176
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags);
10791177
#endif
10801178
}
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
+}
10811207
--- 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 @@
264264
VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest
265265
awk '{ printf "#define MANIFEST_UUID \"%s\"\n", $$1}' $(SRCDIR)/../manifest.uuid >VERSION.h
266266
awk '{ printf "#define MANIFEST_VERSION \"[%.10s]\"\n", $$1}' $(SRCDIR)/../manifest.uuid >>VERSION.h
267267
awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n", substr($$2,1,10),substr($$2,12)}' $(SRCDIR)/../manifest >>VERSION.h
268268
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)
271273
272274
# This rule prevents make from using its default rules to try build
273275
# an executable named "manifest" out of the file named "manifest.c"
274276
#
275277
$(SRCDIR)/../manifest:
@@ -792,11 +794,14 @@
792794
793795
zip.h: headers
794796
$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
795797
$(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
796798
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
+
797802
$(OBJDIR)/th.o: $(SRCDIR)/th.c
798803
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
799804
800805
$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
801806
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
802807
803808
--- 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
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -13,11 +13,11 @@
1313
#include <stdlib.h>
1414
#include <ctype.h>
1515
#include <memory.h>
1616
#include <sys/stat.h>
1717
#include <assert.h>
18
-#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER)
18
+#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
1919
# ifndef WIN32
2020
# define WIN32
2121
# endif
2222
# include <string.h>
2323
#else
2424
--- 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 @@
148148
$(SRCDIR)/../manifest.uuid >>VERSION.h
149149
awk '$$1=="D"{printf "#define MANIFEST_DATE \"%s %s\"\n",\
150150
substr($$2,1,10),substr($$2,12)}' \
151151
$(SRCDIR)/../manifest >>VERSION.h
152152
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)
155161
156162
# This rule prevents make from using its default rules to try build
157163
# an executable named "manifest" out of the file named "manifest.c"
158164
#
159165
$(SRCDIR)/../manifest:
@@ -201,10 +207,15 @@
201207
#append opt " -DSQLITE_ENABLE_FTS3=1"
202208
append opt " -Dlocaltime=fossil_localtime"
203209
append opt " -DSQLITE_ENABLE_LOCKING_STYLE=0"
204210
puts "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
205211
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
+
206217
puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
207218
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
208219
209220
puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
210221
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
211222
--- 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 @@
2626
2727
#if INTERFACE
2828
/*
2929
** Types of control files
3030
*/
31
+#define CFTYPE_ANY 0
3132
#define CFTYPE_MANIFEST 1
3233
#define CFTYPE_CLUSTER 2
3334
#define CFTYPE_CONTROL 3
3435
#define CFTYPE_WIKI 4
3536
#define CFTYPE_TICKET 5
3637
#define CFTYPE_ATTACHMENT 6
3738
#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
+
3850
3951
/*
4052
** A parsed manifest or cluster.
4153
*/
4254
struct Manifest {
4355
Blob content; /* The original content blob */
4456
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 */
4560
char *zComment; /* Decoded comment. The C card. */
4661
double rDate; /* Date and time from D card. 0.0 if no D card. */
4762
char *zUser; /* Name of the user from the U card. */
4863
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
4964
char *zWiki; /* Text of the wiki page. W card. */
@@ -54,17 +69,12 @@
5469
char *zAttachName; /* Filename of an attachment. A card. */
5570
char *zAttachSrc; /* UUID of document being attached. A card. */
5671
char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */
5772
int nFile; /* Number of F cards */
5873
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 */
6676
int nParent; /* Number of parents. */
6777
int nParentAlloc; /* Slots allocated in azParent[] */
6878
char **azParent; /* UUIDs of parents. One for each P card argument */
6979
int nCChild; /* Number of cluster children */
7080
int nCChildAlloc; /* Number of closts allocated in azCChild[] */
@@ -83,22 +93,192 @@
8393
char *zValue; /* Value of the field */
8494
} *aField; /* One for each J card */
8595
};
8696
#endif
8797
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
+
88109
89110
/*
90111
** Clear the memory allocated in a manifest object
91112
*/
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;
100280
}
101281
102282
/*
103283
** Parse a blob into a Manifest object. The Manifest object
104284
** takes over the input blob and will free it when the
@@ -124,48 +304,66 @@
124304
** Each card is divided into tokens by a single space character.
125305
** The first token is a single upper-case letter which is the card type.
126306
** The card type determines the other parameters to the card.
127307
** Cards must occur in lexicographical order.
128308
*/
129
-int manifest_parse(Manifest *p, Blob *pContent){
130
- int seenHeader = 0;
309
+static Manifest *manifest_parse(Blob *pContent, int rid){
310
+ Manifest *p;
131311
int seenZ = 0;
132312
int i, lineNo=0;
133
- Blob line, token, a1, a2, a3, a4;
313
+ ManifestText x;
134314
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
+ }
135347
348
+ /* Allocate a Manifest object to hold the parsed control artifact.
349
+ */
350
+ p = fossil_malloc( sizeof(*p) );
136351
memset(p, 0, sizeof(*p));
137352
memcpy(&p->content, pContent, sizeof(p->content));
353
+ p->rid = rid;
138354
blob_zero(pContent);
139355
pContent = &p->content;
140356
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 ){
147363
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 ){
167365
/*
168366
** A <filename> <target> ?<source>?
169367
**
170368
** Identifies an attachment to either a wiki page or a ticket.
171369
** <source> is the artifact that is the attachment. <source>
@@ -172,50 +370,60 @@
172370
** is omitted to delete an attachment. <target> is the name of
173371
** a wiki page or ticket to which that attachment is connected.
174372
*/
175373
case 'A': {
176374
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;
180380
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);
185381
defossilize(zName);
186382
if( !file_is_simple_pathname(zName) ){
187383
goto manifest_syntax_error;
188384
}
189385
defossilize(zTarget);
190
- if( (blob_size(&a2)!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
386
+ if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
191387
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
192388
goto manifest_syntax_error;
193389
}
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)) ){
196391
goto manifest_syntax_error;
197392
}
198393
p->zAttachName = (char*)file_tail(zName);
199394
p->zAttachSrc = zSrc;
200395
p->zAttachTarget = zTarget;
201396
break;
202397
}
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
+
203413
204414
/*
205415
** C <comment>
206416
**
207417
** Comment text is fossil-encoded. There may be no more than
208418
** one C line. C lines are required for manifests and are
209419
** disallowed on all other control files.
210420
*/
211421
case 'C': {
212
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
213422
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;
217425
defossilize(p->zComment);
218426
break;
219427
}
220428
221429
/*
@@ -224,17 +432,13 @@
224432
** The timestamp should be ISO 8601. YYYY-MM-DDtHH:MM:SS
225433
** There can be no more than 1 D line. D lines are required
226434
** for all control files except for clusters.
227435
*/
228436
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;
236440
break;
237441
}
238442
239443
/*
240444
** E <timestamp> <uuid>
@@ -244,68 +448,57 @@
244448
** The event timestamp is distinct from the D timestamp. The D
245449
** timestamp is when the artifact was created whereas the E timestamp
246450
** is when the specific event is said to occur.
247451
*/
248452
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));
257455
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;
260458
if( !validate16(p->zEventId, UUID_SIZE) ) goto manifest_syntax_error;
261459
break;
262460
}
263461
264462
/*
265
- ** F <filename> <uuid> ?<permissions>? ?<old-name>?
463
+ ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>?
266464
**
267465
** Identifies a file in a manifest. Multiple F lines are
268466
** allowed in a manifest. F lines are not allowed in any
269467
** other control file. The filename and old-name are fossil-encoded.
270468
*/
271469
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;
282473
defossilize(zName);
283474
if( !file_is_simple_pathname(zName) ){
284475
goto manifest_syntax_error;
285476
}
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 ){
289485
defossilize(zPriorName);
290486
if( !file_is_simple_pathname(zPriorName) ){
291487
goto manifest_syntax_error;
292488
}
293
- }else{
294
- zPriorName = 0;
295489
}
296490
if( p->nFile>=p->nFileAlloc ){
297491
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]) );
300494
}
301495
i = p->nFile++;
302496
p->aFile[i].zName = zName;
303497
p->aFile[i].zUuid = zUuid;
304498
p->aFile[i].zPerm = zPerm;
305499
p->aFile[i].zPrior = zPriorName;
306
- p->aFile[i].iRename = -1;
307500
if( i>0 && strcmp(p->aFile[i-1].zName, zName)>=0 ){
308501
goto manifest_syntax_error;
309502
}
310503
break;
311504
}
@@ -318,22 +511,19 @@
318511
** value. If <value> is omitted then it is understood to be an
319512
** empty string.
320513
*/
321514
case 'J': {
322515
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 = "";
329520
defossilize(zValue);
330521
if( p->nField>=p->nFieldAlloc ){
331522
p->nFieldAlloc = p->nFieldAlloc*2 + 10;
332
- p->aField = realloc(p->aField,
523
+ p->aField = fossil_realloc(p->aField,
333524
p->nFieldAlloc*sizeof(p->aField[0]) );
334
- if( p->aField==0 ) fossil_panic("out of memory");
335525
}
336526
i = p->nField++;
337527
p->aField[i].zName = zName;
338528
p->aField[i].zValue = zValue;
339529
if( i>0 && strcmp(p->aField[i-1].zName, zName)>=0 ){
@@ -348,18 +538,14 @@
348538
**
349539
** A K-line gives the UUID for the ticket which this control file
350540
** is amending.
351541
*/
352542
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;
359543
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;
361547
break;
362548
}
363549
364550
/*
365551
** L <wikititle>
@@ -366,15 +552,13 @@
366552
**
367553
** The wiki page title is fossil-encoded. There may be no more than
368554
** one L line.
369555
*/
370556
case 'L': {
371
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
372557
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;
376560
defossilize(p->zWikiTitle);
377561
if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){
378562
goto manifest_syntax_error;
379563
}
380564
break;
@@ -385,21 +569,18 @@
385569
**
386570
** An M-line identifies another artifact by its UUID. M-lines
387571
** occur in clusters only.
388572
*/
389573
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;
395577
if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
396578
if( p->nCChild>=p->nCChildAlloc ){
397579
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]) );
401582
}
402583
i = p->nCChild++;
403584
p->azCChild[i] = zUuid;
404585
if( i>0 && strcmp(p->azCChild[i-1], zUuid)>=0 ){
405586
goto manifest_syntax_error;
@@ -413,20 +594,17 @@
413594
** Specify one or more other artifacts where are the parents of
414595
** this artifact. The first parent is the primary parent. All
415596
** others are parents by merge.
416597
*/
417598
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;
423601
if( !validate16(zUuid, UUID_SIZE) ) goto manifest_syntax_error;
424602
if( p->nParent>=p->nParentAlloc ){
425603
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*));
428606
}
429607
i = p->nParent++;
430608
p->azParent[i] = zUuid;
431609
}
432610
break;
@@ -437,16 +615,13 @@
437615
**
438616
** Specify the MD5 checksum over the name and content of all files
439617
** in the manifest.
440618
*/
441619
case 'R': {
442
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
443620
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;
448623
if( !validate16(p->zRepoCksum, 32) ) goto manifest_syntax_error;
449624
break;
450625
}
451626
452627
/*
@@ -463,29 +638,20 @@
463638
** the tag is really a property with the given value.
464639
**
465640
** Tags are not allowed in clusters. Multiple T lines are allowed.
466641
*/
467642
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) ){
485651
/* A valid uuid */
486
- }else if( blob_size(&a2)==1 && zUuid[0]=='*' ){
652
+ }else if( sz==1 && zUuid[0]=='*' ){
487653
zUuid = 0;
488654
}else{
489655
goto manifest_syntax_error;
490656
}
491657
defossilize(zName);
@@ -496,12 +662,11 @@
496662
/* Do not allow tags whose names look like UUIDs */
497663
goto manifest_syntax_error;
498664
}
499665
if( p->nTag>=p->nTagAlloc ){
500666
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]) );
503668
}
504669
i = p->nTag++;
505670
p->aTag[i].zName = zName;
506671
p->aTag[i].zUuid = zUuid;
507672
p->aTag[i].zValue = zValue;
@@ -517,19 +682,17 @@
517682
** Identify the user who created this control file by their
518683
** login. Only one U line is allowed. Prohibited in clusters.
519684
** If the user name is omitted, take that to be "anonymous".
520685
*/
521686
case 'U': {
522
- md5sum_step_text(blob_buffer(&line), blob_size(&line));
523687
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 ){
525690
p->zUser = "anonymous";
526691
}else{
527
- p->zUser = blob_terminate(&a1);
528692
defossilize(p->zUser);
529693
}
530
- if( blob_token(&line, &a2)!=0 ) goto manifest_syntax_error;
531694
break;
532695
}
533696
534697
/*
535698
** W <size>
@@ -537,26 +700,28 @@
537700
** The next <size> bytes of the file contain the text of the wiki
538701
** page. There is always an extra \n before the start of the next
539702
** record.
540703
*/
541704
case 'W': {
542
- int size;
705
+ char *zSize;
706
+ int size, c;
543707
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
+ }
548714
if( size<0 ) goto manifest_syntax_error;
549715
if( p->zWiki!=0 ) goto manifest_syntax_error;
550716
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++;
558723
break;
559724
}
560725
561726
562727
/*
@@ -569,31 +734,24 @@
569734
** This card is required for all control file types except for
570735
** Manifest. It is not required for manifest only for historical
571736
** compatibility reasons.
572737
*/
573738
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;
584742
seenZ = 1;
585743
break;
586744
}
587745
default: {
588746
goto manifest_syntax_error;
589747
}
590748
}
591749
}
592
- if( !seenHeader ) goto manifest_syntax_error;
750
+ if( x.z<x.zEnd ) goto manifest_syntax_error;
593751
594
- if( p->nFile>0 || p->zRepoCksum!=0 ){
752
+ if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
595753
if( p->nCChild>0 ) goto manifest_syntax_error;
596754
if( p->rDate<=0.0 ) goto manifest_syntax_error;
597755
if( p->nField>0 ) goto manifest_syntax_error;
598756
if( p->zTicketUuid ) goto manifest_syntax_error;
599757
if( p->zWiki ) goto manifest_syntax_error;
@@ -674,36 +832,194 @@
674832
if( p->zWikiTitle ) goto manifest_syntax_error;
675833
if( p->zTicketUuid ) goto manifest_syntax_error;
676834
p->type = CFTYPE_MANIFEST;
677835
}
678836
md5sum_init();
679
- return 1;
837
+ return p;
680838
681839
manifest_syntax_error:
682840
/*fprintf(stderr, "Manifest error on line %i\n", lineNo);fflush(stderr);*/
683841
md5sum_init();
684
- manifest_clear(p);
842
+ manifest_destroy(p);
685843
return 0;
686844
}
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
+}
687889
688890
/*
689891
** COMMAND: test-parse-manifest
690892
**
691
-** Usage: %fossil test-parse-manifest FILENAME
893
+** Usage: %fossil test-parse-manifest FILENAME ?N?
692894
**
693895
** Parse the manifest and discarded. Use for testing only.
694896
*/
695897
void manifest_test_parse_cmd(void){
696
- Manifest m;
898
+ Manifest *p;
697899
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 ){
699904
usage("FILENAME");
700905
}
701
- db_must_be_within_tree();
702906
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;
7051021
}
7061022
7071023
/*
7081024
** Translate a filename into a filename-id (fnid). Create a new fnid
7091025
** if no previously exists.
@@ -731,14 +1047,14 @@
7311047
** Add a single entry to the mlink table. Also add the filename to
7321048
** the filename table if it is not there already.
7331049
*/
7341050
static void add_one_mlink(
7351051
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 */
7381054
const char *zFilename, /* Filename */
739
- const char *zPrior /* Previous filename. NULL if unchanged */
1055
+ const char *zPrior /* Previous filename. NULL if unchanged */
7401056
){
7411057
int fnid, pfnid, pid, fid;
7421058
static Stmt s1;
7431059
7441060
fnid = filename_to_fnid(zFilename);
@@ -745,16 +1061,16 @@
7451061
if( zPrior==0 ){
7461062
pfnid = 0;
7471063
}else{
7481064
pfnid = filename_to_fnid(zPrior);
7491065
}
750
- if( zFromUuid==0 ){
1066
+ if( zFromUuid==0 || zFromUuid[0]==0 ){
7511067
pid = 0;
7521068
}else{
7531069
pid = uuid_to_rid(zFromUuid, 1);
7541070
}
755
- if( zToUuid==0 ){
1071
+ if( zToUuid==0 || zToUuid[0]==0 ){
7561072
fid = 0;
7571073
}else{
7581074
fid = uuid_to_rid(zToUuid, 1);
7591075
}
7601076
db_static_prepare(&s1,
@@ -771,33 +1087,83 @@
7711087
content_deltify(pid, fid, 0);
7721088
}
7731089
}
7741090
7751091
/*
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.
7801099
*/
781
-static int find_file_in_manifest(Manifest *p, const char *zName){
1100
+static ManifestFile *manifest_file_seek_base(Manifest *p, const char *zName){
7821101
int lwr, upr;
7831102
int c;
7841103
int i;
7851104
lwr = 0;
7861105
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
+ }
7871116
while( lwr<=upr ){
7881117
i = (lwr+upr)/2;
7891118
c = strcmp(p->aFile[i].zName, zName);
7901119
if( c<0 ){
7911120
lwr = i+1;
7921121
}else if( c>0 ){
7931122
upr = i-1;
7941123
}else{
795
- return i;
1124
+ p->iFile = i;
1125
+ return &p->aFile[i];
7961126
}
7971127
}
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
+ }
7991165
}
8001166
8011167
/*
8021168
** Add mlink table entries associated with manifest cid. The
8031169
** parent manifest is pid.
@@ -808,85 +1174,76 @@
8081174
** Deleted files have mlink.fid=0.
8091175
** Added files have mlink.pid=0.
8101176
** Edited files have both mlink.pid!=0 and mlink.fid!=0
8111177
*/
8121178
static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
813
- Manifest other;
8141179
Blob otherContent;
815
- int i, j;
1180
+ int otherRid;
1181
+ int i, rc;
1182
+ ManifestFile *pChildFile, *pParentFile;
1183
+ Manifest **ppOther;
1184
+ static Stmt eq;
8161185
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
+
8201192
assert( pParent==0 || pChild==0 );
8211193
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);
8881245
}
8891246
8901247
/*
8911248
** True if manifest_crosslink_begin() has been called but
8921249
** manifest_crosslink_end() is still pending.
@@ -1023,38 +1380,44 @@
10231380
** of the routine, "manifest_crosslink", and the name of this source
10241381
** file, is a legacy of its original use.
10251382
*/
10261383
int manifest_crosslink(int rid, Blob *pContent){
10271384
int i;
1028
- Manifest m;
1385
+ Manifest *p;
10291386
Stmt q;
10301387
int parentid = 0;
10311388
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);
10331396
return 0;
10341397
}
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);
10371400
return 0;
10381401
}
10391402
db_begin_transaction();
1040
- if( m.type==CFTYPE_MANIFEST ){
1403
+ if( p->type==CFTYPE_MANIFEST ){
10411404
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
10421405
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);
10451408
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);
10471410
if( i==0 ){
1048
- add_mlink(pid, 0, rid, &m);
1411
+ add_mlink(pid, 0, rid, p);
10491412
parentid = pid;
10501413
}
10511414
}
10521415
db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
10531416
while( db_step(&q)==SQLITE_ROW ){
10541417
int cid = db_column_int(&q, 0);
1055
- add_mlink(rid, &m, cid, 0);
1418
+ add_mlink(rid, p, cid, 0);
10561419
}
10571420
db_finalize(&q);
10581421
db_multi_exec(
10591422
"REPLACE INTO event(type,mtime,objid,user,comment,"
10601423
"bgcolor,euser,ecomment)"
@@ -1065,118 +1428,134 @@
10651428
" ),"
10661429
" %d,%Q,%Q,"
10671430
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0),"
10681431
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
10691432
" (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,
10721435
TAG_BGCOLOR, rid,
10731436
TAG_USER, rid,
10741437
TAG_COMMENT, rid
10751438
);
10761439
zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
10771440
" 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);
11011480
}else{
11021481
tid = rid;
11031482
}
11041483
if( tid ){
1105
- switch( m.aTag[i].zName[0] ){
1484
+ switch( p->aTag[i].zName[0] ){
11061485
case '-': type = 0; break; /* Cancel prior occurances */
11071486
case '+': type = 1; break; /* Apply to target only */
11081487
case '*': type = 2; break; /* Propagate to descendants */
11091488
default:
1110
- fossil_fatal("unknown tag type in manifest: %s", m.aTag);
1489
+ fossil_fatal("unknown tag type in manifest: %s", p->aTag);
11111490
return 0;
11121491
}
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);
11151494
}
11161495
}
11171496
if( parentid ){
11181497
tag_propagate_all(parentid);
11191498
}
11201499
}
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);
11231502
int tagid = tag_findid(zTag, 1);
11241503
int prior;
11251504
char *zComment;
11261505
int nWiki;
11271506
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);
11301509
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);
11321511
free(zTag);
11331512
prior = db_int(0,
11341513
"SELECT rid FROM tagxref"
11351514
" WHERE tagid=%d AND mtime<%.17g"
11361515
" ORDER BY mtime DESC",
1137
- tagid, m.rDate
1516
+ tagid, p->rDate
11381517
);
11391518
if( prior ){
11401519
content_deltify(prior, rid, 0);
11411520
}
11421521
if( nWiki>0 ){
1143
- zComment = mprintf("Changes to wiki page [%h]", m.zWikiTitle);
1522
+ zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
11441523
}else{
1145
- zComment = mprintf("Deleted wiki page [%h]", m.zWikiTitle);
1524
+ zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
11461525
}
11471526
db_multi_exec(
11481527
"REPLACE INTO event(type,mtime,objid,user,comment,"
11491528
" bgcolor,euser,ecomment)"
11501529
"VALUES('w',%.17g,%d,%Q,%Q,"
11511530
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
11521531
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
11531532
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1154
- m.rDate, rid, m.zUser, zComment,
1533
+ p->rDate, rid, p->zUser, zComment,
11551534
TAG_BGCOLOR, rid,
11561535
TAG_BGCOLOR, rid,
11571536
TAG_USER, rid,
11581537
TAG_COMMENT, rid
11591538
);
11601539
free(zComment);
11611540
}
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);
11641543
int tagid = tag_findid(zTag, 1);
11651544
int prior, subsequent;
11661545
int nWiki;
11671546
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);
11701549
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);
11721551
free(zTag);
11731552
prior = db_int(0,
11741553
"SELECT rid FROM tagxref"
11751554
" WHERE tagid=%d AND mtime<%.17g"
11761555
" ORDER BY mtime DESC",
1177
- tagid, m.rDate
1556
+ tagid, p->rDate
11781557
);
11791558
if( prior ){
11801559
content_deltify(prior, rid, 0);
11811560
db_multi_exec(
11821561
"DELETE FROM event"
@@ -1188,104 +1567,87 @@
11881567
}
11891568
subsequent = db_int(0,
11901569
"SELECT rid FROM tagxref"
11911570
" WHERE tagid=%d AND mtime>%.17g"
11921571
" ORDER BY mtime",
1193
- tagid, m.rDate
1572
+ tagid, p->rDate
11941573
);
11951574
if( subsequent ){
11961575
content_deltify(rid, subsequent, 0);
11971576
}else{
11981577
db_multi_exec(
11991578
"REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
12001579
"VALUES('e',%.17g,%d,%d,%Q,%Q,"
12011580
" (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,
12031582
TAG_BGCOLOR, rid
12041583
);
12051584
}
12061585
}
1207
- if( m.type==CFTYPE_TICKET ){
1586
+ if( p->type==CFTYPE_TICKET ){
12081587
char *zTag;
12091588
12101589
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);
12131592
free(zTag);
12141593
db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
1215
- m.zTicketUuid);
1594
+ p->zTicketUuid);
12161595
}
1217
- if( m.type==CFTYPE_ATTACHMENT ){
1596
+ if( p->type==CFTYPE_ATTACHMENT ){
12181597
db_multi_exec(
12191598
"INSERT INTO attachment(attachid, mtime, src, target,"
12201599
"filename, comment, user)"
12211600
"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
12241603
);
12251604
db_multi_exec(
12261605
"UPDATE attachment SET isLatest = (mtime=="
12271606
"(SELECT max(mtime) FROM attachment"
12281607
" WHERE target=%Q AND filename=%Q))"
12291608
" 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
12321611
);
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)
12351614
){
12361615
char *zComment;
1237
- if( m.zAttachSrc && m.zAttachSrc[0] ){
1616
+ if( p->zAttachSrc && p->zAttachSrc[0] ){
12381617
zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
1239
- m.zAttachName, m.zAttachTarget);
1618
+ p->zAttachName, p->zAttachTarget);
12401619
}else{
12411620
zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
1242
- m.zAttachName, m.zAttachTarget);
1621
+ p->zAttachName, p->zAttachTarget);
12431622
}
12441623
db_multi_exec(
12451624
"REPLACE INTO event(type,mtime,objid,user,comment)"
12461625
"VALUES('w',%.17g,%d,%Q,%Q)",
1247
- m.rDate, rid, m.zUser, zComment
1626
+ p->rDate, rid, p->zUser, zComment
12481627
);
12491628
free(zComment);
12501629
}else{
12511630
char *zComment;
1252
- if( m.zAttachSrc && m.zAttachSrc[0] ){
1631
+ if( p->zAttachSrc && p->zAttachSrc[0] ){
12531632
zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
1254
- m.zAttachName, m.zAttachTarget);
1633
+ p->zAttachName, p->zAttachTarget);
12551634
}else{
12561635
zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
1257
- m.zAttachName, m.zAttachTarget);
1636
+ p->zAttachName, p->zAttachTarget);
12581637
}
12591638
db_multi_exec(
12601639
"REPLACE INTO event(type,mtime,objid,user,comment)"
12611640
"VALUES('t',%.17g,%d,%Q,%Q)",
1262
- m.rDate, rid, m.zUser, zComment
1641
+ p->rDate, rid, p->zUser, zComment
12631642
);
12641643
free(zComment);
12651644
}
12661645
}
12671646
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;
12911653
}
12921654
--- 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 @@
4141
uint32 bits[2];
4242
unsigned char in[64];
4343
};
4444
typedef struct Context MD5Context;
4545
46
+#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
47
+# define byteReverse(A,B)
48
+#else
4649
/*
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.
4852
*/
4953
static void byteReverse (unsigned char *buf, unsigned longs){
5054
uint32 t;
5155
do {
5256
t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
@@ -53,10 +57,12 @@
5357
((unsigned)buf[1]<<8 | buf[0]);
5458
*(uint32 *)buf = t;
5559
buf += 4;
5660
} while (--longs);
5761
}
62
+#endif
63
+
5864
/* The four core functions - F1 is optimized somewhat */
5965
6066
/* #define F1(x, y, z) (x & y | ~x & z) */
6167
#define F1(x, y, z) (z ^ (x & (y ^ z)))
6268
#define F2(x, y, z) F1(z, x, y)
@@ -314,10 +320,30 @@
314320
** Add the content of a blob to the incremental MD5 checksum.
315321
*/
316322
void md5sum_step_blob(Blob *p){
317323
md5sum_step_text(blob_buffer(p), blob_size(p));
318324
}
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
+}
319345
320346
/*
321347
** Finish the incremental MD5 checksum. Store the result in blob pOut
322348
** if pOut!=0. Also return a pointer to the result.
323349
**
324350
--- 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 @@
151151
int *aC2; /* Changes from pPivot to pV2 */
152152
int i1, i2; /* Index into aC1[] and aC2[] */
153153
int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
154154
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
155155
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";
157157
static const char zMid[] = "============================\n";
158
- static const char zEnd[] = "<<<<<<< END MERGE CONFLICT\n";
158
+ static const char zEnd[] = ">>>>>>> END MERGE CONFLICT\n";
159159
160160
blob_zero(pOut); /* Merge results stored in pOut */
161161
162162
/* Compute the edits that occur from pPivot => pV1 (into aC1)
163163
** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
164164
--- 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 @@
111111
112112
/*
113113
** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
114114
*/
115115
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;
120120
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;
123123
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;
126126
return 1;
127127
}
128128
129129
/*
130130
** Convert a symbolic tag name into the UUID of a check-in that contains
@@ -280,11 +280,11 @@
280280
281281
if( zName==0 || zName[0]==0 ) return 0;
282282
blob_init(&name, zName, -1);
283283
if( name_to_uuid(&name, -1) ){
284284
blob_reset(&name);
285
- for(i=0; zName[i] && isdigit(zName[i]); i++){}
285
+ for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
286286
if( zName[i]==0 ){
287287
rid = atoi(zName);
288288
if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
289289
return rid;
290290
}
@@ -348,11 +348,11 @@
348348
if( zName==0 || zName[0]==0 ) return 0;
349349
blob_init(&name, zName, -1);
350350
rc = name_to_uuid(&name, -1);
351351
if( rc==1 ){
352352
blob_reset(&name);
353
- for(i=0; zName[i] && isdigit(zName[i]); i++){}
353
+ for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
354354
if( zName[i]==0 ){
355355
rid = atoi(zName);
356356
if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
357357
return rid;
358358
}
359359
--- 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 @@
6262
6363
/*
6464
** Change the size of the queue so that it contains N slots
6565
*/
6666
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);
6868
p->sz = N;
6969
}
7070
7171
/*
7272
** Insert element e into the queue.
7373
--- 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 @@
560560
int i;
561561
int limit = flag_alternateform ? va_arg(ap,int) : -1;
562562
char *e = va_arg(ap,char*);
563563
if( e==0 ){e="";}
564564
length = StrNLen32(e, limit);
565
- zExtra = bufpt = malloc(length+1);
565
+ zExtra = bufpt = fossil_malloc(length+1);
566566
for( i=0; i<length; i++ ){
567567
if( e[i]=='\\' ){
568568
bufpt[i]='/';
569569
}else{
570570
bufpt[i]=e[i];
@@ -605,11 +605,11 @@
605605
int i, j, n, cnt;
606606
n = blob_size(pBlob);
607607
if( limit>=0 && limit<n ) n = limit;
608608
for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; }
609609
if( n+cnt+2 > etBUFSIZE ){
610
- bufpt = zExtra = malloc( n + cnt + 2 );
610
+ bufpt = zExtra = fossil_malloc( n + cnt + 2 );
611611
}else{
612612
bufpt = buf;
613613
}
614614
bufpt[0] = '\'';
615615
for(i=0, j=1; i<n; i++, j++){
@@ -634,12 +634,11 @@
634634
if( escarg[i]=='\'' ) n++;
635635
}
636636
needQuote = !isnull && xtype==etSQLESCAPE2;
637637
n += i + 1 + needQuote*2;
638638
if( n>etBUFSIZE ){
639
- bufpt = zExtra = malloc( n );
640
- if( bufpt==0 ) return -1;
639
+ bufpt = zExtra = fossil_malloc( n );
641640
}else{
642641
bufpt = buf;
643642
}
644643
j = 0;
645644
if( needQuote ) bufpt[j++] = '\'';
646645
--- 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 @@
8282
static int ttyOutput; /* Do progress output */
8383
static Bag bagDone; /* Bag of records rebuilt */
8484
8585
static char *zFNameFormat; /* Format string for filenames on deconstruct */
8686
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
+
87102
88103
/*
89104
** Called after each artifact is processed
90105
*/
91106
static void rebuild_step_done(rid){
92107
/* assert( bag_find(&bagDone, rid)==0 ); */
93108
bag_insert(&bagDone, rid);
94109
if( ttyOutput ){
95110
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);
99113
}
100114
}
101115
}
102116
103117
/*
@@ -167,20 +181,21 @@
167181
rebuild_step_done(rid);
168182
169183
/* Call all children recursively */
170184
rid = 0;
171185
for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172
- Stmt q2;
186
+ static Stmt q2;
173187
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);
175190
if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176191
Blob delta, next;
177192
db_ephemeral_blob(&q2, 0, &delta);
178193
blob_uncompress(&delta, &delta);
179194
blob_delta_apply(pBase, &delta, &next);
180195
blob_reset(&delta);
181
- db_finalize(&q2);
196
+ db_reset(&q2);
182197
if( i<nChild ){
183198
rebuild_step(cid, sz, &next);
184199
}else{
185200
/* Tail recursion */
186201
rid = cid;
@@ -187,11 +202,11 @@
187202
size = sz;
188203
blob_reset(pBase);
189204
*pBase = next;
190205
}
191206
}else{
192
- db_finalize(&q2);
207
+ db_reset(&q2);
193208
blob_reset(pBase);
194209
}
195210
}
196211
bag_clear(&children);
197212
}
@@ -232,17 +247,17 @@
232247
*/
233248
int rebuild_db(int randomize, int doOut){
234249
Stmt s;
235250
int errCnt = 0;
236251
char *zTable;
252
+ int incrSize;
237253
238254
bag_init(&bagDone);
239255
ttyOutput = doOut;
240256
processCnt = 0;
241257
if (!g.fQuiet) {
242
- printf("0 (0%%)...\r");
243
- fflush(stdout);
258
+ percent_complete(0);
244259
}
245260
db_multi_exec(zSchemaUpdates);
246261
for(;;){
247262
zTable = db_text(0,
248263
"SELECT name FROM sqlite_master /*scan*/"
@@ -270,10 +285,12 @@
270285
);
271286
db_multi_exec(
272287
"DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
273288
);
274289
totalSize = db_int(0, "SELECT count(*) FROM blob");
290
+ incrSize = totalSize/100;
291
+ totalSize += incrSize*2;
275292
db_prepare(&s,
276293
"SELECT rid, size FROM blob /*scan*/"
277294
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
278295
" AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
279296
);
@@ -307,10 +324,19 @@
307324
}
308325
}
309326
db_finalize(&s);
310327
manifest_crosslink_end();
311328
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
+ }
312338
if(!g.fQuiet && ttyOutput ){
313339
printf("\n");
314340
}
315341
return errCnt;
316342
}
@@ -326,11 +352,13 @@
326352
*/
327353
void rebuild_database(void){
328354
int forceFlag;
329355
int randomizeFlag;
330356
int errCnt;
357
+ int omitVerify;
331358
359
+ omitVerify = find_option("noverify",0,0)!=0;
332360
forceFlag = find_option("force","f",0)!=0;
333361
randomizeFlag = find_option("randomize", 0, 0)!=0;
334362
if( g.argc==3 ){
335363
db_open_repository(g.argv[2]);
336364
}else{
@@ -347,10 +375,11 @@
347375
if( errCnt && !forceFlag ){
348376
printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
349377
errCnt);
350378
db_end_transaction(1);
351379
}else{
380
+ if( omitVerify ) verify_cancel();
352381
db_end_transaction(0);
353382
}
354383
}
355384
356385
/*
@@ -371,10 +400,32 @@
371400
"UPDATE config SET value='detached-' || value"
372401
" WHERE name='project-name' AND value NOT GLOB 'detached-*';"
373402
);
374403
db_end_transaction(0);
375404
}
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
+}
376427
377428
/*
378429
** COMMAND: scrub
379430
** %fossil scrub [--verily] [--force] [REPOSITORY]
380431
**
381432
--- 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 @@
8888
/*
8989
** Remove whitespace from both ends of a string.
9090
*/
9191
char *trim_string(const char *zOrig){
9292
int i;
93
- while( isspace(*zOrig) ){ zOrig++; }
93
+ while( fossil_isspace(*zOrig) ){ zOrig++; }
9494
i = strlen(zOrig);
95
- while( i>0 && isspace(zOrig[i-1]) ){ i--; }
95
+ while( i>0 && fossil_isspace(zOrig[i-1]) ){ i--; }
9696
return mprintf("%.*s", i, zOrig);
9797
}
9898
9999
/*
100100
** Extract a numeric (integer) value from a string.
101101
*/
102102
char *extract_integer(const char *zOrig){
103103
if( zOrig == NULL || zOrig[0] == 0 ) return "";
104
- while( *zOrig && !isdigit(*zOrig) ){ zOrig++; }
104
+ while( *zOrig && !fossil_isdigit(*zOrig) ){ zOrig++; }
105105
if( *zOrig ){
106106
/* we have a digit. atoi() will get as much of the number as it
107107
** can. We'll run it through mprintf() to get a string. Not
108108
** an efficient way to do it, but effective.
109109
*/
@@ -118,18 +118,18 @@
118118
** which also converts any CRNL sequence into a single NL.
119119
*/
120120
char *remove_blank_lines(const char *zOrig){
121121
int i, j, n;
122122
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; }
124124
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--; }
126126
z = mprintf("%.*s", n, &zOrig[j]);
127127
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]) ){
129129
z[j] = z[i];
130
- while(isspace(z[j]) && z[j] != '\n' ){ j--; }
130
+ while(fossil_isspace(z[j]) && z[j] != '\n' ){ j--; }
131131
j++;
132132
continue;
133133
}
134134
135135
z[j++] = z[i];
@@ -145,11 +145,11 @@
145145
** This is the SQLite authorizer callback used to make sure that the
146146
** SQL statements entered by users do not try to do anything untoward.
147147
** If anything suspicious is tried, set *(char**)pError to an error
148148
** message obtained from malloc.
149149
*/
150
-static int report_query_authorizer(
150
+int report_query_authorizer(
151151
void *pError,
152152
int code,
153153
const char *zArg1,
154154
const char *zArg2,
155155
const char *zArg3,
@@ -212,11 +212,11 @@
212212
int rc;
213213
214214
/* First make sure the SQL is a single query command by verifying that
215215
** the first token is "SELECT" and that there are no unquoted semicolons.
216216
*/
217
- for(i=0; isspace(zSql[i]); i++){}
217
+ for(i=0; fossil_isspace(zSql[i]); i++){}
218218
if( strncasecmp(&zSql[i],"select",6)!=0 ){
219219
return mprintf("The SQL must be a SELECT statement");
220220
}
221221
for(i=0; zSql[i]; i++){
222222
if( zSql[i]==';' ){
@@ -583,39 +583,10 @@
583583
@ FROM ticket
584584
@ </pre></blockquote>
585585
@
586586
}
587587
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)&amp;order_by=%d(nCol)&amp;\
611
- @ order_dir=%s(desc?"ASC":"DESC")\
612
- @ %s(zExtra)">%h(zCol)</a></th>
613
- }
614
-}
615
-#endif
616
-
617588
/*
618589
** The state of the report generation.
619590
*/
620591
struct GenerateHTML {
621592
int rn; /* Report number */
@@ -723,11 +694,11 @@
723694
724695
/* Output the data for this entry from the database
725696
*/
726697
zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
727698
if( zBg==0 ) zBg = "white";
728
- @ <tr bgcolor="%h(zBg)">
699
+ @ <tr style="background-color:%h(zBg)">
729700
zTid = 0;
730701
zPage[0] = 0;
731702
for(i=0; i<nArg; i++){
732703
char *zData;
733704
if( i==pState->iBg ) continue;
@@ -738,11 +709,11 @@
738709
@ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td>
739710
zTid = 0;
740711
}
741712
if( zData[0] ){
742713
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)>
744715
blob_init(&content, zData, -1);
745716
wiki_convert(&content, 0, 0);
746717
blob_reset(&content);
747718
}
748719
}else if( azName[i][0]=='#' ){
@@ -772,15 +743,15 @@
772743
** spaces.
773744
*/
774745
static void output_no_tabs(const char *z){
775746
while( z && z[0] ){
776747
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++){}
778749
if( i>0 ){
779750
cgi_printf("%.*s", i, z);
780751
}
781
- for(j=i; isspace(z[j]); j++){}
752
+ for(j=i; fossil_isspace(z[j]); j++){}
782753
if( j>i ){
783754
cgi_printf("%*s", j-i, "");
784755
}
785756
z += j;
786757
}
@@ -816,21 +787,21 @@
816787
** Generate HTML that describes a color key.
817788
*/
818789
void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){
819790
int i, j, k;
820791
char *zSafeKey, *zToFree;
821
- while( isspace(*zClrKey) ) zClrKey++;
792
+ while( fossil_isspace(*zClrKey) ) zClrKey++;
822793
if( zClrKey[0]==0 ) return;
823794
@ <table %s(zTabArgs)>
824795
if( horiz ){
825796
@ <tr>
826797
}
827798
zToFree = zSafeKey = mprintf("%h", zClrKey);
828799
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++){}
832803
for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
833804
if( !horiz ){
834805
cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n",
835806
i, zSafeKey, k-j, &zSafeKey[j]);
836807
}else{
@@ -942,5 +913,168 @@
942913
sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
943914
sqlite3_set_authorizer(g.db, 0, 0);
944915
cgi_set_content_type("text/plain");
945916
}
946917
}
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
+}
9471081
--- 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)&amp;order_by=%d(nCol)&amp;\
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 @@
241241
@ -- UUID but we do not (yet) know the file content.
242242
@ --
243243
@ CREATE TABLE phantom(
244244
@ rid INTEGER PRIMARY KEY -- Record ID of the phantom
245245
@ );
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);
246256
@
247257
@ -- Unclustered records. An unclustered record is a record (including
248258
@ -- a cluster records themselves) that is not mentioned by some other
249259
@ -- cluster.
250260
@ --
251261
--- 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 @@
4242
int nPattern = strlen(zPattern);
4343
Search *p;
4444
char *z;
4545
int i;
4646
47
- p = malloc( nPattern + sizeof(*p) + 1);
48
- if( p==0 ) fossil_panic("out of memory");
47
+ p = fossil_malloc( nPattern + sizeof(*p) + 1);
4948
z = (char*)&p[1];
5049
strcpy(z, zPattern);
5150
memset(p, 0, sizeof(*p));
5251
while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){
53
- while( !isalnum(*z) && *z ){ z++; }
52
+ while( !fossil_isalnum(*z) && *z ){ z++; }
5453
if( *z==0 ) break;
5554
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++){}
5756
p->a[p->nTerm].n = i;
5857
z += i;
5958
p->nTerm++;
6059
}
6160
return p;
6261
--- 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 @@
694694
}
695695
if( iVal ){
696696
@ <input type="checkbox" name="%s(zQParm)" checked="checked" />
697697
@ <b>%s(zLabel)</b>
698698
}else{
699
- @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b>
699
+ @ <input type="checkbox" name="%s(zQParm)" /> <b>%s(zLabel)</b>
700700
}
701701
}
702702
703703
/*
704704
** Generate an entry box for an attribute.
@@ -877,11 +877,11 @@
877877
login_insert_csrf_secret();
878878
for(pSet=ctrlSettings; pSet->name!=0; pSet++){
879879
if( pSet->width==0 ){
880880
onoff_attribute(pSet->name, pSet->name,
881881
pSet->var!=0 ? pSet->var : pSet->name,
882
- pSet->def[0]=='1');
882
+ is_truth(pSet->def));
883883
@ <br />
884884
}
885885
}
886886
@ </td><td style="width: 30;"></td><td valign="top">
887887
for(pSet=ctrlSettings; pSet->name!=0; pSet++){
888888
--- 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 @@
11
/*
2
-** This implementation of SHA1 is adapted from the example implementation
3
-** contained in RFC-3174.
2
+** This implementation of SHA1.
43
*/
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
164
#include <sys/types.h>
175
#include "config.h"
186
#include "sha1.h"
197
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
+ }
413186
}
414187
415188
416189
/*
417190
** Convert a digest into base-16. digest should be declared as
@@ -441,18 +214,18 @@
441214
/*
442215
** Add more text to the incremental SHA1 checksum.
443216
*/
444217
void sha1sum_step_text(const char *zText, int nBytes){
445218
if( !incrInit ){
446
- SHA1Reset(&incrCtx);
219
+ SHA1Init(&incrCtx);
447220
incrInit = 1;
448221
}
449222
if( nBytes<=0 ){
450223
if( nBytes==0 ) return;
451224
nBytes = strlen(zText);
452225
}
453
- SHA1Input(&incrCtx, (unsigned char*)zText, nBytes);
226
+ SHA1Update(&incrCtx, (unsigned char*)zText, nBytes);
454227
}
455228
456229
/*
457230
** Add the content of a blob to the incremental SHA1 checksum.
458231
*/
@@ -470,11 +243,11 @@
470243
*/
471244
char *sha1sum_finish(Blob *pOut){
472245
unsigned char zResult[20];
473246
static char zOut[41];
474247
sha1sum_step_text(0,0);
475
- SHA1Result(&incrCtx, zResult);
248
+ SHA1Final(&incrCtx, zResult);
476249
incrInit = 0;
477250
DigestToBase16(zResult, zOut);
478251
if( pOut ){
479252
blob_zero(pOut);
480253
blob_append(pOut, zOut, 40);
@@ -497,21 +270,21 @@
497270
498271
in = fopen(zFilename,"rb");
499272
if( in==0 ){
500273
return 1;
501274
}
502
- SHA1Reset(&ctx);
275
+ SHA1Init(&ctx);
503276
for(;;){
504277
int n;
505278
n = fread(zBuf, 1, sizeof(zBuf), in);
506279
if( n<=0 ) break;
507
- SHA1Input(&ctx, (unsigned char*)zBuf, (unsigned)n);
280
+ SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
508281
}
509282
fclose(in);
510283
blob_zero(pCksum);
511284
blob_resize(pCksum, 40);
512
- SHA1Result(&ctx, zResult);
285
+ SHA1Final(&ctx, zResult);
513286
DigestToBase16(zResult, blob_buffer(pCksum));
514287
return 0;
515288
}
516289
517290
/*
@@ -523,19 +296,19 @@
523296
*/
524297
int sha1sum_blob(const Blob *pIn, Blob *pCksum){
525298
SHA1Context ctx;
526299
unsigned char zResult[20];
527300
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));
530303
if( pIn==pCksum ){
531304
blob_reset(pCksum);
532305
}else{
533306
blob_zero(pCksum);
534307
}
535308
blob_resize(pCksum, 40);
536
- SHA1Result(&ctx, zResult);
309
+ SHA1Final(&ctx, zResult);
537310
DigestToBase16(zResult, blob_buffer(pCksum));
538311
return 0;
539312
}
540313
541314
/*
@@ -545,13 +318,13 @@
545318
char *sha1sum(const char *zIn){
546319
SHA1Context ctx;
547320
unsigned char zResult[20];
548321
char zDigest[41];
549322
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);
553326
DigestToBase16(zResult, zDigest);
554327
return mprintf("%s", zDigest);
555328
}
556329
557330
/*
@@ -575,11 +348,11 @@
575348
static char *zProjectId = 0;
576349
SHA1Context ctx;
577350
unsigned char zResult[20];
578351
char zDigest[41];
579352
580
- SHA1Reset(&ctx);
353
+ SHA1Init(&ctx);
581354
if( zProjectId==0 ){
582355
zProjectId = db_get("project-code", 0);
583356
584357
/* On the first xfer request of a clone, the project-code is not yet
585358
** known. Use the cleartext password, since that is all we have.
@@ -586,16 +359,16 @@
586359
*/
587360
if( zProjectId==0 ){
588361
return mprintf("%s", zPw);
589362
}
590363
}
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);
597370
DigestToBase16(zResult, zDigest);
598371
return mprintf("%s", zDigest);
599372
}
600373
601374
/*
602375
603376
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
--- 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 @@
650650
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
651651
** [sqlite_version()] and [sqlite_source_id()].
652652
*/
653653
#define SQLITE_VERSION "3.7.3"
654654
#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"
656656
657657
/*
658658
** CAPI3REF: Run-Time Library Version Numbers
659659
** KEYWORDS: sqlite3_version, sqlite3_sourceid
660660
**
@@ -26956,16 +26956,26 @@
2695626956
2695726957
/* Parameter isDelete is only used on vxworks. Express this explicitly
2695826958
** here to prevent compiler warnings about unused parameters.
2695926959
*/
2696026960
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
2696126972
2696226973
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
2696326974
pNew->h = h;
2696426975
pNew->dirfd = dirfd;
2696526976
pNew->fileFlags = 0;
26966
- assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */
2696726977
pNew->zPath = zFilename;
2696826978
2696926979
#if OS_VXWORKS
2697026980
pNew->pId = vxworksFindFileId(zFilename);
2697126981
if( pNew->pId==0 ){
@@ -39764,34 +39774,34 @@
3976439774
/* Higher-level routines never call this function if database is not
3976539775
** writable. But check anyway, just for robustness. */
3976639776
if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
3976739777
3976839778
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) );
3976939795
3977039796
/* Mark the page as dirty. If the page has already been written
3977139797
** to the journal then we can return right away.
3977239798
*/
3977339799
sqlite3PcacheMakeDirty(pPg);
3977439800
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
3977539801
assert( !pagerUseWal(pPager) );
39776
- assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
3977739802
}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) );
3979339803
3979439804
/* The transaction journal now exists and we have a RESERVED or an
3979539805
** EXCLUSIVE lock on the main database file. Write the current page to
3979639806
** the transaction journal if it is not there already.
3979739807
*/
@@ -43589,11 +43599,11 @@
4358943599
** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left
4359043600
** unchanged.
4359143601
**
4359243602
** SQLITE_OK is returned if no error is encountered (regardless of whether
4359343603
** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned
43594
-** if some error
43604
+** if an error occurs.
4359543605
*/
4359643606
static int walRestartLog(Wal *pWal){
4359743607
int rc = SQLITE_OK;
4359843608
int cnt;
4359943609
@@ -43622,10 +43632,12 @@
4362243632
walIndexWriteHdr(pWal);
4362343633
pInfo->nBackfill = 0;
4362443634
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
4362543635
assert( pInfo->aReadMark[0]==0 );
4362643636
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
43637
+ }else if( rc!=SQLITE_BUSY ){
43638
+ return rc;
4362743639
}
4362843640
}
4362943641
walUnlockShared(pWal, WAL_READ_LOCK(0));
4363043642
pWal->readLock = -1;
4363143643
cnt = 0;
@@ -54130,18 +54142,11 @@
5413054142
}else if( pMem->flags & MEM_Int ){
5413154143
return (double)pMem->u.i;
5413254144
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
5413354145
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
5413454146
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);
5414354148
return val;
5414454149
}else{
5414554150
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
5414654151
return (double)0;
5414754152
}
@@ -87647,11 +87652,10 @@
8764787652
addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
8764887653
sqlite3VdbeJumpHere(v, addr1);
8764987654
sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
8765087655
sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
8765187656
sqlite3VdbeJumpHere(v, addr2);
87652
- pSelect->iLimit = 0;
8765387657
}
8765487658
}
8765587659
8765687660
/*
8765787661
** Add code to implement the OFFSET
@@ -87926,15 +87930,15 @@
8792687930
break;
8792787931
}
8792887932
#endif
8792987933
}
8793087934
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.
8793287938
*/
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 ){
8793687940
sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
8793787941
}
8793887942
}
8793987943
8794087944
/*
@@ -88065,14 +88069,10 @@
8806588069
}
8806688070
}
8806788071
sqlite3ReleaseTempReg(pParse, regRow);
8806888072
sqlite3ReleaseTempReg(pParse, regRowid);
8806988073
88070
- /* LIMIT has been implemented by the pushOntoSorter() routine.
88071
- */
88072
- assert( p->iLimit==0 );
88073
-
8807488074
/* The bottom of the loop
8807588075
*/
8807688076
sqlite3VdbeResolveLabel(v, addrContinue);
8807788077
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
8807888078
sqlite3VdbeResolveLabel(v, addrBreak);
@@ -89386,11 +89386,10 @@
8938689386
}
8938789387
8938889388
/* Separate the left and the right query from one another
8938989389
*/
8939089390
p->pPrior = 0;
89391
- pPrior->pRightmost = 0;
8939289391
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
8939389392
if( pPrior->pPrior==0 ){
8939489393
sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
8939589394
}
8939689395
8939789396
--- 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 @@
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110110
#define SQLITE_VERSION "3.7.3"
111111
#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"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
118118
--- 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 @@
103103
@ <tr><th>Server&nbsp;ID:</th><td>
104104
@ %h(db_get("server-code",""))
105105
@ </td></tr>
106106
107107
@ <tr><th>Fossil&nbsp;Version:</th><td>
108
- @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
108
+ @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) (%h(COMPILER_NAME))
109109
@ </td></tr>
110110
@ <tr><th>SQLite&nbsp;Version:</th><td>
111111
sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
112112
SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
113113
@ %s(zBuf)
114114
--- src/stat.c
+++ src/stat.c
@@ -103,11 +103,11 @@
103 @ <tr><th>Server&nbsp;ID:</th><td>
104 @ %h(db_get("server-code",""))
105 @ </td></tr>
106
107 @ <tr><th>Fossil&nbsp;Version:</th><td>
108 @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
109 @ </td></tr>
110 @ <tr><th>SQLite&nbsp;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&nbsp;ID:</th><td>
104 @ %h(db_get("server-code",""))
105 @ </td></tr>
106
107 @ <tr><th>Fossil&nbsp;Version:</th><td>
108 @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) (%h(COMPILER_NAME))
109 @ </td></tr>
110 @ <tr><th>SQLite&nbsp;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/style.c
+++ src/style.c
@@ -101,10 +101,11 @@
101101
Th_Store("baseurl", g.zBaseURL);
102102
Th_Store("index_page", db_get("index-page","/home"));
103103
Th_Store("current_page", g.zPath);
104104
Th_Store("manifest_version", MANIFEST_VERSION);
105105
Th_Store("manifest_date", MANIFEST_DATE);
106
+ Th_Store("compiler_name", COMPILER_NAME);
106107
if( g.zLogin ){
107108
Th_Store("login", g.zLogin);
108109
}
109110
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
110111
Th_Render(zHeader);
111112
--- 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 @@
5656
}
5757
zUrl = db_get("last-sync-url", 0);
5858
if( zUrl==0 ){
5959
return; /* No default server */
6060
}
61
- zPw = db_get("last-sync-pw", 0);
61
+ zPw = unobscure(db_get("last-sync-pw", 0));
6262
url_parse(zUrl);
6363
if( g.urlUser!=0 && g.urlPasswd==0 ){
6464
g.urlPasswd = mprintf("%s", zPw);
6565
}
6666
if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
@@ -93,11 +93,11 @@
9393
url_proxy_options();
9494
db_find_and_open_repository(1);
9595
db_open_config(0);
9696
if( g.argc==2 ){
9797
zUrl = db_get("last-sync-url", 0);
98
- zPw = db_get("last-sync-pw", 0);
98
+ zPw = unobscure(db_get("last-sync-pw", 0));
9999
if( db_get_boolean("auto-sync",1) ) configSync = CONFIGSET_SHUN;
100100
}else if( g.argc==3 ){
101101
zUrl = g.argv[2];
102102
}
103103
if( zUrl==0 ){
@@ -105,11 +105,11 @@
105105
usage("URL");
106106
}
107107
url_parse(zUrl);
108108
if( !g.dontKeepUrl ){
109109
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);
111111
}
112112
if( g.urlUser!=0 && g.urlPasswd==0 ){
113113
if( zPw==0 ){
114114
url_prompt_for_password();
115115
}else{
@@ -232,11 +232,11 @@
232232
if( g.urlUser && g.urlPasswd==0 ){
233233
url_prompt_for_password();
234234
}
235235
db_set("last-sync-url", g.urlCanonical, 0);
236236
if( g.urlPasswd ){
237
- db_set("last-sync-pw", g.urlPasswd, 0);
237
+ db_set("last-sync-pw", obscure(g.urlPasswd), 0);
238238
}else{
239239
db_unset("last-sync-pw", 0);
240240
}
241241
}
242242
}
243243
--- 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 @@
355355
** fossil update tag:decaf
356356
**
357357
** will assume that "decaf" is a tag/branch name.
358358
**
359359
** 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' \\
361361
** --user-override user
362362
** in order to import history from other scm systems
363363
*/
364364
void tag_cmd(void){
365365
int n;
366366
--- 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 @@
77
** declared in th.h, so this file serves as both a part of the language
88
** implementation and an example of how to extend the language with
99
** new commands.
1010
*/
1111
12
+#include "config.h"
1213
#include "th.h"
1314
#include <string.h>
1415
#include <assert.h>
1516
1617
int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){
@@ -571,11 +572,11 @@
571572
return Th_WrongNumArgs(interp, "return ?value?");
572573
}
573574
if( argc==2 ){
574575
Th_SetResult(interp, argv[1], argl[1]);
575576
}
576
- return (int)ctx;
577
+ return FOSSIL_PTR_TO_INT(ctx);
577578
}
578579
579580
/*
580581
** TH Syntax:
581582
**
582583
--- 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 @@
3030
3131
/*
3232
** Implementations of malloc() and free() to pass to the interpreter.
3333
*/
3434
static void *xMalloc(unsigned int n){
35
- void *p = malloc(n);
35
+ void *p = fossil_malloc(n);
3636
if( p ){
3737
nOutstandingMalloc++;
3838
}
3939
return p;
4040
}
@@ -435,20 +435,20 @@
435435
int inBracket = 0;
436436
if( z[0]=='<' ){
437437
inBracket = 1;
438438
z++;
439439
}
440
- if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
440
+ if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){
441441
z += 3;
442442
i += 3;
443
- }else if( isalpha(z[0]) ){
443
+ }else if( fossil_isalpha(z[0]) ){
444444
z ++;
445445
i += 1;
446446
}else{
447447
return 0;
448448
}
449
- while( isalnum(z[0]) || z[0]=='_' ){
449
+ while( fossil_isalnum(z[0]) || z[0]=='_' ){
450450
z++;
451451
i++;
452452
}
453453
if( inBracket ){
454454
if( z[0]!='>' ) return 0;
455455
--- 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 @@
830830
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
831831
zSearch, zSearch);
832832
url_add_parameter(&url, "s", zSearch);
833833
}
834834
if( zAfter ){
835
- while( isspace(zAfter[0]) ){ zAfter++; }
835
+ while( fossil_isspace(zAfter[0]) ){ zAfter++; }
836836
if( zAfter[0] ){
837837
blob_appendf(&sql,
838838
" AND event.mtime>=(SELECT julianday(%Q, 'utc'))"
839839
" ORDER BY event.mtime ASC", zAfter);
840840
url_add_parameter(&url, "a", zAfter);
@@ -841,21 +841,21 @@
841841
zBefore = 0;
842842
}else{
843843
zAfter = 0;
844844
}
845845
}else if( zBefore ){
846
- while( isspace(zBefore[0]) ){ zBefore++; }
846
+ while( fossil_isspace(zBefore[0]) ){ zBefore++; }
847847
if( zBefore[0] ){
848848
blob_appendf(&sql,
849849
" AND event.mtime<=(SELECT julianday(%Q, 'utc'))"
850850
" ORDER BY event.mtime DESC", zBefore);
851851
url_add_parameter(&url, "b", zBefore);
852852
}else{
853853
zBefore = 0;
854854
}
855855
}else if( zCirca ){
856
- while( isspace(zCirca[0]) ){ zCirca++; }
856
+ while( fossil_isspace(zCirca[0]) ){ zCirca++; }
857857
if( zCirca[0] ){
858858
double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca);
859859
Blob sql2;
860860
blob_init(&sql2, blob_str(&sql), -1);
861861
blob_appendf(&sql2,
@@ -943,10 +943,13 @@
943943
if( nEntry<200 ){
944944
timeline_submenu(&url, "200 Entries", "n", "200", 0);
945945
}
946946
}
947947
}
948
+ if( P("showsql") ){
949
+ @ <blockquote>%h(blob_str(&sql))</blockquote>
950
+ }
948951
blob_zero(&sql);
949952
db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/");
950953
@ <h2>%b(&desc)</h2>
951954
blob_reset(&desc);
952955
www_print_timeline(&q, tmFlags, 0);
@@ -1057,12 +1060,12 @@
10571060
*/
10581061
static int isIsoDate(const char *z){
10591062
return strlen(z)==10
10601063
&& z[4]=='-'
10611064
&& z[7]=='-'
1062
- && isdigit(z[0])
1063
- && isdigit(z[5]);
1065
+ && fossil_isdigit(z[0])
1066
+ && fossil_isdigit(z[5]);
10641067
}
10651068
10661069
/*
10671070
** COMMAND: timeline
10681071
**
10691072
--- 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 @@
5353
db_prepare(&q, "PRAGMA table_info(ticket)");
5454
while( db_step(&q)==SQLITE_ROW ){
5555
const char *zField = db_column_text(&q, 1);
5656
if( strncmp(zField,"tkt_",4)==0 ) continue;
5757
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) );
6259
}
6360
azField[nField] = mprintf("%s", zField);
6461
nField++;
6562
}
6663
db_finalize(&q);
@@ -217,25 +214,25 @@
217214
*/
218215
void ticket_rebuild_entry(const char *zTktUuid){
219216
char *zTag = mprintf("tkt-%s", zTktUuid);
220217
int tagid = tag_findid(zTag, 1);
221218
Stmt q;
222
- Manifest manifest;
223
- Blob content;
219
+ Manifest *pTicket;
224220
int createFlag = 1;
225221
226222
db_multi_exec(
227223
"DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid
228224
);
229225
db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
230226
while( db_step(&q)==SQLITE_ROW ){
231227
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
+ }
237234
createFlag = 0;
238235
}
239236
db_finalize(&q);
240237
}
241238
@@ -445,11 +442,11 @@
445442
blob_appendf(&tktchng, "J +%s %z\n", azField[i],
446443
fossilize(azAppend[i], -1));
447444
}else{
448445
zValue = Th_Fetch(azField[i], &nValue);
449446
if( zValue ){
450
- while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; }
447
+ while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
451448
if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){
452449
if( strncmp(azField[i], "private_", 8)==0 ){
453450
zValue = db_conceal(zValue, nValue);
454451
blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue);
455452
}else{
@@ -469,11 +466,18 @@
469466
*(const char**)pUuid = zUuid;
470467
blob_appendf(&tktchng, "K %s\n", zUuid);
471468
blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : "");
472469
md5sum_blob(&tktchng, &cksum);
473470
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 ){
475479
Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
476480
"}<br />\n",
477481
blob_str(&tktchng));
478482
}else{
479483
rid = content_put(&tktchng, 0, 0);
@@ -749,12 +753,11 @@
749753
" AND blob.rid=attachid"
750754
" ORDER BY 1 DESC",
751755
tagid, tagid
752756
);
753757
while( db_step(&q)==SQLITE_ROW ){
754
- Blob content;
755
- Manifest m;
758
+ Manifest *pTicket;
756759
char zShort[12];
757760
const char *zDate = db_column_text(&q, 0);
758761
int rid = db_column_int(&q, 1);
759762
const char *zChngUuid = db_column_text(&q, 2);
760763
const char *zFile = db_column_text(&q, 4);
@@ -773,22 +776,22 @@
773776
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
774777
@ (rid %d(rid)) by
775778
hyperlink_to_user(zUser,zDate," on");
776779
hyperlink_to_date(zDate, ".</p>");
777780
}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 ){
780783
@
781784
@ <p>Ticket change
782785
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
783786
@ (rid %d(rid)) by
784
- hyperlink_to_user(m.zUser,zDate," on");
787
+ hyperlink_to_user(pTicket->zUser,zDate," on");
785788
hyperlink_to_date(zDate, ":");
786789
@ </p>
787
- ticket_output_change_artifact(&m);
790
+ ticket_output_change_artifact(pTicket);
788791
}
789
- manifest_clear(&m);
792
+ manifest_destroy(pTicket);
790793
}
791794
}
792795
db_finalize(&q);
793796
style_footer();
794797
}
@@ -830,5 +833,239 @@
830833
}
831834
blob_reset(&val);
832835
}
833836
@ </ol>
834837
}
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
+}
8351072
--- 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 @@
279279
db_end_transaction(1); /* With --nochange, rollback changes */
280280
}else{
281281
if( g.argc<=3 ){
282282
/* All files updated. Shift the current checkout to the target. */
283283
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
284
+ checkout_set_all_exe(vid);
284285
manifest_to_disk(tid);
285286
db_lset_int("checkout", tid);
286287
}else{
287288
/* A subset of files have been checked out. Keep the current
288289
** checkout unchanged. */
@@ -302,13 +303,13 @@
302303
const char *revision, /* The checkin containing the file */
303304
const char *file, /* Full treename of the file */
304305
Blob *content, /* Put the content here */
305306
int errCode /* Error code if file not found. Panic if 0. */
306307
){
307
- Blob mfile;
308
- Manifest m;
309
- int i, rid=0;
308
+ Manifest *pManifest;
309
+ ManifestFile *pFile;
310
+ int rid=0;
310311
311312
if( revision ){
312313
rid = name_to_rid(revision);
313314
}else{
314315
rid = db_lget_int("checkout", 0);
@@ -315,21 +316,22 @@
315316
}
316317
if( !is_a_version(rid) ){
317318
if( errCode>0 ) return errCode;
318319
fossil_fatal("no such checkin: %s", revision);
319320
}
320
- content_get(rid, &mfile);
321
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
321322
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);
327329
return content_get(rid, content);
328330
}
329331
}
330
- manifest_clear(&m);
332
+ manifest_destroy(pManifest);
331333
if( errCode<=0 ){
332334
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
333335
}
334336
}else if( errCode<=0 ){
335337
fossil_panic("could not parse manifest for checkin: %s", revision);
@@ -386,14 +388,15 @@
386388
}else{
387389
int vid;
388390
vid = db_lget_int("checkout", 0);
389391
vfile_check_signature(vid, 0);
390392
db_multi_exec(
393
+ "DELETE FROM vmerge;"
391394
"INSERT INTO torevert "
392395
"SELECT pathname"
393396
" FROM vfile "
394
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname"
397
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
395398
);
396399
}
397400
blob_zero(&record);
398401
db_prepare(&q, "SELECT name FROM torevert");
399402
while( db_step(&q)==SQLITE_ROW ){
400403
--- 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 @@
2323
/*
2424
** Convert a string to lower-case.
2525
*/
2626
static void url_tolower(char *z){
2727
while( *z ){
28
- *z = tolower(*z);
28
+ *z = fossil_tolower(*z);
2929
z++;
3030
}
3131
}
3232
3333
/*
@@ -109,11 +109,11 @@
109109
}
110110
url_tolower(g.urlName);
111111
if( c==':' ){
112112
g.urlPort = 0;
113113
i++;
114
- while( (c = zUrl[i])!=0 && isdigit(c) ){
114
+ while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
115115
g.urlPort = g.urlPort*10 + c - '0';
116116
i++;
117117
}
118118
g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
119119
}else{
120120
--- 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 @@
2727
** onto the end of a blob.
2828
*/
2929
static void strip_string(Blob *pBlob, char *z){
3030
int i;
3131
blob_reset(pBlob);
32
- while( isspace(*z) ){ z++; }
32
+ while( fossil_isspace(*z) ){ z++; }
3333
for(i=0; z[i]; i++){
3434
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--; }
3636
z[i] = 0;
3737
break;
3838
}
3939
if( z[i]<' ' ) z[i] = ' ';
4040
}
4141
--- 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 @@
8585
}
8686
}
8787
}
8888
8989
/*
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.
9691
*/
97
-void vfile_build(int vid, Blob *p){
92
+void vfile_build(int vid){
9893
int rid;
99
- char *zName, *zUuid;
10094
Stmt ins;
101
- Blob line, token, name, uuid;
102
- int seenHeader = 0;
95
+ Manifest *p;
96
+ ManifestFile *pFile;
97
+
10398
db_begin_transaction();
10499
vfile_verify_not_phantom(vid, 0, 0);
100
+ p = manifest_get(vid, CFTYPE_MANIFEST);
101
+ if( p==0 ) return;
105102
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
106103
db_prepare(&ins,
107104
"INSERT INTO vfile(vid,rid,mrid,pathname) "
108105
" VALUES(:vid,:id,:id,:name)");
109106
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);
135115
}
136116
db_finalize(&ins);
117
+ manifest_destroy(p);
137118
db_end_transaction(0);
138119
}
139120
140121
/*
141122
** Check the file signature of the disk image for every VFILE of vid.
@@ -369,10 +350,11 @@
369350
}
370351
fseek(in, 0L, SEEK_END);
371352
sprintf(zBuf, " %ld\n", ftell(in));
372353
fseek(in, 0L, SEEK_SET);
373354
md5sum_step_text(zBuf, -1);
355
+ /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
374356
for(;;){
375357
int n;
376358
n = fread(zBuf, 1, sizeof(zBuf), in);
377359
if( n<=0 ) break;
378360
md5sum_step_text(zBuf, n);
@@ -422,10 +404,11 @@
422404
int rid = db_column_int(&q, 1);
423405
md5sum_step_text(zName, -1);
424406
content_get(rid, &file);
425407
sprintf(zBuf, " %d\n", blob_size(&file));
426408
md5sum_step_text(zBuf, -1);
409
+ /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
427410
md5sum_step_blob(&file);
428411
blob_reset(&file);
429412
}
430413
db_finalize(&q);
431414
md5sum_finish(pOut);
@@ -438,41 +421,43 @@
438421
**
439422
** If pManOut is not NULL then fill it with the checksum found in the
440423
** "R" card near the end of the manifest.
441424
*/
442425
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;
446430
char zBuf[100];
447431
448432
blob_zero(pOut);
449433
if( pManOut ){
450434
blob_zero(pManOut);
451435
}
452436
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 ){
455439
fossil_panic("manifest file (%d) is malformed", vid);
456440
}
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);
460445
content_get(fid, &file);
461446
sprintf(zBuf, " %d\n", blob_size(&file));
462447
md5sum_step_text(zBuf, -1);
463448
md5sum_step_blob(&file);
464449
blob_reset(&file);
465450
}
466451
if( pManOut ){
467
- if( m.zRepoCksum ){
468
- blob_append(pManOut, m.zRepoCksum, -1);
452
+ if( pManifest->zRepoCksum ){
453
+ blob_append(pManOut, pManifest->zRepoCksum, -1);
469454
}else{
470455
blob_zero(pManOut);
471456
}
472457
}
473
- manifest_clear(&m);
458
+ manifest_destroy(pManifest);
474459
md5sum_finish(pOut);
475460
}
476461
477462
/*
478463
** COMMAND: test-agg-cksum
479464
--- 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 @@
8686
if( !g.okRdWiki ){
8787
cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
8888
}
8989
if( zIndexPage ){
9090
const char *zPathInfo = P("PATH_INFO");
91
+ while( zIndexPage[0]=='/' ) zIndexPage++;
9192
if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0;
9293
}
9394
if( zIndexPage ){
94
- while( zIndexPage[0]=='/' ) zIndexPage++;
9595
cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage);
9696
}
9797
if( zPageName ){
9898
login_check_credentials();
9999
g.zExtra = zPageName;
@@ -127,11 +127,11 @@
127127
void wiki_page(void){
128128
char *zTag;
129129
int rid = 0;
130130
int isSandbox;
131131
Blob wiki;
132
- Manifest m;
132
+ Manifest *pWiki = 0;
133133
const char *zPageName;
134134
char *zBody = mprintf("%s","<i>Empty Page</i>");
135135
Stmt q;
136136
int cnt = 0;
137137
@@ -179,20 +179,14 @@
179179
"SELECT rid FROM tagxref"
180180
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
181181
" ORDER BY mtime DESC", zTag
182182
);
183183
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;
194188
}
195189
}
196190
if( !g.isHome ){
197191
if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
198192
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
@@ -249,13 +243,11 @@
249243
if( cnt ){
250244
@ </ul>
251245
}
252246
db_finalize(&q);
253247
254
- if( !isSandbox ){
255
- manifest_clear(&m);
256
- }
248
+ manifest_destroy(pWiki);
257249
style_footer();
258250
}
259251
260252
/*
261253
** WEBPAGE: wikiedit
@@ -264,11 +256,11 @@
264256
void wikiedit_page(void){
265257
char *zTag;
266258
int rid = 0;
267259
int isSandbox;
268260
Blob wiki;
269
- Manifest m;
261
+ Manifest *pWiki = 0;
270262
const char *zPageName;
271263
char *zHtmlPageName;
272264
int n;
273265
const char *z;
274266
char *zBody = (char*)P("w");
@@ -298,19 +290,12 @@
298290
free(zTag);
299291
if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
300292
login_needed();
301293
return;
302294
}
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;
312297
}
313298
}
314299
if( P("submit")!=0 && zBody!=0 ){
315300
char *zDate;
316301
Blob cksum;
@@ -377,13 +362,11 @@
377362
@ <br />
378363
@ <input type="submit" name="preview" value="Preview Your Changes" />
379364
@ <input type="submit" name="submit" value="Apply These Changes" />
380365
@ <input type="submit" name="cancel" value="Cancel" />
381366
@ </div></form>
382
- if( !isSandbox ){
383
- manifest_clear(&m);
384
- }
367
+ manifest_destroy(pWiki);
385368
style_footer();
386369
}
387370
388371
/*
389372
** WEBPAGE: wikinew
@@ -477,27 +460,25 @@
477460
if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
478461
char *zDate;
479462
Blob cksum;
480463
int nrid;
481464
Blob body;
482
- Blob content;
483465
Blob wiki;
484
- Manifest m;
466
+ Manifest *pWiki = 0;
485467
486468
blob_zero(&body);
487469
if( isSandbox ){
488470
blob_appendf(&body, db_get("sandbox",""));
489471
appendRemark(&body);
490472
db_set("sandbox", blob_str(&body), 0);
491473
}else{
492474
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);
497479
}
498
- manifest_clear(&m);
499480
blob_zero(&wiki);
500481
db_begin_transaction();
501482
zDate = db_text(0, "SELECT datetime('now')");
502483
zDate[10] = 'T';
503484
blob_appendf(&wiki, "D %s\n", zDate);
@@ -612,12 +593,11 @@
612593
*/
613594
void wdiff_page(void){
614595
char *zTitle;
615596
int rid1, rid2;
616597
const char *zPageName;
617
- Blob content1, content2;
618
- Manifest m1, m2;
598
+ Manifest *pW1, *pW2 = 0;
619599
Blob w1, w2, d;
620600
621601
login_check_credentials();
622602
rid1 = atoi(PD("a","0"));
623603
if( !g.okHistory ){ login_needed(); return; }
@@ -635,27 +615,24 @@
635615
" WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
636616
" ORDER BY event.mtime DESC LIMIT 1",
637617
zPageName, rid1
638618
);
639619
}
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);
644623
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);
651626
}
652627
blob_zero(&d);
653628
text_diff(&w2, &w1, &d, 5, 1);
654629
@ <pre>
655630
@ %h(blob_str(&d))
656631
@ </pre>
632
+ manifest_destroy(pW1);
633
+ manifest_destroy(pW2);
657634
style_footer();
658635
}
659636
660637
/*
661638
** WEBPAGE: wcontent
@@ -914,35 +891,31 @@
914891
}
915892
916893
if( strncmp(g.argv[2],"export",n)==0 ){
917894
char const *zPageName; /* Name of the wiki page to export */
918895
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
+
923901
if( (g.argc!=4) && (g.argc!=5) ){
924902
usage("export PAGENAME ?FILE?");
925903
}
926904
zPageName = g.argv[3];
927905
rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
928906
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
929907
" ORDER BY x.mtime DESC LIMIT 1",
930908
zPageName
931909
);
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;
939912
}
940913
if( zBody==0 ){
941914
fossil_fatal("wiki page [%s] not found",zPageName);
942915
}
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--){}
944917
zFile = (g.argc==4) ? 0 : g.argv[4];
945918
if( zFile ){
946919
FILE * zF;
947920
short doClose = 0;
948921
if( (1 == strlen(zFile)) && ('-'==zFile[0]) ){
@@ -955,12 +928,13 @@
955928
fossil_fatal("wiki export could not open output file for writing.");
956929
}
957930
fprintf(zF,"%.*s\n", i, zBody);
958931
if( doClose ) fclose(zF);
959932
}else{
960
- printf("%.*s\n", i, zBody);
933
+ printf("%.*s\n", i, zBody);
961934
}
935
+ manifest_destroy(pWiki);
962936
return;
963937
}else
964938
if( strncmp(g.argv[2],"commit",n)==0
965939
|| strncmp(g.argv[2],"create",n)==0 ){
966940
char *zPageName;
967941
--- 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 @@
412412
static int markupLength(const char *z){
413413
int n = 1;
414414
int inparen = 0;
415415
int c;
416416
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++; }
419419
c = z[n];
420420
if( c=='/' && z[n+1]=='>' ){ return n+2; }
421
- if( c!='>' && !isspace(c) ) return 0;
421
+ if( c!='>' && !fossil_isspace(c) ) return 0;
422422
while( (c = z[n])!=0 && (c!='>' || inparen) ){
423423
if( c==inparen ){
424424
inparen = 0;
425425
}else if( inparen==0 && (c=='"' || c=='\'') ){
426426
inparen = c;
@@ -437,11 +437,11 @@
437437
** of characters through the closing "\n". If not, return 0.
438438
*/
439439
static int paragraphBreakLength(const char *z){
440440
int i, n;
441441
int nNewline = 1;
442
- for(i=1, n=0; isspace(z[i]); i++){
442
+ for(i=1, n=0; fossil_isspace(z[i]); i++){
443443
if( z[i]=='\n' ){
444444
nNewline++;
445445
n = i;
446446
}
447447
}
@@ -482,14 +482,14 @@
482482
*/
483483
static int isElement(const char *z){
484484
int i;
485485
assert( z[0]=='&' );
486486
if( z[1]=='#' ){
487
- for(i=2; isdigit(z[i]); i++){}
487
+ for(i=2; fossil_isdigit(z[i]); i++){}
488488
return i>2 && z[i]==';';
489489
}else{
490
- for(i=1; isalpha(z[i]); i++){}
490
+ for(i=1; fossil_isalpha(z[i]); i++){}
491491
return i>1 && z[i]==';';
492492
}
493493
}
494494
495495
/*
@@ -511,11 +511,11 @@
511511
while( z[n]==' ' || z[n]=='\t' ){
512512
if( z[n]=='\t' ) i++;
513513
i++;
514514
n++;
515515
}
516
- if( i<2 || isspace(z[n]) ) return 0;
516
+ if( i<2 || fossil_isspace(z[n]) ) return 0;
517517
return n;
518518
}
519519
520520
/*
521521
** Check to see if the z[] string is the beginning of a enumeration value.
@@ -536,11 +536,11 @@
536536
if( z[n]=='\t' ) i++;
537537
i++;
538538
n++;
539539
}
540540
if( i<2 ) return 0;
541
- for(i=0; isdigit(z[n]); i++, n++){}
541
+ for(i=0; fossil_isdigit(z[n]); i++, n++){}
542542
if( i==0 ) return 0;
543543
if( z[n]=='.' ){
544544
n++;
545545
}
546546
i = 0;
@@ -547,11 +547,11 @@
547547
while( z[n]==' ' || z[n]=='\t' ){
548548
if( z[n]=='\t' ) i++;
549549
i++;
550550
n++;
551551
}
552
- if( i<2 || isspace(z[n]) ) return 0;
552
+ if( i<2 || fossil_isspace(z[n]) ) return 0;
553553
return n;
554554
}
555555
556556
/*
557557
** Check to see if the z[] string is the beginning of an indented
@@ -565,11 +565,11 @@
565565
while( z[n]==' ' || z[n]=='\t' ){
566566
if( z[n]=='\t' ) i++;
567567
i++;
568568
n++;
569569
}
570
- if( i<2 || isspace(z[n]) ) return 0;
570
+ if( i<2 || fossil_isspace(z[n]) ) return 0;
571571
return n;
572572
}
573573
574574
/*
575575
** Check to see if the z[] string is a wiki hyperlink. If it is,
@@ -612,16 +612,16 @@
612612
if( z[0]=='\n' ){
613613
n = paragraphBreakLength(z);
614614
if( n>0 ){
615615
*pTokenType = TOKEN_PARAGRAPH;
616616
return n;
617
- }else if( isspace(z[1]) ){
617
+ }else if( fossil_isspace(z[1]) ){
618618
*pTokenType = TOKEN_NEWLINE;
619619
return 1;
620620
}
621621
}
622
- if( (p->state & AT_NEWLINE)!=0 && isspace(z[0]) ){
622
+ if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){
623623
n = listItemLength(z, '*');
624624
if( n>0 ){
625625
*pTokenType = TOKEN_BUL_LI;
626626
return n;
627627
}
@@ -634,11 +634,11 @@
634634
if( n>0 ){
635635
*pTokenType = TOKEN_ENUM;
636636
return n;
637637
}
638638
}
639
- if( (p->state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){
639
+ if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
640640
n = indentLength(z);
641641
if( n>0 ){
642642
*pTokenType = TOKEN_INDENT;
643643
return n;
644644
}
@@ -705,37 +705,37 @@
705705
}else{
706706
p->endTag = 0;
707707
i = 1;
708708
}
709709
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]);
712712
i++;
713713
}
714714
zTag[j] = 0;
715715
p->iCode = findTag(zTag);
716716
p->iType = aMarkup[p->iCode].iType;
717717
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]) ){
720720
int attrOk; /* True to preserver attribute. False to ignore it */
721721
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]);
724724
i++;
725725
}
726726
zTag[j] = 0;
727727
p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
728728
attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
729
- while( isspace(z[i]) ){ z++; }
729
+ while( fossil_isspace(z[i]) ){ z++; }
730730
if( z[i]!='=' ){
731731
p->aAttr[p->nAttr].zValue = 0;
732732
p->aAttr[p->nAttr].cTerm = 0;
733733
c = 0;
734734
}else{
735735
i++;
736
- while( isspace(z[i]) ){ z++; }
736
+ while( fossil_isspace(z[i]) ){ z++; }
737737
if( z[i]=='"' ){
738738
i++;
739739
zValue = &z[i];
740740
while( z[i] && z[i]!='"' ){ i++; }
741741
}else if( z[i]=='\'' ){
@@ -742,11 +742,11 @@
742742
i++;
743743
zValue = &z[i];
744744
while( z[i] && z[i]!='\'' ){ i++; }
745745
}else{
746746
zValue = &z[i];
747
- while( !isspace(z[i]) && z[i]!='>' ){ z++; }
747
+ while( !fossil_isspace(z[i]) && z[i]!='>' ){ z++; }
748748
}
749749
if( attrOk ){
750750
p->aAttr[p->nAttr].zValue = zValue;
751751
p->aAttr[p->nAttr].cTerm = c = z[i];
752752
z[i] = 0;
@@ -755,11 +755,11 @@
755755
}
756756
if( attrOk ){
757757
seen |= aAttribute[iACode].iMask;
758758
p->nAttr++;
759759
}
760
- while( isspace(z[i]) ){ i++; }
760
+ while( fossil_isspace(z[i]) ){ i++; }
761761
if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
762762
}
763763
}
764764
765765
/*
@@ -838,14 +838,11 @@
838838
** if necessary.
839839
*/
840840
static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){
841841
if( p->nStack>=p->nAlloc ){
842842
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]));
847844
}
848845
p->aStack[p->nStack].iCode = elem;
849846
p->aStack[p->nStack].zId = zId;
850847
p->aStack[p->nStack].allowWiki = w;
851848
p->nStack++;
@@ -1067,11 +1064,11 @@
10671064
}
10681065
}
10691066
}else if( g.okHistory ){
10701067
blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget);
10711068
}
1072
- }else if( strlen(zTarget)>=10 && isdigit(zTarget[0]) && zTarget[4]=='-'
1069
+ }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
10731070
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
10741071
blob_appendf(p->pOut, "<a href=\"%s/timeline?c=%T\">", g.zBaseURL, zTarget);
10751072
}else if( strncmp(zTarget, "wiki:", 5)==0
10761073
&& wiki_name_is_wellformed((const unsigned char*)zTarget) ){
10771074
zTarget += 5;
@@ -1245,18 +1242,18 @@
12451242
zTarget = &z[1];
12461243
for(i=1; z[i] && z[i]!=']'; i++){
12471244
if( z[i]=='|' && zDisplay==0 ){
12481245
zDisplay = &z[i+1];
12491246
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; }
12511248
}
12521249
}
12531250
z[i] = 0;
12541251
if( zDisplay==0 ){
12551252
zDisplay = zTarget;
12561253
}else{
1257
- while( isspace(*zDisplay) ) zDisplay++;
1254
+ while( fossil_isspace(*zDisplay) ) zDisplay++;
12581255
}
12591256
openHyperlink(p, zTarget, zClose, sizeof(zClose));
12601257
savedState = p->state;
12611258
p->state &= ~ALLOW_WIKI;
12621259
p->state |= FONT_MARKUP_ONLY;
@@ -1265,11 +1262,11 @@
12651262
blob_append(p->pOut, zClose, -1);
12661263
break;
12671264
}
12681265
case TOKEN_TEXT: {
12691266
int i;
1270
- for(i=0; i<n && isspace(z[i]); i++){}
1267
+ for(i=0; i<n && fossil_isspace(z[i]); i++){}
12711268
if( i<n ) startAutoParagraph(p);
12721269
blob_append(p->pOut, z, n);
12731270
break;
12741271
}
12751272
case TOKEN_RAW: {
@@ -1521,11 +1518,11 @@
15211518
int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
15221519
char *z;
15231520
int i;
15241521
int iStart;
15251522
z = skip_bom(blob_str(pIn));
1526
- for(i=0; isspace(z[i]); i++){}
1523
+ for(i=0; fossil_isspace(z[i]); i++){}
15271524
if( z[i]!='<' ) return 0;
15281525
i++;
15291526
if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
15301527
iStart = i+6;
15311528
for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
15321529
--- 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 @@
106106
wanted -= got;
107107
}
108108
fclose(out);
109109
out = 0;
110110
sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s",
111
- g.argv[0], g.zRepositoryName, zRequestFName, zReplyFName,
111
+ _pgmptr, g.zRepositoryName, zRequestFName, zReplyFName,
112112
inet_ntoa(p->addr.sin_addr), p->zNotFound
113113
);
114
- portable_system(zCmd);
114
+ fossil_system(zCmd);
115115
in = fopen(zReplyFName, "rb");
116116
if( in ){
117117
while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
118118
send(p->s, zHdr, got, 0);
119119
}
@@ -190,11 +190,11 @@
190190
zTempPrefix = mprintf("fossil_server_P%d_", iPort);
191191
printf("Listening for HTTP requests on TCP port %d\n", iPort);
192192
if( zBrowser ){
193193
zBrowser = mprintf(zBrowser, iPort);
194194
printf("Launch webbrowser: %s\n", zBrowser);
195
- portable_system(zBrowser);
195
+ fossil_system(zBrowser);
196196
}
197197
printf("Type Ctrl-C to stop the HTTP server\n");
198198
for(;;){
199199
SOCKET client;
200200
SOCKADDR_IN client_addr;
@@ -207,14 +207,11 @@
207207
}
208208
if( client==INVALID_SOCKET ){
209209
closesocket(s);
210210
fossil_fatal("error from accept()");
211211
}
212
- p = malloc( sizeof(*p) );
213
- if( p==0 ){
214
- fossil_fatal("out of memory");
215
- }
212
+ p = fossil_malloc( sizeof(*p) );
216213
p->id = ++idCnt;
217214
p->s = client;
218215
p->addr = client_addr;
219216
p->zNotFound = zNotFoundOption;
220217
_beginthread(win32_process_one_http_request, 0, (void*)p);
221218
--- 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 @@
7272
/*
7373
** Remember that the other side of the connection already has a copy
7474
** of the file rid.
7575
*/
7676
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
+ }
7884
}
7985
8086
/*
8187
** The aToken[0..nToken-1] blob array is a parse of a "file" line
8288
** message. This routine finishes parsing that message and does
@@ -95,11 +101,11 @@
95101
** be initialized to an empty string.
96102
**
97103
** Any artifact successfully received by this routine is considered to
98104
** be public and is therefore removed from the "private" table.
99105
*/
100
-static void xfer_accept_file(Xfer *pXfer){
106
+static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
101107
int n;
102108
int rid;
103109
int srcid = 0;
104110
Blob content, hash;
105111
@@ -114,13 +120,26 @@
114120
return;
115121
}
116122
blob_zero(&content);
117123
blob_zero(&hash);
118124
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])) ){
120126
/* Ignore files that have been shunned */
121127
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;
122141
}
123142
if( pXfer->nToken==4 ){
124143
Blob src, next;
125144
srcid = rid_from_uuid(&pXfer->aToken[2], 1);
126145
if( content_get(srcid, &src)==0 ){
@@ -467,14 +486,15 @@
467486
** Check to see if the number of unclustered entries is greater than
468487
** 100 and if it is, form a new cluster. Unclustered phantoms do not
469488
** count toward the 100 total. And phantoms are never added to a new
470489
** cluster.
471490
*/
472
-static void create_cluster(void){
491
+void create_cluster(void){
473492
Blob cluster, cksum;
474493
Stmt q;
475494
int nUncl;
495
+ int nRow = 0;
476496
477497
/* We should not ever get any private artifacts in the unclustered table.
478498
** But if we do (because of a bug) now is a good time to delete them. */
479499
db_multi_exec(
480500
"DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
@@ -481,30 +501,41 @@
481501
);
482502
483503
nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
484504
" WHERE NOT EXISTS(SELECT 1 FROM phantom"
485505
" 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
+ }
506537
}
507538
508539
/*
509540
** Send an igot message for every entry in unclustered table.
510541
** Return the number of cards sent.
@@ -606,19 +637,17 @@
606637
blobarray_zero(xfer.aToken, count(xfer.aToken));
607638
cgi_set_content_type(g.zContentType);
608639
blob_zero(&xfer.err);
609640
xfer.pIn = &g.cgiIn;
610641
xfer.pOut = cgi_output_blob();
611
- xfer.mxSend = db_get_int("max-download", 5000000);
642
+ xfer.mxSend = db_get_int("max-download", 20000000);
612643
g.xferPanic = 1;
613644
614645
db_begin_transaction();
615646
db_multi_exec(
616647
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
617648
);
618
- zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
619
- @ # timestamp %s(zNow)
620649
manifest_crosslink_begin();
621650
while( blob_line(xfer.pIn, &xfer.line) ){
622651
if( blob_buffer(&xfer.line)[0]=='#' ) continue;
623652
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
624653
@@ -632,11 +661,11 @@
632661
cgi_reset_content();
633662
@ error not\sauthorized\sto\swrite
634663
nErr++;
635664
break;
636665
}
637
- xfer_accept_file(&xfer);
666
+ xfer_accept_file(&xfer, 0);
638667
if( blob_size(&xfer.err) ){
639668
cgi_reset_content();
640669
@ error %T(blob_str(&xfer.err))
641670
nErr++;
642671
break;
@@ -718,26 +747,42 @@
718747
isPush = 1;
719748
}
720749
}
721750
}else
722751
723
- /* clone
752
+ /* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER?
724753
**
725754
** The client knows nothing. Tell all.
726755
*/
727756
if( blob_eq(&xfer.aToken[0], "clone") ){
757
+ int iVers;
728758
login_check_credentials();
729759
if( !g.okClone ){
730760
cgi_reset_content();
731761
@ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
732762
@ error not\sauthorized\sto\sclone
733763
nErr++;
734764
break;
735765
}
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
+ }
739784
@ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
740785
}else
741786
742787
/* login USER NONCE SIGNATURE
743788
**
@@ -874,10 +919,18 @@
874919
}
875920
if( recvConfig ){
876921
configure_finalize_receive();
877922
}
878923
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
+
879932
db_end_transaction(0);
880933
}
881934
882935
/*
883936
** COMMAND: test-xfer
@@ -943,13 +996,17 @@
943996
int origConfigRcvMask; /* Original value of configRcvMask */
944997
int nFileRecv; /* Number of files received */
945998
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
946999
const char *zCookie; /* Server cookie */
9471000
int nSent, nRcvd; /* Bytes sent and received (after compression) */
1001
+ int cloneSeqno = 1; /* Sequence number for clones */
9481002
Blob send; /* Text we are sending to the server */
9491003
Blob recv; /* Reply we got back from the server */
9501004
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 */
9511008
const char *zSCode = db_get("server-code", "x");
9521009
const char *zPCode = db_get("project-code", 0);
9531010
9541011
if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
9551012
if( pushFlag + pullFlag + cloneFlag == 0
@@ -977,15 +1034,16 @@
9771034
9781035
/*
9791036
** Always begin with a clone, pull, or push message
9801037
*/
9811038
if( cloneFlag ){
982
- blob_appendf(&send, "clone\n");
1039
+ blob_appendf(&send, "clone 2 %d\n", cloneSeqno);
9831040
pushFlag = 0;
9841041
pullFlag = 0;
9851042
nCardSent++;
9861043
/* TBD: Request all transferable configuration values */
1044
+ content_enable_dephantomize(0);
9871045
}else if( pullFlag ){
9881046
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
9891047
nCardSent++;
9901048
}
9911049
if( pushFlag ){
@@ -1009,11 +1067,11 @@
10091067
}
10101068
10111069
/* Generate gimme cards for phantoms and leaf cards
10121070
** for all leaves.
10131071
*/
1014
- if( pullFlag || cloneFlag ){
1072
+ if( pullFlag || (cloneFlag && cloneSeqno==1) ){
10151073
request_phantoms(&xfer, mxPhantomReq);
10161074
}
10171075
if( pushFlag ){
10181076
send_unsent(&xfer);
10191077
nCardSent += send_unclustered(&xfer);
@@ -1058,22 +1116,27 @@
10581116
blob_appendf(&send, "# %s\n", zRandomness);
10591117
free(zRandomness);
10601118
10611119
/* Exchange messages with the server */
10621120
nFileSend = xfer.nFileSent + xfer.nDeltaSent;
1063
- fossil_print(zValueFormat, "Send:",
1121
+ fossil_print(zValueFormat, "Sent:",
10641122
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
10651123
xfer.nFileSent, xfer.nDeltaSent);
10661124
nCardSent = 0;
10671125
nCardRcvd = 0;
10681126
xfer.nFileSent = 0;
10691127
xfer.nDeltaSent = 0;
10701128
xfer.nGimmeSent = 0;
10711129
xfer.nIGotSent = 0;
1130
+ if( !g.cgiOutput && !g.fQuiet ){
1131
+ printf("waiting for server...");
1132
+ }
10721133
fflush(stdout);
10731134
http_exchange(&send, &recv, cloneFlag==0 || nCycle>0);
1135
+ lastPctDone = -1;
10741136
blob_reset(&send);
1137
+ rArrivalTime = db_double(0.0, "SELECT julianday('now')");
10751138
10761139
/* Begin constructing the next message (which might never be
10771140
** sent) by beginning with the pull or push cards
10781141
*/
10791142
if( pullFlag ){
@@ -1086,18 +1149,22 @@
10861149
}
10871150
go = 0;
10881151
10891152
/* Process the reply that came back from the server */
10901153
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
+ }
10911158
if( blob_buffer(&xfer.line)[0]=='#' ){
10921159
const char *zLine = blob_buffer(&xfer.line);
10931160
if( memcmp(zLine, "# timestamp ", 12)==0 ){
10941161
char zTime[20];
10951162
double rDiff;
10961163
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);
10991166
if( rDiff<0.0 ) rDiff = -rDiff;
11001167
if( rDiff>9e98 ) rDiff = 0.0;
11011168
if( (rDiff*24.0*3600.0)>=60.0 ){
11021169
fossil_warning("*** time skew *** server time differs by %s",
11031170
db_timespan_name(rDiff));
@@ -1106,22 +1173,26 @@
11061173
}
11071174
continue;
11081175
}
11091176
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
11101177
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
+ }
11141185
}
11151186
11161187
/* file UUID SIZE \n CONTENT
11171188
** file UUID DELTASRC SIZE \n CONTENT
11181189
**
11191190
** Receive a file transmitted from the server.
11201191
*/
11211192
if( blob_eq(&xfer.aToken[0],"file") ){
1122
- xfer_accept_file(&xfer);
1193
+ xfer_accept_file(&xfer, cloneFlag);
11231194
}else
11241195
11251196
/* gimme UUID
11261197
**
11271198
** Server is requesting a file. If the file is a manifest, assume
@@ -1177,11 +1248,11 @@
11771248
}
11781249
if( zPCode==0 ){
11791250
zPCode = mprintf("%b", &xfer.aToken[2]);
11801251
db_set("project-code", zPCode, 0);
11811252
}
1182
- blob_appendf(&send, "clone\n");
1253
+ blob_appendf(&send, "clone 2 %d\n", cloneSeqno);
11831254
nCardSent++;
11841255
}else
11851256
11861257
/* config NAME SIZE \n CONTENT
11871258
**
@@ -1236,10 +1307,21 @@
12361307
** same server.
12371308
*/
12381309
if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
12391310
db_set("cookie", blob_str(&xfer.aToken[1]), 0);
12401311
}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
12411323
12421324
/* message MESSAGE
12431325
**
12441326
** Print a message. Similar to "error" but does not stop processing.
12451327
**
@@ -1314,10 +1396,12 @@
13141396
nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
13151397
if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
13161398
go = 1;
13171399
mxPhantomReq = nFileRecv*2;
13181400
if( mxPhantomReq<200 ) mxPhantomReq = 200;
1401
+ }else if( cloneFlag && nFileRecv>0 ){
1402
+ go = 1;
13191403
}
13201404
nCardRcvd = 0;
13211405
xfer.nFileRcvd = 0;
13221406
xfer.nDeltaRcvd = 0;
13231407
xfer.nDanglingFile = 0;
@@ -1329,15 +1413,19 @@
13291413
go = 1;
13301414
}
13311415
13321416
/* If this is a clone, the go at least two rounds */
13331417
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;
13341421
};
13351422
transport_stats(&nSent, &nRcvd, 1);
13361423
fossil_print("Total network traffic: %d bytes sent, %d bytes received\n",
13371424
nSent, nRcvd);
13381425
transport_close();
13391426
transport_global_shutdown();
13401427
db_multi_exec("DROP TABLE onremote");
13411428
manifest_crosslink_end();
1429
+ content_enable_dephantomize(1);
13421430
db_end_transaction(0);
13431431
}
13441432
--- 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 @@
102102
for(j=0; j<nDir; j++){
103103
if( strcmp(zName, azDir[j])==0 ) break;
104104
}
105105
if( j>=nDir ){
106106
nDir++;
107
- azDir = realloc(azDir, sizeof(azDir[0])*nDir);
107
+ azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
108108
azDir[j] = mprintf("%s", zName);
109109
zip_add_file(zName, 0);
110110
}
111111
zName[i+1] = c;
112112
}
@@ -313,64 +313,65 @@
313313
** politely expands into a subdir instead of filling your current dir
314314
** with source files. For example, pass a UUID or "ProjectName".
315315
**
316316
*/
317317
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;
321321
Blob filename;
322322
int nPrefix;
323323
324324
content_get(rid, &mfile);
325325
if( blob_size(&mfile)==0 ){
326326
blob_zero(pZip);
327327
return;
328328
}
329
- blob_zero(&file);
330329
blob_zero(&hash);
331
- blob_copy(&file, &mfile);
332330
blob_zero(&filename);
333331
zip_open();
334332
335333
if( zDir && zDir[0] ){
336334
blob_appendf(&filename, "%s/", zDir);
337335
}
338336
nPrefix = blob_size(&filename);
339337
340
- if( manifest_parse(&m, &mfile) ){
338
+ pManifest = manifest_get(rid, CFTYPE_MANIFEST);
339
+ if( pManifest ){
341340
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);
357359
if( fid ){
358360
content_get(fid, &file);
359361
blob_resize(&filename, nPrefix);
360
- blob_append(&filename, m.aFile[i].zName, -1);
362
+ blob_append(&filename, pFile->zName, -1);
361363
zName = blob_str(&filename);
362364
zip_add_folders(zName);
363365
zip_add_file(zName, &file);
364366
blob_reset(&file);
365367
}
366368
}
367
- manifest_clear(&m);
368369
}else{
369370
blob_reset(&mfile);
370
- blob_reset(&file);
371371
}
372
+ manifest_destroy(pManifest);
372373
blob_reset(&filename);
373374
zip_close(pZip);
374375
}
375376
376377
/*
377378
--- 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
--- test/delta1.test
+++ test/delta1.test
@@ -32,11 +32,11 @@
3232
# work properly.
3333
#
3434
set filelist [glob $testdir/*]
3535
foreach f $filelist {
3636
set base [file root [file tail $f]]
37
-puts "base=$base f=$f"
37
+protOut "base=$base f=$f"
3838
set f1 [read_file $f]
3939
write_file t1 $f1
4040
for {set i 0} {$i<100} {incr i} {
4141
write_file t2 [random_changes $f1 1 1 0 0.1]
4242
fossil test-delta t1 t2
4343
--- 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 @@
7777
333 - This is a test of the merging algohm - 3333
7878
444 - If all goes well, we will be pleased - 4444
7979
555 - we think it well and other stuff too - 5555
8080
}
8181
write_file_indented t23 {
82
- >>>>>>> BEGIN MERGE CONFLICT
82
+ <<<<<<< BEGIN MERGE CONFLICT
8383
111 - This is line ONE of the demo program - 1111
8484
============================
8585
111 - This is line one OF the demo program - 1111
86
- <<<<<<< END MERGE CONFLICT
86
+ >>>>>>> END MERGE CONFLICT
8787
222 - The second line program line in code - 2222
8888
333 - This is a test of the merging algohm - 3333
8989
444 - If all goes well, we will be pleased - 4444
9090
555 - we think it well and other stuff too - 5555
9191
}
9292
write_file_indented t32 {
93
- >>>>>>> BEGIN MERGE CONFLICT
93
+ <<<<<<< BEGIN MERGE CONFLICT
9494
111 - This is line one OF the demo program - 1111
9595
============================
9696
111 - This is line ONE of the demo program - 1111
97
- <<<<<<< END MERGE CONFLICT
97
+ >>>>>>> END MERGE CONFLICT
9898
222 - The second line program line in code - 2222
9999
333 - This is a test of the merging algohm - 3333
100100
444 - If all goes well, we will be pleased - 4444
101101
555 - we think it well and other stuff too - 5555
102102
}
@@ -158,26 +158,26 @@
158158
333 - This is a test of the merging algohm - 3333
159159
444 - If all goes well, we will be pleased - 4444
160160
555 - we think it well and other stuff too - 5555
161161
}
162162
write_file_indented t32 {
163
- >>>>>>> BEGIN MERGE CONFLICT
163
+ <<<<<<< BEGIN MERGE CONFLICT
164164
============================
165165
000 - Zero lines added to the beginning of - 0000
166166
111 - This is line one of the demo program - 1111
167
- <<<<<<< END MERGE CONFLICT
167
+ >>>>>>> END MERGE CONFLICT
168168
222 - The second line program line in code - 2222
169169
333 - This is a test of the merging algohm - 3333
170170
444 - If all goes well, we will be pleased - 4444
171171
555 - we think it well and other stuff too - 5555
172172
}
173173
write_file_indented t23 {
174
- >>>>>>> BEGIN MERGE CONFLICT
174
+ <<<<<<< BEGIN MERGE CONFLICT
175175
000 - Zero lines added to the beginning of - 0000
176176
111 - This is line one of the demo program - 1111
177177
============================
178
- <<<<<<< END MERGE CONFLICT
178
+ >>>>>>> END MERGE CONFLICT
179179
222 - The second line program line in code - 2222
180180
333 - This is a test of the merging algohm - 3333
181181
444 - If all goes well, we will be pleased - 4444
182182
555 - we think it well and other stuff too - 5555
183183
}
@@ -293,11 +293,11 @@
293293
STUV
294294
XYZ.
295295
}
296296
write_file_indented t23 {
297297
abcd
298
- >>>>>>> BEGIN MERGE CONFLICT
298
+ <<<<<<< BEGIN MERGE CONFLICT
299299
efgh 2
300300
ijkl 2
301301
mnop 2
302302
qrst
303303
uvwx
@@ -311,11 +311,11 @@
311311
qrst 3
312312
uvwx 3
313313
yzAB 3
314314
CDEF
315315
GHIJ
316
- <<<<<<< END MERGE CONFLICT
316
+ >>>>>>> END MERGE CONFLICT
317317
KLMN
318318
OPQR
319319
STUV
320320
XYZ.
321321
}
@@ -354,11 +354,11 @@
354354
}
355355
write_file_indented t23 {
356356
abcd
357357
efgh 2
358358
ijkl 2
359
- >>>>>>> BEGIN MERGE CONFLICT
359
+ <<<<<<< BEGIN MERGE CONFLICT
360360
mnop
361361
qrst
362362
uvwx
363363
yzAB 2
364364
CDEF 2
@@ -368,13 +368,13 @@
368368
qrst 3
369369
uvwx 3
370370
yzAB 3
371371
CDEF
372372
GHIJ
373
- <<<<<<< END MERGE CONFLICT
373
+ >>>>>>> END MERGE CONFLICT
374374
KLMN
375375
OPQR
376376
STUV
377377
XYZ.
378378
}
379379
fossil test-3 t1 t2 t3 a23
380380
test merge1-7.2 {[same_file t23 a23]}
381381
--- 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
--- test/merge3.test
+++ test/merge3.test
@@ -28,18 +28,18 @@
2828
write_file t1 [join [string trim $basis] \n]\n
2929
write_file t2 [join [string trim $v1] \n]\n
3030
write_file t3 [join [string trim $v2] \n]\n
3131
fossil test-3-way-merge t1 t2 t3 t4
3232
set x [read_file t4]
33
- regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x
33
+ regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x
3434
regsub -all {============================} $x {=} x
35
- regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x
35
+ regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x
3636
set x [split [string trim $x] \n]
3737
set result [string trim $result]
3838
if {$x!=$result} {
39
- puts " Expected \[$result\]"
40
- puts " Got \[$x\]"
39
+ protOut " Expected \[$result\]"
40
+ protOut " Got \[$x\]"
4141
test merge3-$testid 0
4242
} else {
4343
test merge3-$testid 1
4444
}
4545
}
4646
--- 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
--- test/merge4.test
+++ test/merge4.test
@@ -29,29 +29,29 @@
2929
write_file t2 [join [string trim $v1] \n]\n
3030
write_file t3 [join [string trim $v2] \n]\n
3131
fossil test-3-way-merge t1 t2 t3 t4
3232
fossil test-3-way-merge t1 t3 t2 t5
3333
set x [read_file t4]
34
- regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $x {>} x
34
+ regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $x {>} x
3535
regsub -all {============================} $x {=} x
36
- regsub -all {<<<<<<< END MERGE CONFLICT} $x {<} x
36
+ regsub -all {>>>>>>> END MERGE CONFLICT} $x {<} x
3737
set x [split [string trim $x] \n]
3838
set y [read_file t5]
39
- regsub -all {>>>>>>> BEGIN MERGE CONFLICT} $y {>} y
39
+ regsub -all {<<<<<<< BEGIN MERGE CONFLICT} $y {>} y
4040
regsub -all {============================} $y {=} y
41
- regsub -all {<<<<<<< END MERGE CONFLICT} $y {<} y
41
+ regsub -all {>>>>>>> END MERGE CONFLICT} $y {<} y
4242
set y [split [string trim $y] \n]
4343
set result1 [string trim $result1]
4444
if {$x!=$result1} {
45
- puts " Expected \[$result1\]"
46
- puts " Got \[$x\]"
45
+ protOut " Expected \[$result1\]"
46
+ protOut " Got \[$x\]"
4747
test merge3-$testid 0
4848
} else {
4949
set result2 [string trim $result2]
5050
if {$y!=$result2} {
51
- puts " Expected \[$result2\]"
52
- puts " Got \[$y\]"
51
+ protOut " Expected \[$result2\]"
52
+ protOut " Got \[$y\]"
5353
test merge3-$testid 0
5454
} else {
5555
test merge3-$testid 1
5656
}
5757
}
5858
--- 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 @@
3838
set HALT 1
3939
set argv [lreplace $argv $i $i]
4040
} else {
4141
set HALT 0
4242
}
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
+}
4351
4452
if {[llength $argv]==0} {
4553
foreach f [lsort [glob $testdir/*.test]] {
4654
set base [file root [file tail $f]]
4755
lappend argv $base
4856
}
4957
}
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
+}
5081
5182
# Run the fossil program
5283
#
5384
proc fossil {args} {
5485
global fossilexe
5586
set cmd $fossilexe
5687
foreach a $args {
5788
lappend cmd $a
5889
}
59
- puts $cmd
90
+ protOut $cmd
91
+
6092
flush stdout
6193
set rc [catch {eval exec $cmd} result]
6294
global RESULT CODE
6395
set CODE $rc
6496
set RESULT $result
@@ -100,13 +132,13 @@
100132
#
101133
proc test {name expr} {
102134
global bad_test
103135
set r [uplevel 1 [list expr $expr]]
104136
if {$r} {
105
- puts "test $name OK"
137
+ protOut "test $name OK"
106138
} else {
107
- puts "test $name FAILED!"
139
+ protOut "test $name FAILED!"
108140
lappend bad_test $name
109141
if {$::HALT} exit
110142
}
111143
}
112144
set bad_test {}
@@ -167,10 +199,11 @@
167199
append out \n$line
168200
}
169201
return [string range $out 1 end]
170202
}
171203
204
+protInit $fossilexe
172205
foreach testfile $argv {
173
- puts "***** $testfile ******"
206
+ protOut "***** $testfile ******"
174207
source $testdir/$testfile.test
175208
}
176
-puts "[llength $bad_test] errors: $bad_test"
209
+protOut "[llength $bad_test] errors: $bad_test"
177210
--- 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
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -44,73 +44,111 @@
4444
TARGETMACHINE_CC=x86
4545
TARGETMACHINE_LN=ix86
4646
TARGETEXTEND=
4747
endif
4848
49
+# define the project directories
4950
B=..
5051
SRCDIR=$(B)/src/
5152
WINDIR=$(B)/win/
52
-ZLIBSRCDIR=E:/fossil-w32/zlib/
53
+ZLIBSRCDIR=../../zlib/
5354
55
+# define linker command and options
5456
LINK=$(PellesCDir)/bin/polink.exe
5557
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
5658
59
+# define standard C-compiler and flags, used to compile
60
+# the fossil binary. Some special definitions follow for
61
+# special files follow
5762
CC=$(PellesCDir)\bin\pocc.exe
5863
DEFINES=-DFOSSIL_I18N=0 -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
5964
CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES)
6065
INCLUDE=/I $(PellesCDir)\Include\Win /I $(PellesCDir)\Include /I $(ZLIBSRCDIR) /I $(SRCDIR)
6166
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
6274
UTILS=translate.exe mkindex.exe makeheaders.exe
6375
UTILS_OBJ=$(UTILS:.exe=.obj)
76
+UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))
6477
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
7079
SQLITESRC=sqlite3.c
7180
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
7281
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
7382
SQLITEDEFINES=-DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
7483
84
+# define the th scripting files, which need special flags on compile
7585
THSRC=th.c th_lang.c
7686
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
7787
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))
7888
89
+# define the zlib files, needed by this compile
7990
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
8091
ORIGZLIBSRC=$(foreach sf,$(ZLIBSRC),$(ZLIBSRCDIR)$(sf))
8192
ZLIBOBJ=$(foreach sf,$(ZLIBSRC),$(sf:.c=.obj))
8293
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
83103
APPLICATION=fossil.exe
84104
105
+# ###########################################################################
106
+# define the standard make target
85107
.PHONY: default
86108
default: page_index.h headers $(APPLICATION)
87109
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
89117
$(LINK) $(LINKFLAGS) -out:"$@" $<
90118
119
+# compiling standard fossil utils
91120
$(UTILS_OBJ): %.obj: $(SRCDIR)%.c
92121
$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
93122
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
103124
version.obj: $(WINDIR)version.c
104125
$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
105126
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
106139
VERSION.h: version.exe ..\manifest.uuid ..\manifest
107140
version.exe ..\manifest.uuid ..\manifest > $@
108141
142
+# ###########################################################################
143
+# generate the simplified headers
109144
headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h
110145
makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h
111146
echo Done >$@
147
+
148
+# ###########################################################################
149
+# compile C sources with relevant options
112150
113151
$(TRANSLATEDOBJ): %_.obj: %_.c %.h
114152
$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
115153
116154
$(SQLITEOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)%.h
@@ -120,18 +158,29 @@
120158
$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
121159
122160
$(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c
123161
$(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@"
124162
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
127175
128176
.PHONY: clean
129177
clean:
130
- del /F *.obj
131
- del /F *.c
178
+ del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
179
+ del /F $(TRANSLATEDSRC)
132180
del /F *.h headers
181
+ del /F $(RESOURCE)
133182
134183
.PHONY: clobber
135184
clobber: clean
136185
del /F *.exe
137186
138187
--- 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 @@
2424
CFLAGS = -o
2525
BCC = $(DMDIR)\bin\dmc $(CFLAGS)
2626
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL)
2727
LIBS = $(DMDIR)\extra\lib\ zlib wsock32
2828
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
3030
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
3232
33
+RC=$(DMDIR)\bin\rcc
34
+RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
3335
3436
APPNAME = $(OBJDIR)\fossil$(E)
3537
3638
all: $(APPNAME)
3739
38
-$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link
40
+$(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link
3941
cd $(OBJDIR)
4042
$(DMDIR)\bin\link @link
43
+
44
+fossil.res: $B\win\fossil.rc
45
+ $(RC) $(RCFLAGS) -o$@ $**
4146
4247
$(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 > $@
4449
+echo fossil >> $@
4550
+echo fossil >> $@
4651
+echo $(LIBS) >> $@
52
+ +echo. >> $@
53
+ +echo fossil >> $@
4754
4855
4956
5057
translate$E: $(SRCDIR)\translate.c
5158
$(BCC) -o$@ $**
@@ -57,10 +64,13 @@
5764
$(BCC) -o$@ $**
5865
5966
version$E: $B\win\version.c
6067
$(BCC) -o$@ $**
6168
69
+$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
70
+ $(TCC) -o$@ -c -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 $**
71
+
6272
$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
6373
$(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
6474
6575
$(OBJDIR)\th$O : $(SRCDIR)\th.c
6676
$(TCC) -o$@ -c $**
@@ -217,16 +227,28 @@
217227
$(OBJDIR)\doc$O : doc_.c doc.h
218228
$(TCC) -o$@ -c doc_.c
219229
220230
doc_.c : $(SRCDIR)\doc.c
221231
+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 $** > $@
222238
223239
$(OBJDIR)\encode$O : encode_.c encode.h
224240
$(TCC) -o$@ -c encode_.c
225241
226242
encode_.c : $(SRCDIR)\encode.c
227243
+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 $** > $@
228250
229251
$(OBJDIR)\file$O : file_.c file.h
230252
$(TCC) -o$@ -c file_.c
231253
232254
file_.c : $(SRCDIR)\file.c
@@ -507,7 +529,7 @@
507529
508530
zip_.c : $(SRCDIR)\zip.c
509531
+translate$E $** > $@
510532
511533
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
513535
@copy /Y nul: headers
514536
515537
ADDED win/fossil.ico
516538
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

--- 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
--- win/version.c
+++ win/version.c
@@ -10,10 +10,11 @@
1010
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
1111
m = fopen(argv[2],"r");
1212
while(b == fgets(b, sizeof(b)-1,m)){
1313
if(0 == strncmp("D ",b,2)){
1414
printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
15
+ printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
1516
return 0;
1617
}
1718
}
1819
return 1;
1920
}
2021
--- 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
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -95,26 +95,35 @@
9595
may contain no additional text or data beyond what is described here.
9696
9797
Allowed cards in the manifest are as follows:
9898
9999
<blockquote>
100
+<b>B</b> <i>baseline-manifest</i><br>
100101
<b>C</b> <i>checkin-comment</i><br>
101102
<b>D</b> <i>time-and-date-stamp</i><br>
102103
<b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br>
103104
<b>P</b> <i>SHA1-hash</i>+<br>
104105
<b>R</b> <i>repository-checksum</i><br>
105106
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br>
106107
<b>U</b> <i>user-login</i><br>
107108
<b>Z</b> <i>manifest-checksum</i>
108109
</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.
109118
110119
A manifest must have exactly one C-card. The sole argument to
111120
the C-card is a check-in comment that describes the check-in that
112121
the manifest defines. The check-in comment is text. The following
113122
escape sequences are applied to the text:
114123
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
116125
(ASCII 0x5C) is represented as two backslashes "\\". Apart from
117126
space and newline, no other whitespace characters are allowed in
118127
the check-in comment. Nor are any unprintable characters allowed
119128
in the comment.
120129
@@ -125,26 +134,29 @@
125134
126135
<blockquote>
127136
<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>
128137
</blockquote>
129138
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.
133141
The first argument
134142
is the pathname of the file in the check-in relative to the root
135143
of the project file hierarchy. No ".." or "." directories are allowed
136144
within the filename. Space characters are escaped as in C-card
137145
comment text. Backslash characters and newlines are not allowed
138146
within filenames. The directory separator character is a forward
139147
slash (ASCII 0x2F). The second argument to the F-card is the
140148
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
142153
permissions associated with the file. The only special code currently
143154
defined is "x" which means that the file is executable. All files are
144155
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.
146158
The optional 4th argument is the name of the same file as it existed in
147159
the parent check-in. If the name of the file is unchanged from its
148160
parent, then the 4th argument is omitted.
149161
150162
A manifest has zero or one P-cards. Most manifests have one P-card.
@@ -504,10 +516,20 @@
504516
<td>&nbsp;</td>
505517
<td>&nbsp;</td>
506518
<td align=center><b>X</b></td>
507519
<td>&nbsp;</td>
508520
</tr>
521
+<tr>
522
+<td><b>B</b> <i>baseline</i></td>
523
+<td align=center><b>X</b></td>
524
+<td>&nbsp;</td>
525
+<td>&nbsp;</td>
526
+<td>&nbsp;</td>
527
+<td>&nbsp;</td>
528
+<td>&nbsp;</td>
529
+<td>&nbsp;</td>
530
+</tr>
509531
<tr>
510532
<td><b>C</b> <i>comment-text</i></td>
511533
<td align=center><b>X</b></td>
512534
<td>&nbsp;</td>
513535
<td>&nbsp;</td>
514536
--- 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>&nbsp;</td>
505 <td>&nbsp;</td>
506 <td align=center><b>X</b></td>
507 <td>&nbsp;</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>&nbsp;</td>
513 <td>&nbsp;</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>&nbsp;</td>
517 <td>&nbsp;</td>
518 <td align=center><b>X</b></td>
519 <td>&nbsp;</td>
520 </tr>
521 <tr>
522 <td><b>B</b> <i>baseline</i></td>
523 <td align=center><b>X</b></td>
524 <td>&nbsp;</td>
525 <td>&nbsp;</td>
526 <td>&nbsp;</td>
527 <td>&nbsp;</td>
528 <td>&nbsp;</td>
529 <td>&nbsp;</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>&nbsp;</td>
535 <td>&nbsp;</td>
536

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button