Fossil SCM

Another merge from trunk.

michael 2010-09-16 14:13 ttmrichter-skins merge
Commit 1516a26dc85d4758620fbc77ffc4984d770ce93b
+2 -2
--- src/add.c
+++ src/add.c
@@ -53,11 +53,11 @@
5353
fossil_warning("cannot add %s", zPath);
5454
}else{
5555
if( !file_is_simple_pathname(zPath) ){
5656
fossil_fatal("filename contains illegal characters: %s", zPath);
5757
}
58
-#ifdef __MINGW32__
58
+#if defined(_WIN32)
5959
if( db_exists("SELECT 1 FROM vfile"
6060
" WHERE pathname=%Q COLLATE nocase", zPath) ){
6161
db_multi_exec("UPDATE vfile SET deleted=0"
6262
" WHERE pathname=%Q COLLATE nocase", zPath);
6363
}
@@ -152,11 +152,11 @@
152152
db_begin_transaction();
153153
if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
154154
blob_zero(&repo);
155155
}
156156
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
157
-#ifdef __MINGW32__
157
+#if defined(_WIN32)
158158
db_multi_exec(
159159
"CREATE INDEX IF NOT EXISTS vfile_pathname "
160160
" ON vfile(pathname COLLATE nocase)"
161161
);
162162
#endif
163163
--- src/add.c
+++ src/add.c
@@ -53,11 +53,11 @@
53 fossil_warning("cannot add %s", zPath);
54 }else{
55 if( !file_is_simple_pathname(zPath) ){
56 fossil_fatal("filename contains illegal characters: %s", zPath);
57 }
58 #ifdef __MINGW32__
59 if( db_exists("SELECT 1 FROM vfile"
60 " WHERE pathname=%Q COLLATE nocase", zPath) ){
61 db_multi_exec("UPDATE vfile SET deleted=0"
62 " WHERE pathname=%Q COLLATE nocase", zPath);
63 }
@@ -152,11 +152,11 @@
152 db_begin_transaction();
153 if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
154 blob_zero(&repo);
155 }
156 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
157 #ifdef __MINGW32__
158 db_multi_exec(
159 "CREATE INDEX IF NOT EXISTS vfile_pathname "
160 " ON vfile(pathname COLLATE nocase)"
161 );
162 #endif
163
--- src/add.c
+++ src/add.c
@@ -53,11 +53,11 @@
53 fossil_warning("cannot add %s", zPath);
54 }else{
55 if( !file_is_simple_pathname(zPath) ){
56 fossil_fatal("filename contains illegal characters: %s", zPath);
57 }
58 #if defined(_WIN32)
59 if( db_exists("SELECT 1 FROM vfile"
60 " WHERE pathname=%Q COLLATE nocase", zPath) ){
61 db_multi_exec("UPDATE vfile SET deleted=0"
62 " WHERE pathname=%Q COLLATE nocase", zPath);
63 }
@@ -152,11 +152,11 @@
152 db_begin_transaction();
153 if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
154 blob_zero(&repo);
155 }
156 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
157 #if defined(_WIN32)
158 db_multi_exec(
159 "CREATE INDEX IF NOT EXISTS vfile_pathname "
160 " ON vfile(pathname COLLATE nocase)"
161 );
162 #endif
163
+8 -8
--- src/attach.c
+++ src/attach.c
@@ -75,20 +75,20 @@
7575
zFilename = &zFilename[i+1];
7676
i = -1;
7777
}
7878
}
7979
if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
80
- zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
80
+ zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
8181
}else{
82
- zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
82
+ zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
8383
}
8484
@
8585
@ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
86
- @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
86
+ @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
8787
if( zComment ) while( isspace(zComment[0]) ) zComment++;
8888
if( zComment && zComment[0] ){
89
- @ %w(zComment)<br>
89
+ @ %w(zComment)<br />
9090
}
9191
if( zPage==0 && zTkt==0 ){
9292
if( zSrc==0 || zSrc[0]==0 ){
9393
zSrc = "Deleted from";
9494
}else {
@@ -273,13 +273,13 @@
273273
style_header("Add Attachment");
274274
@ <h2>Add Attachment To %s(zTargetType)</h2>
275275
@ <form action="%s(g.zBaseURL)/attachadd" method="POST"
276276
@ enctype="multipart/form-data">
277277
@ File to Attach:
278
- @ <input type="file" name="f" size="60"><br>
279
- @ Description:<br>
280
- @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br>
278
+ @ <input type="file" name="f" size="60"><br />
279
+ @ Description:<br />
280
+ @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br />
281281
if( zTkt ){
282282
@ <input type="hidden" name="tkt" value="%h(zTkt)">
283283
}else{
284284
@ <input type="hidden" name="page" value="%h(zPage)">
285285
}
@@ -351,11 +351,11 @@
351351
cgi_redirect(zFrom);
352352
}
353353
style_header("Delete Attachment");
354354
@ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
355355
@ <p>Confirm that you want to delete the attachment named
356
- @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
356
+ @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
357357
if( zTkt ){
358358
@ <input type="hidden" name="tkt" value="%h(zTkt)">
359359
}else{
360360
@ <input type="hidden" name="page" value="%h(zPage)">
361361
}
362362
--- src/attach.c
+++ src/attach.c
@@ -75,20 +75,20 @@
75 zFilename = &zFilename[i+1];
76 i = -1;
77 }
78 }
79 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
80 zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
81 }else{
82 zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
83 }
84 @
85 @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
86 @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
87 if( zComment ) while( isspace(zComment[0]) ) zComment++;
88 if( zComment && zComment[0] ){
89 @ %w(zComment)<br>
90 }
91 if( zPage==0 && zTkt==0 ){
92 if( zSrc==0 || zSrc[0]==0 ){
93 zSrc = "Deleted from";
94 }else {
@@ -273,13 +273,13 @@
273 style_header("Add Attachment");
274 @ <h2>Add Attachment To %s(zTargetType)</h2>
275 @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
276 @ enctype="multipart/form-data">
277 @ File to Attach:
278 @ <input type="file" name="f" size="60"><br>
279 @ Description:<br>
280 @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br>
281 if( zTkt ){
282 @ <input type="hidden" name="tkt" value="%h(zTkt)">
283 }else{
284 @ <input type="hidden" name="page" value="%h(zPage)">
285 }
@@ -351,11 +351,11 @@
351 cgi_redirect(zFrom);
352 }
353 style_header("Delete Attachment");
354 @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
355 @ <p>Confirm that you want to delete the attachment named
356 @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
357 if( zTkt ){
358 @ <input type="hidden" name="tkt" value="%h(zTkt)">
359 }else{
360 @ <input type="hidden" name="page" value="%h(zPage)">
361 }
362
--- src/attach.c
+++ src/attach.c
@@ -75,20 +75,20 @@
75 zFilename = &zFilename[i+1];
76 i = -1;
77 }
78 }
79 if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
80 zUrlTail = mprintf("tkt=%s&amp;file=%t", zTarget, zFilename);
81 }else{
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 ){
93 zSrc = "Deleted from";
94 }else {
@@ -273,13 +273,13 @@
273 style_header("Add Attachment");
274 @ <h2>Add Attachment To %s(zTargetType)</h2>
275 @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
276 @ enctype="multipart/form-data">
277 @ File to Attach:
278 @ <input type="file" name="f" size="60"><br />
279 @ Description:<br />
280 @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br />
281 if( zTkt ){
282 @ <input type="hidden" name="tkt" value="%h(zTkt)">
283 }else{
284 @ <input type="hidden" name="page" value="%h(zPage)">
285 }
@@ -351,11 +351,11 @@
351 cgi_redirect(zFrom);
352 }
353 style_header("Delete Attachment");
354 @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
355 @ <p>Confirm that you want to delete the attachment named
356 @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
357 if( zTkt ){
358 @ <input type="hidden" name="tkt" value="%h(zTkt)">
359 }else{
360 @ <input type="hidden" name="page" value="%h(zPage)">
361 }
362
+24 -3
--- src/blob.c
+++ src/blob.c
@@ -660,11 +660,11 @@
660660
}
661661
nName = file_simplify_name(zName, nName);
662662
for(i=1; i<nName; i++){
663663
if( zName[i]=='/' ){
664664
zName[i] = 0;
665
-#ifdef __MINGW32__
665
+#if defined(_WIN32)
666666
/*
667667
** On Windows, local path looks like: C:/develop/project/file.txt
668668
** The if stops us from trying to create a directory of a drive letter
669669
** C: in this example.
670670
*/
@@ -672,11 +672,11 @@
672672
#endif
673673
if( file_mkdir(zName, 1) ){
674674
fossil_fatal_recursive("unable to create directory %s", zName);
675675
return 0;
676676
}
677
-#ifdef __MINGW32__
677
+#if defined(_WIN32)
678678
}
679679
#endif
680680
zName[i] = '/';
681681
}
682682
}
@@ -856,11 +856,11 @@
856856
blob_reset(&b3);
857857
}
858858
printf("ok\n");
859859
}
860860
861
-#ifdef __MINGW32__
861
+#if defined(_WIN32)
862862
/*
863863
** Convert every \n character in the given blob into \r\n.
864864
*/
865865
void blob_add_cr(Blob *p){
866866
char *z = p->aData;
@@ -896,5 +896,26 @@
896896
if( z[i]!='\r' ) z[j++] = z[i];
897897
}
898898
z[j] = 0;
899899
p->nUsed = j;
900900
}
901
+
902
+/*
903
+** Shell-escape the given string. Append the result to a blob.
904
+*/
905
+void shell_escape(Blob *pBlob, const char *zIn){
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
+ return;
918
+ }
919
+ }
920
+ blob_append(pBlob, zIn, -1);
921
+}
901922
--- src/blob.c
+++ src/blob.c
@@ -660,11 +660,11 @@
660 }
661 nName = file_simplify_name(zName, nName);
662 for(i=1; i<nName; i++){
663 if( zName[i]=='/' ){
664 zName[i] = 0;
665 #ifdef __MINGW32__
666 /*
667 ** On Windows, local path looks like: C:/develop/project/file.txt
668 ** The if stops us from trying to create a directory of a drive letter
669 ** C: in this example.
670 */
@@ -672,11 +672,11 @@
672 #endif
673 if( file_mkdir(zName, 1) ){
674 fossil_fatal_recursive("unable to create directory %s", zName);
675 return 0;
676 }
677 #ifdef __MINGW32__
678 }
679 #endif
680 zName[i] = '/';
681 }
682 }
@@ -856,11 +856,11 @@
856 blob_reset(&b3);
857 }
858 printf("ok\n");
859 }
860
861 #ifdef __MINGW32__
862 /*
863 ** Convert every \n character in the given blob into \r\n.
864 */
865 void blob_add_cr(Blob *p){
866 char *z = p->aData;
@@ -896,5 +896,26 @@
896 if( z[i]!='\r' ) z[j++] = z[i];
897 }
898 z[j] = 0;
899 p->nUsed = j;
900 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
--- src/blob.c
+++ src/blob.c
@@ -660,11 +660,11 @@
660 }
661 nName = file_simplify_name(zName, nName);
662 for(i=1; i<nName; i++){
663 if( zName[i]=='/' ){
664 zName[i] = 0;
665 #if defined(_WIN32)
666 /*
667 ** On Windows, local path looks like: C:/develop/project/file.txt
668 ** The if stops us from trying to create a directory of a drive letter
669 ** C: in this example.
670 */
@@ -672,11 +672,11 @@
672 #endif
673 if( file_mkdir(zName, 1) ){
674 fossil_fatal_recursive("unable to create directory %s", zName);
675 return 0;
676 }
677 #if defined(_WIN32)
678 }
679 #endif
680 zName[i] = '/';
681 }
682 }
@@ -856,11 +856,11 @@
856 blob_reset(&b3);
857 }
858 printf("ok\n");
859 }
860
861 #if defined(_WIN32)
862 /*
863 ** Convert every \n character in the given blob into \r\n.
864 */
865 void blob_add_cr(Blob *p){
866 char *z = p->aData;
@@ -896,5 +896,26 @@
896 if( z[i]!='\r' ) z[j++] = z[i];
897 }
898 z[j] = 0;
899 p->nUsed = j;
900 }
901
902 /*
903 ** Shell-escape the given string. Append the result to a blob.
904 */
905 void shell_escape(Blob *pBlob, const char *zIn){
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 return;
918 }
919 }
920 blob_append(pBlob, zIn, -1);
921 }
922
+46 -51
--- src/branch.c
+++ src/branch.c
@@ -215,73 +215,71 @@
215215
** Show a timeline of all branches
216216
*/
217217
void brlist_page(void){
218218
Stmt q;
219219
int cnt;
220
+ int showClosed = P("closed")!=0;
220221
221222
login_check_credentials();
222223
if( !g.okRead ){ login_needed(); return; }
223224
224
- style_header("Branches");
225
+ style_header(showClosed ? "Closed Branches" : "Open Branches");
225226
style_submenu_element("Timeline", "Timeline", "brtimeline");
227
+ if( showClosed ){
228
+ style_submenu_element("Open","Open","brlist");
229
+ }else{
230
+ style_submenu_element("Closed","Closed","brlist?closed");
231
+ }
226232
login_anonymous_available();
227233
compute_leaves(0, 1);
228234
style_sidebox_begin("Nomenclature:", "33%");
229235
@ <ol>
230
- @ <li> An <b>open branch</b> is a branch that has one or
236
+ @ <li> An <div class="sideboxDescribed"><a href="brlist">
237
+ @ open branch</a></div> is a branch that has one or
231238
@ more <a href="leaves">open leaves.</a>
232239
@ The presence of open leaves presumably means
233240
@ that the branch is still being extended with new check-ins.</li>
234
- @ <li> A <b>closed branch</b> is a branch with only
235
- @ <a href="leaves?closed">closed leaves</a>.
241
+ @ <li> A <div class="sideboxDescribed"><a href="brlist?closed">
242
+ @ closed branch</a></div> is a branch with only
243
+ @ <div class="sideboxDescribed"><a href="leaves?closed">
244
+ @ closed leaves</a></div>.
236245
@ Closed branches are fixed and do not change (unless they are first
237246
@ reopened)</li>
238247
@ </ol>
239248
style_sidebox_end();
240249
241
- db_prepare(&q,
242
- "SELECT DISTINCT value FROM tagxref"
243
- " WHERE tagid=%d AND value NOT NULL"
244
- " AND rid IN leaves"
245
- " ORDER BY value /*sort*/",
246
- TAG_BRANCH
247
- );
248
- cnt = 0;
249
- while( db_step(&q)==SQLITE_ROW ){
250
- const char *zBr = db_column_text(&q, 0);
251
- if( cnt==0 ){
252
- @ <h2>Open Branches:</h2>
253
- @ <ul>
254
- cnt++;
255
- }
256
- if( g.okHistory ){
257
- @ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
258
- }else{
259
- @ <li><b>%h(zBr)</b></li>
260
- }
261
- }
262
- db_finalize(&q);
263
- if( cnt ){
264
- @ </ul>
265
- }
266
- cnt = 0;
267
- db_prepare(&q,
268
- "SELECT value FROM tagxref"
269
- " WHERE tagid=%d AND value NOT NULL"
270
- " EXCEPT "
271
- "SELECT value FROM tagxref"
272
- " WHERE tagid=%d AND value NOT NULL"
273
- " AND rid IN leaves"
274
- " ORDER BY value /*sort*/",
275
- TAG_BRANCH, TAG_BRANCH
276
- );
277
- while( db_step(&q)==SQLITE_ROW ){
278
- const char *zBr = db_column_text(&q, 0);
279
- if( cnt==0 ){
280
- @ <h2>Closed Branches:</h2>
281
- @ <ul>
282
- cnt++;
250
+ cnt = 0;
251
+ if( !showClosed ){
252
+ db_prepare(&q,
253
+ "SELECT DISTINCT value FROM tagxref"
254
+ " WHERE tagid=%d AND value NOT NULL"
255
+ " AND rid IN leaves"
256
+ " ORDER BY value /*sort*/",
257
+ TAG_BRANCH
258
+ );
259
+ }else{
260
+ db_prepare(&q,
261
+ "SELECT value FROM tagxref"
262
+ " WHERE tagid=%d AND value NOT NULL"
263
+ " EXCEPT "
264
+ "SELECT value FROM tagxref"
265
+ " WHERE tagid=%d AND value NOT NULL"
266
+ " AND rid IN leaves"
267
+ " ORDER BY value /*sort*/",
268
+ TAG_BRANCH, TAG_BRANCH
269
+ );
270
+ }
271
+ while( db_step(&q)==SQLITE_ROW ){
272
+ const char *zBr = db_column_text(&q, 0);
273
+ if( cnt==0 ){
274
+ if( showClosed ){
275
+ @ <h2>Closed Branches:</h2>
276
+ }else{
277
+ @ <h2>Open Branches:</h2>
278
+ }
279
+ @ <ul>
280
+ cnt++;
283281
}
284282
if( g.okHistory ){
285283
@ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
286284
}else{
287285
@ <li><b>%h(zBr)</b></li>
@@ -289,13 +287,11 @@
289287
}
290288
if( cnt ){
291289
@ </ul>
292290
}
293291
db_finalize(&q);
294
- @ </ul>
295
- @ <br clear="both">
296
- @ <script>
292
+ @ <script type="text/JavaScript">
297293
@ function xin(id){
298294
@ }
299295
@ function xout(id){
300296
@ }
301297
@ </script>
@@ -346,14 +342,13 @@
346342
" ORDER BY event.mtime DESC",
347343
timeline_query_for_www(), TAG_BRANCH
348344
);
349345
www_print_timeline(&q, 0, brtimeline_extra);
350346
db_finalize(&q);
351
- @ <br clear="both">
352
- @ <script>
347
+ @ <script type="text/JavaScript">
353348
@ function xin(id){
354349
@ }
355350
@ function xout(id){
356351
@ }
357352
@ </script>
358353
style_footer();
359354
}
360355
--- src/branch.c
+++ src/branch.c
@@ -215,73 +215,71 @@
215 ** Show a timeline of all branches
216 */
217 void brlist_page(void){
218 Stmt q;
219 int cnt;
 
220
221 login_check_credentials();
222 if( !g.okRead ){ login_needed(); return; }
223
224 style_header("Branches");
225 style_submenu_element("Timeline", "Timeline", "brtimeline");
 
 
 
 
 
226 login_anonymous_available();
227 compute_leaves(0, 1);
228 style_sidebox_begin("Nomenclature:", "33%");
229 @ <ol>
230 @ <li> An <b>open branch</b> is a branch that has one or
 
231 @ more <a href="leaves">open leaves.</a>
232 @ The presence of open leaves presumably means
233 @ that the branch is still being extended with new check-ins.</li>
234 @ <li> A <b>closed branch</b> is a branch with only
235 @ <a href="leaves?closed">closed leaves</a>.
 
 
236 @ Closed branches are fixed and do not change (unless they are first
237 @ reopened)</li>
238 @ </ol>
239 style_sidebox_end();
240
241 db_prepare(&q,
242 "SELECT DISTINCT value FROM tagxref"
243 " WHERE tagid=%d AND value NOT NULL"
244 " AND rid IN leaves"
245 " ORDER BY value /*sort*/",
246 TAG_BRANCH
247 );
248 cnt = 0;
249 while( db_step(&q)==SQLITE_ROW ){
250 const char *zBr = db_column_text(&q, 0);
251 if( cnt==0 ){
252 @ <h2>Open Branches:</h2>
253 @ <ul>
254 cnt++;
255 }
256 if( g.okHistory ){
257 @ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
258 }else{
259 @ <li><b>%h(zBr)</b></li>
260 }
261 }
262 db_finalize(&q);
263 if( cnt ){
264 @ </ul>
265 }
266 cnt = 0;
267 db_prepare(&q,
268 "SELECT value FROM tagxref"
269 " WHERE tagid=%d AND value NOT NULL"
270 " EXCEPT "
271 "SELECT value FROM tagxref"
272 " WHERE tagid=%d AND value NOT NULL"
273 " AND rid IN leaves"
274 " ORDER BY value /*sort*/",
275 TAG_BRANCH, TAG_BRANCH
276 );
277 while( db_step(&q)==SQLITE_ROW ){
278 const char *zBr = db_column_text(&q, 0);
279 if( cnt==0 ){
280 @ <h2>Closed Branches:</h2>
281 @ <ul>
282 cnt++;
283 }
284 if( g.okHistory ){
285 @ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
286 }else{
287 @ <li><b>%h(zBr)</b></li>
@@ -289,13 +287,11 @@
289 }
290 if( cnt ){
291 @ </ul>
292 }
293 db_finalize(&q);
294 @ </ul>
295 @ <br clear="both">
296 @ <script>
297 @ function xin(id){
298 @ }
299 @ function xout(id){
300 @ }
301 @ </script>
@@ -346,14 +342,13 @@
346 " ORDER BY event.mtime DESC",
347 timeline_query_for_www(), TAG_BRANCH
348 );
349 www_print_timeline(&q, 0, brtimeline_extra);
350 db_finalize(&q);
351 @ <br clear="both">
352 @ <script>
353 @ function xin(id){
354 @ }
355 @ function xout(id){
356 @ }
357 @ </script>
358 style_footer();
359 }
360
--- src/branch.c
+++ src/branch.c
@@ -215,73 +215,71 @@
215 ** Show a timeline of all branches
216 */
217 void brlist_page(void){
218 Stmt q;
219 int cnt;
220 int showClosed = P("closed")!=0;
221
222 login_check_credentials();
223 if( !g.okRead ){ login_needed(); return; }
224
225 style_header(showClosed ? "Closed Branches" : "Open Branches");
226 style_submenu_element("Timeline", "Timeline", "brtimeline");
227 if( showClosed ){
228 style_submenu_element("Open","Open","brlist");
229 }else{
230 style_submenu_element("Closed","Closed","brlist?closed");
231 }
232 login_anonymous_available();
233 compute_leaves(0, 1);
234 style_sidebox_begin("Nomenclature:", "33%");
235 @ <ol>
236 @ <li> An <div class="sideboxDescribed"><a href="brlist">
237 @ open branch</a></div> is a branch that has one or
238 @ more <a href="leaves">open leaves.</a>
239 @ The presence of open leaves presumably means
240 @ that the branch is still being extended with new check-ins.</li>
241 @ <li> A <div class="sideboxDescribed"><a href="brlist?closed">
242 @ closed branch</a></div> is a branch with only
243 @ <div class="sideboxDescribed"><a href="leaves?closed">
244 @ closed leaves</a></div>.
245 @ Closed branches are fixed and do not change (unless they are first
246 @ reopened)</li>
247 @ </ol>
248 style_sidebox_end();
249
250 cnt = 0;
251 if( !showClosed ){
252 db_prepare(&q,
253 "SELECT DISTINCT value FROM tagxref"
254 " WHERE tagid=%d AND value NOT NULL"
255 " AND rid IN leaves"
256 " ORDER BY value /*sort*/",
257 TAG_BRANCH
258 );
259 }else{
260 db_prepare(&q,
261 "SELECT value FROM tagxref"
262 " WHERE tagid=%d AND value NOT NULL"
263 " EXCEPT "
264 "SELECT value FROM tagxref"
265 " WHERE tagid=%d AND value NOT NULL"
266 " AND rid IN leaves"
267 " ORDER BY value /*sort*/",
268 TAG_BRANCH, TAG_BRANCH
269 );
270 }
271 while( db_step(&q)==SQLITE_ROW ){
272 const char *zBr = db_column_text(&q, 0);
273 if( cnt==0 ){
274 if( showClosed ){
275 @ <h2>Closed Branches:</h2>
276 }else{
277 @ <h2>Open Branches:</h2>
278 }
279 @ <ul>
280 cnt++;
 
 
 
 
 
 
 
 
 
 
 
281 }
282 if( g.okHistory ){
283 @ <li><a href="%s(g.zBaseURL)/timeline?r=%T(zBr)">%h(zBr)</a></li>
284 }else{
285 @ <li><b>%h(zBr)</b></li>
@@ -289,13 +287,11 @@
287 }
288 if( cnt ){
289 @ </ul>
290 }
291 db_finalize(&q);
292 @ <script type="text/JavaScript">
 
 
293 @ function xin(id){
294 @ }
295 @ function xout(id){
296 @ }
297 @ </script>
@@ -346,14 +342,13 @@
342 " ORDER BY event.mtime DESC",
343 timeline_query_for_www(), TAG_BRANCH
344 );
345 www_print_timeline(&q, 0, brtimeline_extra);
346 db_finalize(&q);
347 @ <script type="text/JavaScript">
 
348 @ function xin(id){
349 @ }
350 @ function xout(id){
351 @ }
352 @ </script>
353 style_footer();
354 }
355
+9 -8
--- src/browse.c
+++ src/browse.c
@@ -144,11 +144,11 @@
144144
char zShort[20];
145145
memcpy(zShort, zUuid, 10);
146146
zShort[10] = 0;
147147
@ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
148148
@ %s(blob_str(&dirname))</h2>
149
- zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix);
149
+ zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
150150
if( zD ){
151151
style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
152152
style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
153153
}else{
154154
style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
@@ -157,13 +157,13 @@
157157
@ <h2>The union of all files from all check-ins
158158
@ %s(blob_str(&dirname))</h2>
159159
zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
160160
if( zD ){
161161
style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
162
- style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
162
+ style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
163163
g.zBaseURL, zD);
164
- style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
164
+ style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
165165
g.zBaseURL,zD);
166166
}else{
167167
style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
168168
style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
169169
}
@@ -216,29 +216,30 @@
216216
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
217217
cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
218218
nCol = 4;
219219
nRow = (cnt+nCol-1)/nCol;
220220
db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
221
- @ <table border="0" width="100%%"><tr><td valign="top" width="25%%">
221
+ @ <table class="browser"><tr><td class="browser"><ul class="browser">
222222
i = 0;
223223
while( db_step(&q)==SQLITE_ROW ){
224224
const char *zFN;
225225
if( i==nRow ){
226
- @ </td><td valign="top" width="25%%">
226
+ @ </ul></td><td class="browser"><ul class="browser">
227227
i = 0;
228228
}
229229
i++;
230230
zFN = db_column_text(&q, 0);
231231
if( zFN[0]=='/' ){
232232
zFN++;
233233
@ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
234234
}else if( zCI ){
235235
const char *zUuid = db_column_text(&q, 1);
236
- @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a>
236
+ @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
237237
}else{
238
- @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)</a>
238
+ @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
239
+ @ </a></li>
239240
}
240241
}
241242
db_finalize(&q);
242
- @ </td></tr></table>
243
+ @ </ul></td></tr></table>
243244
style_footer();
244245
}
245246
--- src/browse.c
+++ src/browse.c
@@ -144,11 +144,11 @@
144 char zShort[20];
145 memcpy(zShort, zUuid, 10);
146 zShort[10] = 0;
147 @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
148 @ %s(blob_str(&dirname))</h2>
149 zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix);
150 if( zD ){
151 style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
152 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
153 }else{
154 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
@@ -157,13 +157,13 @@
157 @ <h2>The union of all files from all check-ins
158 @ %s(blob_str(&dirname))</h2>
159 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
160 if( zD ){
161 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
162 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
163 g.zBaseURL, zD);
164 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
165 g.zBaseURL,zD);
166 }else{
167 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
168 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
169 }
@@ -216,29 +216,30 @@
216 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
217 cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
218 nCol = 4;
219 nRow = (cnt+nCol-1)/nCol;
220 db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
221 @ <table border="0" width="100%%"><tr><td valign="top" width="25%%">
222 i = 0;
223 while( db_step(&q)==SQLITE_ROW ){
224 const char *zFN;
225 if( i==nRow ){
226 @ </td><td valign="top" width="25%%">
227 i = 0;
228 }
229 i++;
230 zFN = db_column_text(&q, 0);
231 if( zFN[0]=='/' ){
232 zFN++;
233 @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
234 }else if( zCI ){
235 const char *zUuid = db_column_text(&q, 1);
236 @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a>
237 }else{
238 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)</a>
 
239 }
240 }
241 db_finalize(&q);
242 @ </td></tr></table>
243 style_footer();
244 }
245
--- src/browse.c
+++ src/browse.c
@@ -144,11 +144,11 @@
144 char zShort[20];
145 memcpy(zShort, zUuid, 10);
146 zShort[10] = 0;
147 @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
148 @ %s(blob_str(&dirname))</h2>
149 zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
150 if( zD ){
151 style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
152 style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
153 }else{
154 style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
@@ -157,13 +157,13 @@
157 @ <h2>The union of all files from all check-ins
158 @ %s(blob_str(&dirname))</h2>
159 zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
160 if( zD ){
161 style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
162 style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
163 g.zBaseURL, zD);
164 style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
165 g.zBaseURL,zD);
166 }else{
167 style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
168 style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
169 }
@@ -216,29 +216,30 @@
216 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
217 cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
218 nCol = 4;
219 nRow = (cnt+nCol-1)/nCol;
220 db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
221 @ <table class="browser"><tr><td class="browser"><ul class="browser">
222 i = 0;
223 while( db_step(&q)==SQLITE_ROW ){
224 const char *zFN;
225 if( i==nRow ){
226 @ </ul></td><td class="browser"><ul class="browser">
227 i = 0;
228 }
229 i++;
230 zFN = db_column_text(&q, 0);
231 if( zFN[0]=='/' ){
232 zFN++;
233 @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
234 }else if( zCI ){
235 const char *zUuid = db_column_text(&q, 1);
236 @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
237 }else{
238 @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
239 @ </a></li>
240 }
241 }
242 db_finalize(&q);
243 @ </ul></td></tr></table>
244 style_footer();
245 }
246
+1 -1
--- src/captcha.c
+++ src/captcha.c
@@ -399,11 +399,11 @@
399399
}
400400
}
401401
402402
/*
403403
** Compute a seed value for a captcha. The seed is public and is sent
404
-** has a hidden parameter with the page that contains the captcha. Knowledge
404
+** as a hidden parameter with the page that contains the captcha. Knowledge
405405
** of the seed is insufficient for determining the captcha without additional
406406
** information held only on the server and never revealed.
407407
*/
408408
unsigned int captcha_seed(void){
409409
unsigned int x;
410410
--- src/captcha.c
+++ src/captcha.c
@@ -399,11 +399,11 @@
399 }
400 }
401
402 /*
403 ** Compute a seed value for a captcha. The seed is public and is sent
404 ** has a hidden parameter with the page that contains the captcha. Knowledge
405 ** of the seed is insufficient for determining the captcha without additional
406 ** information held only on the server and never revealed.
407 */
408 unsigned int captcha_seed(void){
409 unsigned int x;
410
--- src/captcha.c
+++ src/captcha.c
@@ -399,11 +399,11 @@
399 }
400 }
401
402 /*
403 ** Compute a seed value for a captcha. The seed is public and is sent
404 ** as a hidden parameter with the page that contains the captcha. Knowledge
405 ** of the seed is insufficient for determining the captcha without additional
406 ** information held only on the server and never revealed.
407 */
408 unsigned int captcha_seed(void){
409 unsigned int x;
410
+34 -150
--- src/cgi.c
+++ src/cgi.c
@@ -20,15 +20,16 @@
2020
** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
2121
** formatting function and its cousins, and routines to encode and
2222
** decode strings in HTML or HTTP.
2323
*/
2424
#include "config.h"
25
-#ifdef __MINGW32__
25
+#ifdef _WIN32
2626
# include <windows.h> /* for Sleep once server works again */
27
-# include <winsock2.h> /* socket operations */
28
-# define sleep Sleep /* windows does not have sleep, but Sleep */
29
-# include <ws2tcpip.h>
27
+# if defined(__MINGW32__)
28
+# define sleep Sleep /* windows does not have sleep, but Sleep */
29
+# include <ws2tcpip.h>
30
+# endif
3031
#else
3132
# include <sys/socket.h>
3233
# include <netinet/in.h>
3334
# include <arpa/inet.h>
3435
# include <sys/times.h>
@@ -851,145 +852,10 @@
851852
cgi_printf("%s = %s <br />\n",
852853
htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
853854
}
854855
}
855856
856
-/*
857
-** Write HTML text for an option menu to standard output. zParam
858
-** is the query parameter that the option menu sets. zDflt is the
859
-** initial value of the option menu. Addition arguments are name/value
860
-** pairs that define values on the menu. The list is terminated with
861
-** a single NULL argument.
862
-*/
863
-void cgi_optionmenu(int in, const char *zP, const char *zD, ...){
864
- va_list ap;
865
- char *zName, *zVal;
866
- int dfltSeen = 0;
867
- cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
868
- va_start(ap, zD);
869
- while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
870
- if( strcmp(zVal,zD)==0 ){ dfltSeen = 1; break; }
871
- }
872
- va_end(ap);
873
- if( !dfltSeen ){
874
- if( zD[0] ){
875
- cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
876
- in+2, "", zD, zD);
877
- }else{
878
- cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n", in+2, "");
879
- }
880
- }
881
- va_start(ap, zD);
882
- while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
883
- if( zName[0] ){
884
- cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
885
- in+2, "",
886
- zVal,
887
- strcmp(zVal, zD) ? "" : " selected",
888
- zName
889
- );
890
- }else{
891
- cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
892
- in+2, "",
893
- strcmp(zVal, zD) ? "" : " selected"
894
- );
895
- }
896
- }
897
- va_end(ap);
898
- cgi_printf("%*s</select>\n", in, "");
899
-}
900
-
901
-/*
902
-** This routine works a lot like cgi_optionmenu() except that the list of
903
-** values is contained in an array. Also, the values are just values, not
904
-** name/value pairs as in cgi_optionmenu.
905
-*/
906
-void cgi_v_optionmenu(
907
- int in, /* Indent by this amount */
908
- const char *zP, /* The query parameter name */
909
- const char *zD, /* Default value */
910
- const char **az /* NULL-terminated list of allowed values */
911
-){
912
- const char *zVal;
913
- int i;
914
- cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
915
- for(i=0; az[i]; i++){
916
- if( strcmp(az[i],zD)==0 ) break;
917
- }
918
- if( az[i]==0 ){
919
- if( zD[0]==0 ){
920
- cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
921
- in+2, "");
922
- }else{
923
- cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
924
- in+2, "", zD, zD);
925
- }
926
- }
927
- while( (zVal = *(az++))!=0 ){
928
- if( zVal[0] ){
929
- cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
930
- in+2, "",
931
- zVal,
932
- strcmp(zVal, zD) ? "" : " selected",
933
- zVal
934
- );
935
- }else{
936
- cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
937
- in+2, "",
938
- strcmp(zVal, zD) ? "" : " selected"
939
- );
940
- }
941
- }
942
- cgi_printf("%*s</select>\n", in, "");
943
-}
944
-
945
-/*
946
-** This routine works a lot like cgi_v_optionmenu() except that the list
947
-** is a list of pairs. The first element of each pair is the value used
948
-** internally and the second element is the value displayed to the user.
949
-*/
950
-void cgi_v_optionmenu2(
951
- int in, /* Indent by this amount */
952
- const char *zP, /* The query parameter name */
953
- const char *zD, /* Default value */
954
- const char **az /* NULL-terminated list of allowed values */
955
-){
956
- const char *zVal;
957
- int i;
958
- cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
959
- for(i=0; az[i]; i+=2){
960
- if( strcmp(az[i],zD)==0 ) break;
961
- }
962
- if( az[i]==0 ){
963
- if( zD[0]==0 ){
964
- cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
965
- in+2, "");
966
- }else{
967
- cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
968
- in+2, "", zD, zD);
969
- }
970
- }
971
- while( (zVal = *(az++))!=0 ){
972
- const char *zName = *(az++);
973
- if( zName[0] ){
974
- cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
975
- in+2, "",
976
- zVal,
977
- strcmp(zVal, zD) ? "" : " selected",
978
- zName
979
- );
980
- }else{
981
- cgi_printf("%*s<option value=\"%h\"%s>&nbsp;</option>\n",
982
- in+2, "",
983
- zVal,
984
- strcmp(zVal, zD) ? "" : " selected"
985
- );
986
- }
987
- }
988
- cgi_printf("%*s</select>\n", in, "");
989
-}
990
-
991857
/*
992858
** This routine works like "printf" except that it has the
993859
** extra formatting capabilities such as %h and %t.
994860
*/
995861
void cgi_printf(const char *zFormat, ...){
@@ -1122,32 +988,45 @@
1122988
while( isspace(*zVal) ){ zVal++; }
1123989
i = strlen(zVal);
1124990
while( i>0 && isspace(zVal[i-1]) ){ i--; }
1125991
zVal[i] = 0;
1126992
for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
1127
- if( strcmp(zFieldName,"user-agent:")==0 ){
1128
- cgi_setenv("HTTP_USER_AGENT", zVal);
1129
- }else if( strcmp(zFieldName,"content-length:")==0 ){
993
+ if( strcmp(zFieldName,"content-length:")==0 ){
1130994
cgi_setenv("CONTENT_LENGTH", zVal);
1131
- }else if( strcmp(zFieldName,"referer:")==0 ){
1132
- cgi_setenv("HTTP_REFERER", zVal);
1133
- }else if( strcmp(zFieldName,"host:")==0 ){
1134
- cgi_setenv("HTTP_HOST", zVal);
1135995
}else if( strcmp(zFieldName,"content-type:")==0 ){
1136996
cgi_setenv("CONTENT_TYPE", zVal);
1137997
}else if( strcmp(zFieldName,"cookie:")==0 ){
1138998
cgi_setenv("HTTP_COOKIE", zVal);
999
+ }else if( strcmp(zFieldName,"https:")==0 ){
1000
+ cgi_setenv("HTTPS", zVal);
1001
+ }else if( strcmp(zFieldName,"host:")==0 ){
1002
+ cgi_setenv("HTTP_HOST", zVal);
11391003
}else if( strcmp(zFieldName,"if-none-match:")==0 ){
11401004
cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
11411005
}else if( strcmp(zFieldName,"if-modified-since:")==0 ){
11421006
cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
11431007
}
1008
+#if 0
1009
+ else if( strcmp(zFieldName,"referer:")==0 ){
1010
+ cgi_setenv("HTTP_REFERER", zVal);
1011
+ }else if( strcmp(zFieldName,"user-agent:")==0 ){
1012
+ cgi_setenv("HTTP_USER_AGENT", zVal);
1013
+ }
1014
+#endif
11441015
}
11451016
11461017
cgi_init();
11471018
}
11481019
1020
+#if INTERFACE
1021
+/*
1022
+** Bitmap values for the flags parameter to cgi_http_server().
1023
+*/
1024
+#define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
1025
+
1026
+#endif /* INTERFACE */
1027
+
11491028
/*
11501029
** Maximum number of child processes that we can have running
11511030
** at one time before we start slowing things down.
11521031
*/
11531032
#define MAX_PARALLEL 2
@@ -1160,12 +1039,12 @@
11601039
** The parent never returns from this procedure.
11611040
**
11621041
** Return 0 to each child as it runs. If unable to establish a
11631042
** listening socket, return non-zero.
11641043
*/
1165
-int cgi_http_server(int mnPort, int mxPort, char *zBrowser){
1166
-#ifdef __MINGW32__
1044
+int cgi_http_server(int mnPort, int mxPort, char *zBrowser, int flags){
1045
+#if defined(_WIN32)
11671046
/* Use win32_http_server() instead */
11681047
fossil_exit(1);
11691048
#else
11701049
int listener = -1; /* The server socket */
11711050
int connection; /* A socket for each individual connection */
@@ -1179,11 +1058,15 @@
11791058
int iPort = mnPort;
11801059
11811060
while( iPort<=mxPort ){
11821061
memset(&inaddr, 0, sizeof(inaddr));
11831062
inaddr.sin_family = AF_INET;
1184
- inaddr.sin_addr.s_addr = INADDR_ANY;
1063
+ if( flags & HTTP_SERVER_LOCALHOST ){
1064
+ inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1065
+ }else{
1066
+ inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
1067
+ }
11851068
inaddr.sin_port = htons(iPort);
11861069
listener = socket(AF_INET, SOCK_STREAM, 0);
11871070
if( listener<0 ){
11881071
iPort++;
11891072
continue;
@@ -1224,11 +1107,12 @@
12241107
}
12251108
delay.tv_sec = 60;
12261109
delay.tv_usec = 0;
12271110
FD_ZERO(&readfds);
12281111
FD_SET( listener, &readfds);
1229
- if( select( listener+1, &readfds, 0, 0, &delay) ){
1112
+ select( listener+1, &readfds, 0, 0, &delay);
1113
+ if( FD_ISSET(listener, &readfds) ){
12301114
lenaddr = sizeof(inaddr);
12311115
connection = accept(listener, (struct sockaddr*)&inaddr,
12321116
(socklen_t*) &lenaddr);
12331117
if( connection>=0 ){
12341118
child = fork();
12351119
--- src/cgi.c
+++ src/cgi.c
@@ -20,15 +20,16 @@
20 ** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
21 ** formatting function and its cousins, and routines to encode and
22 ** decode strings in HTML or HTTP.
23 */
24 #include "config.h"
25 #ifdef __MINGW32__
26 # include <windows.h> /* for Sleep once server works again */
27 # include <winsock2.h> /* socket operations */
28 # define sleep Sleep /* windows does not have sleep, but Sleep */
29 # include <ws2tcpip.h>
 
30 #else
31 # include <sys/socket.h>
32 # include <netinet/in.h>
33 # include <arpa/inet.h>
34 # include <sys/times.h>
@@ -851,145 +852,10 @@
851 cgi_printf("%s = %s <br />\n",
852 htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
853 }
854 }
855
856 /*
857 ** Write HTML text for an option menu to standard output. zParam
858 ** is the query parameter that the option menu sets. zDflt is the
859 ** initial value of the option menu. Addition arguments are name/value
860 ** pairs that define values on the menu. The list is terminated with
861 ** a single NULL argument.
862 */
863 void cgi_optionmenu(int in, const char *zP, const char *zD, ...){
864 va_list ap;
865 char *zName, *zVal;
866 int dfltSeen = 0;
867 cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
868 va_start(ap, zD);
869 while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
870 if( strcmp(zVal,zD)==0 ){ dfltSeen = 1; break; }
871 }
872 va_end(ap);
873 if( !dfltSeen ){
874 if( zD[0] ){
875 cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
876 in+2, "", zD, zD);
877 }else{
878 cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n", in+2, "");
879 }
880 }
881 va_start(ap, zD);
882 while( (zName = va_arg(ap, char*))!=0 && (zVal = va_arg(ap, char*))!=0 ){
883 if( zName[0] ){
884 cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
885 in+2, "",
886 zVal,
887 strcmp(zVal, zD) ? "" : " selected",
888 zName
889 );
890 }else{
891 cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
892 in+2, "",
893 strcmp(zVal, zD) ? "" : " selected"
894 );
895 }
896 }
897 va_end(ap);
898 cgi_printf("%*s</select>\n", in, "");
899 }
900
901 /*
902 ** This routine works a lot like cgi_optionmenu() except that the list of
903 ** values is contained in an array. Also, the values are just values, not
904 ** name/value pairs as in cgi_optionmenu.
905 */
906 void cgi_v_optionmenu(
907 int in, /* Indent by this amount */
908 const char *zP, /* The query parameter name */
909 const char *zD, /* Default value */
910 const char **az /* NULL-terminated list of allowed values */
911 ){
912 const char *zVal;
913 int i;
914 cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
915 for(i=0; az[i]; i++){
916 if( strcmp(az[i],zD)==0 ) break;
917 }
918 if( az[i]==0 ){
919 if( zD[0]==0 ){
920 cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
921 in+2, "");
922 }else{
923 cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
924 in+2, "", zD, zD);
925 }
926 }
927 while( (zVal = *(az++))!=0 ){
928 if( zVal[0] ){
929 cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
930 in+2, "",
931 zVal,
932 strcmp(zVal, zD) ? "" : " selected",
933 zVal
934 );
935 }else{
936 cgi_printf("%*s<option value=\"\"%s>&nbsp;</option>\n",
937 in+2, "",
938 strcmp(zVal, zD) ? "" : " selected"
939 );
940 }
941 }
942 cgi_printf("%*s</select>\n", in, "");
943 }
944
945 /*
946 ** This routine works a lot like cgi_v_optionmenu() except that the list
947 ** is a list of pairs. The first element of each pair is the value used
948 ** internally and the second element is the value displayed to the user.
949 */
950 void cgi_v_optionmenu2(
951 int in, /* Indent by this amount */
952 const char *zP, /* The query parameter name */
953 const char *zD, /* Default value */
954 const char **az /* NULL-terminated list of allowed values */
955 ){
956 const char *zVal;
957 int i;
958 cgi_printf("%*s<select size=1 name=\"%s\">\n", in, "", zP);
959 for(i=0; az[i]; i+=2){
960 if( strcmp(az[i],zD)==0 ) break;
961 }
962 if( az[i]==0 ){
963 if( zD[0]==0 ){
964 cgi_printf("%*s<option value=\"\" selected>&nbsp;</option>\n",
965 in+2, "");
966 }else{
967 cgi_printf("%*s<option value=\"%h\" selected>%h</option>\n",
968 in+2, "", zD, zD);
969 }
970 }
971 while( (zVal = *(az++))!=0 ){
972 const char *zName = *(az++);
973 if( zName[0] ){
974 cgi_printf("%*s<option value=\"%h\"%s>%h</option>\n",
975 in+2, "",
976 zVal,
977 strcmp(zVal, zD) ? "" : " selected",
978 zName
979 );
980 }else{
981 cgi_printf("%*s<option value=\"%h\"%s>&nbsp;</option>\n",
982 in+2, "",
983 zVal,
984 strcmp(zVal, zD) ? "" : " selected"
985 );
986 }
987 }
988 cgi_printf("%*s</select>\n", in, "");
989 }
990
991 /*
992 ** This routine works like "printf" except that it has the
993 ** extra formatting capabilities such as %h and %t.
994 */
995 void cgi_printf(const char *zFormat, ...){
@@ -1122,32 +988,45 @@
1122 while( isspace(*zVal) ){ zVal++; }
1123 i = strlen(zVal);
1124 while( i>0 && isspace(zVal[i-1]) ){ i--; }
1125 zVal[i] = 0;
1126 for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
1127 if( strcmp(zFieldName,"user-agent:")==0 ){
1128 cgi_setenv("HTTP_USER_AGENT", zVal);
1129 }else if( strcmp(zFieldName,"content-length:")==0 ){
1130 cgi_setenv("CONTENT_LENGTH", zVal);
1131 }else if( strcmp(zFieldName,"referer:")==0 ){
1132 cgi_setenv("HTTP_REFERER", zVal);
1133 }else if( strcmp(zFieldName,"host:")==0 ){
1134 cgi_setenv("HTTP_HOST", zVal);
1135 }else if( strcmp(zFieldName,"content-type:")==0 ){
1136 cgi_setenv("CONTENT_TYPE", zVal);
1137 }else if( strcmp(zFieldName,"cookie:")==0 ){
1138 cgi_setenv("HTTP_COOKIE", zVal);
 
 
 
 
1139 }else if( strcmp(zFieldName,"if-none-match:")==0 ){
1140 cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
1141 }else if( strcmp(zFieldName,"if-modified-since:")==0 ){
1142 cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
1143 }
 
 
 
 
 
 
 
1144 }
1145
1146 cgi_init();
1147 }
1148
 
 
 
 
 
 
 
 
1149 /*
1150 ** Maximum number of child processes that we can have running
1151 ** at one time before we start slowing things down.
1152 */
1153 #define MAX_PARALLEL 2
@@ -1160,12 +1039,12 @@
1160 ** The parent never returns from this procedure.
1161 **
1162 ** Return 0 to each child as it runs. If unable to establish a
1163 ** listening socket, return non-zero.
1164 */
1165 int cgi_http_server(int mnPort, int mxPort, char *zBrowser){
1166 #ifdef __MINGW32__
1167 /* Use win32_http_server() instead */
1168 fossil_exit(1);
1169 #else
1170 int listener = -1; /* The server socket */
1171 int connection; /* A socket for each individual connection */
@@ -1179,11 +1058,15 @@
1179 int iPort = mnPort;
1180
1181 while( iPort<=mxPort ){
1182 memset(&inaddr, 0, sizeof(inaddr));
1183 inaddr.sin_family = AF_INET;
1184 inaddr.sin_addr.s_addr = INADDR_ANY;
 
 
 
 
1185 inaddr.sin_port = htons(iPort);
1186 listener = socket(AF_INET, SOCK_STREAM, 0);
1187 if( listener<0 ){
1188 iPort++;
1189 continue;
@@ -1224,11 +1107,12 @@
1224 }
1225 delay.tv_sec = 60;
1226 delay.tv_usec = 0;
1227 FD_ZERO(&readfds);
1228 FD_SET( listener, &readfds);
1229 if( select( listener+1, &readfds, 0, 0, &delay) ){
 
1230 lenaddr = sizeof(inaddr);
1231 connection = accept(listener, (struct sockaddr*)&inaddr,
1232 (socklen_t*) &lenaddr);
1233 if( connection>=0 ){
1234 child = fork();
1235
--- src/cgi.c
+++ src/cgi.c
@@ -20,15 +20,16 @@
20 ** dispensing QUERY_STRING parameters and cookies, the "mprintf()"
21 ** formatting function and its cousins, and routines to encode and
22 ** decode strings in HTML or HTTP.
23 */
24 #include "config.h"
25 #ifdef _WIN32
26 # include <windows.h> /* for Sleep once server works again */
27 # if defined(__MINGW32__)
28 # define sleep Sleep /* windows does not have sleep, but Sleep */
29 # include <ws2tcpip.h>
30 # endif
31 #else
32 # include <sys/socket.h>
33 # include <netinet/in.h>
34 # include <arpa/inet.h>
35 # include <sys/times.h>
@@ -851,145 +852,10 @@
852 cgi_printf("%s = %s <br />\n",
853 htmlize(aParamQP[i].zName, -1), htmlize(aParamQP[i].zValue, -1));
854 }
855 }
856
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857 /*
858 ** This routine works like "printf" except that it has the
859 ** extra formatting capabilities such as %h and %t.
860 */
861 void cgi_printf(const char *zFormat, ...){
@@ -1122,32 +988,45 @@
988 while( isspace(*zVal) ){ zVal++; }
989 i = strlen(zVal);
990 while( i>0 && isspace(zVal[i-1]) ){ i--; }
991 zVal[i] = 0;
992 for(i=0; zFieldName[i]; i++){ zFieldName[i] = tolower(zFieldName[i]); }
993 if( strcmp(zFieldName,"content-length:")==0 ){
 
 
994 cgi_setenv("CONTENT_LENGTH", zVal);
 
 
 
 
995 }else if( strcmp(zFieldName,"content-type:")==0 ){
996 cgi_setenv("CONTENT_TYPE", zVal);
997 }else if( strcmp(zFieldName,"cookie:")==0 ){
998 cgi_setenv("HTTP_COOKIE", zVal);
999 }else if( strcmp(zFieldName,"https:")==0 ){
1000 cgi_setenv("HTTPS", zVal);
1001 }else if( strcmp(zFieldName,"host:")==0 ){
1002 cgi_setenv("HTTP_HOST", zVal);
1003 }else if( strcmp(zFieldName,"if-none-match:")==0 ){
1004 cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
1005 }else if( strcmp(zFieldName,"if-modified-since:")==0 ){
1006 cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
1007 }
1008 #if 0
1009 else if( strcmp(zFieldName,"referer:")==0 ){
1010 cgi_setenv("HTTP_REFERER", zVal);
1011 }else if( strcmp(zFieldName,"user-agent:")==0 ){
1012 cgi_setenv("HTTP_USER_AGENT", zVal);
1013 }
1014 #endif
1015 }
1016
1017 cgi_init();
1018 }
1019
1020 #if INTERFACE
1021 /*
1022 ** Bitmap values for the flags parameter to cgi_http_server().
1023 */
1024 #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */
1025
1026 #endif /* INTERFACE */
1027
1028 /*
1029 ** Maximum number of child processes that we can have running
1030 ** at one time before we start slowing things down.
1031 */
1032 #define MAX_PARALLEL 2
@@ -1160,12 +1039,12 @@
1039 ** The parent never returns from this procedure.
1040 **
1041 ** Return 0 to each child as it runs. If unable to establish a
1042 ** listening socket, return non-zero.
1043 */
1044 int cgi_http_server(int mnPort, int mxPort, char *zBrowser, int flags){
1045 #if defined(_WIN32)
1046 /* Use win32_http_server() instead */
1047 fossil_exit(1);
1048 #else
1049 int listener = -1; /* The server socket */
1050 int connection; /* A socket for each individual connection */
@@ -1179,11 +1058,15 @@
1058 int iPort = mnPort;
1059
1060 while( iPort<=mxPort ){
1061 memset(&inaddr, 0, sizeof(inaddr));
1062 inaddr.sin_family = AF_INET;
1063 if( flags & HTTP_SERVER_LOCALHOST ){
1064 inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1065 }else{
1066 inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
1067 }
1068 inaddr.sin_port = htons(iPort);
1069 listener = socket(AF_INET, SOCK_STREAM, 0);
1070 if( listener<0 ){
1071 iPort++;
1072 continue;
@@ -1224,11 +1107,12 @@
1107 }
1108 delay.tv_sec = 60;
1109 delay.tv_usec = 0;
1110 FD_ZERO(&readfds);
1111 FD_SET( listener, &readfds);
1112 select( listener+1, &readfds, 0, 0, &delay);
1113 if( FD_ISSET(listener, &readfds) ){
1114 lenaddr = sizeof(inaddr);
1115 connection = accept(listener, (struct sockaddr*)&inaddr,
1116 (socklen_t*) &lenaddr);
1117 if( connection>=0 ){
1118 child = fork();
1119
+40 -22
--- src/checkin.c
+++ src/checkin.c
@@ -401,19 +401,19 @@
401401
}
402402
if( zEditor==0 ){
403403
zEditor = getenv("EDITOR");
404404
}
405405
if( zEditor==0 ){
406
-#ifdef __MINGW32__
406
+#if defined(_WIN32)
407407
zEditor = "notepad";
408408
#else
409409
zEditor = "ed";
410410
#endif
411411
}
412412
zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
413413
g.zLocalRoot);
414
-#ifdef __MINGW32__
414
+#if defined(_WIN32)
415415
blob_add_cr(&text);
416416
#endif
417417
blob_write_to_file(&text, zFile);
418418
zCmd = mprintf("%s \"%s\"", zEditor, zFile);
419419
printf("%s\n", zCmd);
@@ -513,15 +513,32 @@
513513
" WHERE datetime(mtime)>=%Q"
514514
" AND type='ci' AND objid=%d",
515515
zDate, rid
516516
);
517517
if( b ){
518
- fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)",
519
- zUuid, zDate);
518
+ fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)"
519
+ " Use -f to override.", zUuid, zDate);
520520
}
521521
#endif
522522
}
523
+
524
+/*
525
+** zDate should be a valid date string. Convert this string into the
526
+** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
527
+** print a fatal error and quit.
528
+*/
529
+char *date_in_standard_format(const char *zInputDate){
530
+ char *zDate = db_text(0, "SELECT datetime(%Q)", zInputDate);
531
+ if( zDate[0]==0 ){
532
+ fossil_fatal("unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS\"",
533
+ zInputDate);
534
+ }
535
+ assert( strlen(zDate)==19 );
536
+ assert( zDate[10]==' ' );
537
+ zDate[10] = 'T';
538
+ return zDate;
539
+}
523540
524541
/*
525542
** COMMAND: ci
526543
** COMMAND: commit
527544
**
@@ -739,12 +756,11 @@
739756
blob_zero(&manifest);
740757
if( blob_size(&comment)==0 ){
741758
blob_append(&comment, "(no comment)", -1);
742759
}
743760
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
744
- zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
745
- zDate[10] = 'T';
761
+ zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
746762
blob_appendf(&manifest, "D %s\n", zDate);
747763
zDate[10] = ' ';
748764
db_prepare(&q,
749765
"SELECT pathname, uuid, origname, blob.rid, isexe"
750766
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
@@ -759,11 +775,11 @@
759775
const char *zOrig = db_column_text(&q, 2);
760776
int frid = db_column_int(&q, 3);
761777
int isexe = db_column_int(&q, 4);
762778
const char *zPerm;
763779
blob_append(&filename, zName, -1);
764
-#ifndef __MINGW32__
780
+#if !defined(_WIN32)
765781
/* For unix, extract the "executable" permission bit directly from
766782
** the filesystem. On windows, the "executable" bit is retained
767783
** unchanged from the original. */
768784
isexe = file_isexe(blob_str(&filename));
769785
#endif
@@ -783,25 +799,27 @@
783799
}
784800
blob_reset(&filename);
785801
db_finalize(&q);
786802
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
787803
blob_appendf(&manifest, "P %s", zUuid);
788
- checkin_verify_younger(vid, zUuid, zDate);
789
-
790
- db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
791
- db_bind_int(&q2, ":id", 0);
792
- while( db_step(&q2)==SQLITE_ROW ){
793
- int mid = db_column_int(&q2, 0);
794
- if( !g.markPrivate && content_is_private(mid) ) continue;
795
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
796
- if( zUuid ){
797
- blob_appendf(&manifest, " %s", zUuid);
798
- checkin_verify_younger(mid, zUuid, zDate);
799
- free(zUuid);
800
- }
801
- }
802
- db_reset(&q2);
804
+
805
+ if( !forceFlag ){
806
+ checkin_verify_younger(vid, zUuid, zDate);
807
+ db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
808
+ db_bind_int(&q2, ":id", 0);
809
+ while( db_step(&q2)==SQLITE_ROW ){
810
+ int mid = db_column_int(&q2, 0);
811
+ if( !g.markPrivate && content_is_private(mid) ) continue;
812
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
813
+ if( zUuid ){
814
+ blob_appendf(&manifest, " %s", zUuid);
815
+ checkin_verify_younger(mid, zUuid, zDate);
816
+ free(zUuid);
817
+ }
818
+ }
819
+ db_finalize(&q2);
820
+ }
803821
804822
blob_appendf(&manifest, "\n");
805823
blob_appendf(&manifest, "R %b\n", &cksum1);
806824
if( zBranch && zBranch[0] ){
807825
Stmt q;
808826
--- src/checkin.c
+++ src/checkin.c
@@ -401,19 +401,19 @@
401 }
402 if( zEditor==0 ){
403 zEditor = getenv("EDITOR");
404 }
405 if( zEditor==0 ){
406 #ifdef __MINGW32__
407 zEditor = "notepad";
408 #else
409 zEditor = "ed";
410 #endif
411 }
412 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
413 g.zLocalRoot);
414 #ifdef __MINGW32__
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);
@@ -513,15 +513,32 @@
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 zUuid, zDate);
520 }
521 #endif
522 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
524 /*
525 ** COMMAND: ci
526 ** COMMAND: commit
527 **
@@ -739,12 +756,11 @@
739 blob_zero(&manifest);
740 if( blob_size(&comment)==0 ){
741 blob_append(&comment, "(no comment)", -1);
742 }
743 blob_appendf(&manifest, "C %F\n", blob_str(&comment));
744 zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now");
745 zDate[10] = 'T';
746 blob_appendf(&manifest, "D %s\n", zDate);
747 zDate[10] = ' ';
748 db_prepare(&q,
749 "SELECT pathname, uuid, origname, blob.rid, isexe"
750 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
@@ -759,11 +775,11 @@
759 const char *zOrig = db_column_text(&q, 2);
760 int frid = db_column_int(&q, 3);
761 int isexe = db_column_int(&q, 4);
762 const char *zPerm;
763 blob_append(&filename, zName, -1);
764 #ifndef __MINGW32__
765 /* For unix, extract the "executable" permission bit directly from
766 ** the filesystem. On windows, the "executable" bit is retained
767 ** unchanged from the original. */
768 isexe = file_isexe(blob_str(&filename));
769 #endif
@@ -783,25 +799,27 @@
783 }
784 blob_reset(&filename);
785 db_finalize(&q);
786 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
787 blob_appendf(&manifest, "P %s", zUuid);
788 checkin_verify_younger(vid, zUuid, zDate);
789
790 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
791 db_bind_int(&q2, ":id", 0);
792 while( db_step(&q2)==SQLITE_ROW ){
793 int mid = db_column_int(&q2, 0);
794 if( !g.markPrivate && content_is_private(mid) ) continue;
795 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
796 if( zUuid ){
797 blob_appendf(&manifest, " %s", zUuid);
798 checkin_verify_younger(mid, zUuid, zDate);
799 free(zUuid);
800 }
801 }
802 db_reset(&q2);
 
 
803
804 blob_appendf(&manifest, "\n");
805 blob_appendf(&manifest, "R %b\n", &cksum1);
806 if( zBranch && zBranch[0] ){
807 Stmt q;
808
--- src/checkin.c
+++ src/checkin.c
@@ -401,19 +401,19 @@
401 }
402 if( zEditor==0 ){
403 zEditor = getenv("EDITOR");
404 }
405 if( zEditor==0 ){
406 #if defined(_WIN32)
407 zEditor = "notepad";
408 #else
409 zEditor = "ed";
410 #endif
411 }
412 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
413 g.zLocalRoot);
414 #if defined(_WIN32)
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);
@@ -513,15 +513,32 @@
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
524 /*
525 ** zDate should be a valid date string. Convert this string into the
526 ** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
527 ** print a fatal error and quit.
528 */
529 char *date_in_standard_format(const char *zInputDate){
530 char *zDate = db_text(0, "SELECT datetime(%Q)", zInputDate);
531 if( zDate[0]==0 ){
532 fossil_fatal("unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS\"",
533 zInputDate);
534 }
535 assert( strlen(zDate)==19 );
536 assert( zDate[10]==' ' );
537 zDate[10] = 'T';
538 return zDate;
539 }
540
541 /*
542 ** COMMAND: ci
543 ** COMMAND: commit
544 **
@@ -739,12 +756,11 @@
756 blob_zero(&manifest);
757 if( blob_size(&comment)==0 ){
758 blob_append(&comment, "(no comment)", -1);
759 }
760 blob_appendf(&manifest, "C %F\n", blob_str(&comment));
761 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
 
762 blob_appendf(&manifest, "D %s\n", zDate);
763 zDate[10] = ' ';
764 db_prepare(&q,
765 "SELECT pathname, uuid, origname, blob.rid, isexe"
766 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
@@ -759,11 +775,11 @@
775 const char *zOrig = db_column_text(&q, 2);
776 int frid = db_column_int(&q, 3);
777 int isexe = db_column_int(&q, 4);
778 const char *zPerm;
779 blob_append(&filename, zName, -1);
780 #if !defined(_WIN32)
781 /* For unix, extract the "executable" permission bit directly from
782 ** the filesystem. On windows, the "executable" bit is retained
783 ** unchanged from the original. */
784 isexe = file_isexe(blob_str(&filename));
785 #endif
@@ -783,25 +799,27 @@
799 }
800 blob_reset(&filename);
801 db_finalize(&q);
802 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
803 blob_appendf(&manifest, "P %s", zUuid);
804
805 if( !forceFlag ){
806 checkin_verify_younger(vid, zUuid, zDate);
807 db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
808 db_bind_int(&q2, ":id", 0);
809 while( db_step(&q2)==SQLITE_ROW ){
810 int mid = db_column_int(&q2, 0);
811 if( !g.markPrivate && content_is_private(mid) ) continue;
812 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
813 if( zUuid ){
814 blob_appendf(&manifest, " %s", zUuid);
815 checkin_verify_younger(mid, zUuid, zDate);
816 free(zUuid);
817 }
818 }
819 db_finalize(&q2);
820 }
821
822 blob_appendf(&manifest, "\n");
823 blob_appendf(&manifest, "R %b\n", &cksum1);
824 if( zBranch && zBranch[0] ){
825 Stmt q;
826
+9 -2
--- src/config.h
+++ src/config.h
@@ -26,13 +26,20 @@
2626
#include <stdlib.h>
2727
#include <ctype.h>
2828
#include <string.h>
2929
#include <stdarg.h>
3030
#include <assert.h>
31
-#ifdef __MINGW32__
32
-# include <windows.h>
31
+#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER)
32
+# if defined(__DMC__) || defined(_MSC_VER)
33
+ typedef int socklen_t;
34
+# endif
35
+# ifndef _WIN32
36
+# define _WIN32
37
+# endif
3338
#else
39
+# include <sys/types.h>
40
+# include <signal.h>
3441
# include <pwd.h>
3542
#endif
3643
3744
#include "sqlite3.h"
3845
3946
--- src/config.h
+++ src/config.h
@@ -26,13 +26,20 @@
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <assert.h>
31 #ifdef __MINGW32__
32 # include <windows.h>
 
 
 
 
 
33 #else
 
 
34 # include <pwd.h>
35 #endif
36
37 #include "sqlite3.h"
38
39
--- src/config.h
+++ src/config.h
@@ -26,13 +26,20 @@
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <assert.h>
31 #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER)
32 # if defined(__DMC__) || defined(_MSC_VER)
33 typedef int socklen_t;
34 # endif
35 # ifndef _WIN32
36 # define _WIN32
37 # endif
38 #else
39 # include <sys/types.h>
40 # include <signal.h>
41 # include <pwd.h>
42 #endif
43
44 #include "sqlite3.h"
45
46
--- src/configure.c
+++ src/configure.c
@@ -459,10 +459,11 @@
459459
zPw = db_get("last-sync-pw", 0);
460460
}
461461
url_parse(zServer);
462462
if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
463463
user_select();
464
+ url_enable_proxy("via proxy: ");
464465
if( strncmp(zMethod, "push", n)==0 ){
465466
client_sync(0,0,0,0,mask);
466467
}else{
467468
client_sync(0,0,0,mask,0);
468469
}
469470
--- src/configure.c
+++ src/configure.c
@@ -459,10 +459,11 @@
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 if( strncmp(zMethod, "push", n)==0 ){
465 client_sync(0,0,0,0,mask);
466 }else{
467 client_sync(0,0,0,mask,0);
468 }
469
--- src/configure.c
+++ src/configure.c
@@ -459,10 +459,11 @@
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 if( strncmp(zMethod, "push", n)==0 ){
466 client_sync(0,0,0,0,mask);
467 }else{
468 client_sync(0,0,0,mask,0);
469 }
470
+28 -13
--- src/db.c
+++ src/db.c
@@ -27,10 +27,13 @@
2727
** (3) A local checkout database named "_FOSSIL_" or ".fos"
2828
** and located at the root of the local copy of the source tree.
2929
**
3030
*/
3131
#include "config.h"
32
+#if ! defined(_WIN32)
33
+# include <pwd.h>
34
+#endif
3235
#include <sqlite3.h>
3336
#include <sys/types.h>
3437
#include <sys/stat.h>
3538
#include <unistd.h>
3639
#include "db.h"
@@ -134,16 +137,16 @@
134137
if( busy || g.db==0 ) return;
135138
busy = 1;
136139
undo_rollback();
137140
if( nBegin ){
138141
sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
142
+ nBegin = 0;
139143
if( isNewRepo ){
140144
db_close();
141145
unlink(g.zRepositoryName);
142146
}
143147
}
144
- nBegin = 0;
145148
busy = 0;
146149
}
147150
148151
/*
149152
** Install a commit hook. Hooks are installed in sequence order.
@@ -534,11 +537,11 @@
534537
}
535538
db_finalize(&s);
536539
return z;
537540
}
538541
539
-#ifdef __MINGW32__
542
+#if defined(_WIN32)
540543
extern char *sqlite3_win32_mbcs_to_utf8(const char*);
541544
#endif
542545
543546
/*
544547
** Initialize a new database file with the given schema. If anything
@@ -552,11 +555,11 @@
552555
sqlite3 *db;
553556
int rc;
554557
const char *zSql;
555558
va_list ap;
556559
557
-#ifdef __MINGW32__
560
+#if defined(_WIN32)
558561
zFileName = sqlite3_win32_mbcs_to_utf8(zFileName);
559562
#endif
560563
rc = sqlite3_open(zFileName, &db);
561564
if( rc!=SQLITE_OK ){
562565
db_err(sqlite3_errmsg(db));
@@ -587,11 +590,11 @@
587590
int rc;
588591
const char *zVfs;
589592
sqlite3 *db;
590593
591594
zVfs = getenv("FOSSIL_VFS");
592
-#ifdef __MINGW32__
595
+#if defined(_WIN32)
593596
zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
594597
#endif
595598
rc = sqlite3_open_v2(
596599
zDbName, &db,
597600
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
@@ -615,11 +618,11 @@
615618
if( !g.db ){
616619
g.db = openDatabase(zDbName);
617620
g.zRepoDb = "main";
618621
db_connection_init();
619622
}else{
620
-#ifdef __MINGW32__
623
+#if defined(_WIN32)
621624
zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
622625
#endif
623626
db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
624627
g.zRepoDb = mprintf("%s", zLabel);
625628
}
@@ -639,11 +642,11 @@
639642
*/
640643
void db_open_config(int useAttach){
641644
char *zDbName;
642645
const char *zHome;
643646
if( g.configOpen ) return;
644
-#ifdef __MINGW32__
647
+#if defined(_WIN32)
645648
zHome = getenv("LOCALAPPDATA");
646649
if( zHome==0 ){
647650
zHome = getenv("APPDATA");
648651
if( zHome==0 ){
649652
zHome = getenv("HOMEPATH");
@@ -661,17 +664,17 @@
661664
}
662665
#endif
663666
if( file_isdir(zHome)!=1 ){
664667
fossil_fatal("invalid home directory: %s", zHome);
665668
}
666
-#ifndef __MINGW32__
669
+#ifndef _WIN32
667670
if( access(zHome, W_OK) ){
668671
fossil_fatal("home directory %s must be writeable", zHome);
669672
}
670673
#endif
671674
g.zHome = mprintf("%/", zHome);
672
-#ifdef __MINGW32__
675
+#if defined(_WIN32)
673676
/* . filenames give some window systems problems and many apps problems */
674677
zDbName = mprintf("%//_fossil", zHome);
675678
#else
676679
zDbName = mprintf("%s/.fossil", zHome);
677680
#endif
@@ -894,20 +897,30 @@
894897
895898
/*
896899
** Close the database connection.
897900
*/
898901
void db_close(void){
902
+ sqlite3_stmt *pStmt;
899903
if( g.db==0 ) return;
900904
while( pAllStmt ){
901905
db_finalize(pAllStmt);
902906
}
903907
db_end_transaction(1);
908
+ pStmt = 0;
909
+ while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
910
+ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
911
+ }
904912
g.repositoryOpen = 0;
905913
g.localOpen = 0;
906914
g.configOpen = 0;
915
+ sqlite3_wal_checkpoint(g.db, 0);
907916
sqlite3_close(g.db);
908917
g.db = 0;
918
+ if( g.dbConfig ){
919
+ sqlite3_close(g.dbConfig);
920
+ g.dbConfig = 0;
921
+ }
909922
}
910923
911924
912925
/*
913926
** Create a new empty repository database with the given name.
@@ -934,11 +947,11 @@
934947
zUser = db_get("default-user", 0);
935948
if( zUser==0 ){
936949
zUser = zDefaultUser;
937950
}
938951
if( zUser==0 ){
939
-#ifdef __MINGW32__
952
+#if defined(_WIN32)
940953
zUser = getenv("USERNAME");
941954
#else
942955
zUser = getenv("USER");
943956
#endif
944957
}
@@ -1000,12 +1013,11 @@
10001013
10011014
if( zInitialDate ){
10021015
int rid;
10031016
blob_zero(&manifest);
10041017
blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
1005
- zDate = db_text(0, "SELECT datetime(%Q)", zInitialDate);
1006
- zDate[10]='T';
1018
+ zDate = date_in_standard_format(zInitialDate);
10071019
blob_appendf(&manifest, "D %s\n", zDate);
10081020
blob_appendf(&manifest, "P\n");
10091021
md5sum_init();
10101022
blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
10111023
blob_appendf(&manifest, "T *branch * trunk\n");
@@ -1253,11 +1265,10 @@
12531265
** so this routine is a no-op.
12541266
*/
12551267
void db_swap_connections(void){
12561268
if( !g.useAttach ){
12571269
sqlite3 *dbTemp = g.db;
1258
- assert( g.dbConfig!=0 );
12591270
g.db = g.dbConfig;
12601271
g.dbConfig = dbTemp;
12611272
}
12621273
}
12631274
@@ -1374,11 +1385,11 @@
13741385
db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
13751386
}
13761387
13771388
/*
13781389
** Record the name of a local repository in the global_config() database.
1379
-** The repostiroy filename %s is recorded as an entry with a "name" field
1390
+** The repository filename %s is recorded as an entry with a "name" field
13801391
** of the following form:
13811392
**
13821393
** repo:%s
13831394
**
13841395
** The value field is set to 1.
@@ -1546,10 +1557,13 @@
15461557
**
15471558
** proxy URL of the HTTP proxy. If undefined or "off" then
15481559
** the "http_proxy" environment variable is consulted.
15491560
** If the http_proxy environment variable is undefined
15501561
** then a direct HTTP connection is used.
1562
+**
1563
+** ssh-command Command used to talk to a remote machine with
1564
+** the "ssh://" protocol.
15511565
**
15521566
** web-browser A shell command used to launch your preferred
15531567
** web browser when given a URL as an argument.
15541568
** Defaults to "start" on windows, "open" on Mac,
15551569
** and "firefox" on Unix.
@@ -1569,10 +1583,11 @@
15691583
"http-port",
15701584
"localauth",
15711585
"mtime-changes",
15721586
"pgp-command",
15731587
"proxy",
1588
+ "ssh-command",
15741589
"web-browser",
15751590
};
15761591
int i;
15771592
int globalFlag = find_option("global","g",0)!=0;
15781593
int unsetFlag = g.argv[1][0]=='u';
15791594
--- src/db.c
+++ src/db.c
@@ -27,10 +27,13 @@
27 ** (3) A local checkout database named "_FOSSIL_" or ".fos"
28 ** and located at the root of the local copy of the source tree.
29 **
30 */
31 #include "config.h"
 
 
 
32 #include <sqlite3.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include "db.h"
@@ -134,16 +137,16 @@
134 if( busy || g.db==0 ) return;
135 busy = 1;
136 undo_rollback();
137 if( nBegin ){
138 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
 
139 if( isNewRepo ){
140 db_close();
141 unlink(g.zRepositoryName);
142 }
143 }
144 nBegin = 0;
145 busy = 0;
146 }
147
148 /*
149 ** Install a commit hook. Hooks are installed in sequence order.
@@ -534,11 +537,11 @@
534 }
535 db_finalize(&s);
536 return z;
537 }
538
539 #ifdef __MINGW32__
540 extern char *sqlite3_win32_mbcs_to_utf8(const char*);
541 #endif
542
543 /*
544 ** Initialize a new database file with the given schema. If anything
@@ -552,11 +555,11 @@
552 sqlite3 *db;
553 int rc;
554 const char *zSql;
555 va_list ap;
556
557 #ifdef __MINGW32__
558 zFileName = sqlite3_win32_mbcs_to_utf8(zFileName);
559 #endif
560 rc = sqlite3_open(zFileName, &db);
561 if( rc!=SQLITE_OK ){
562 db_err(sqlite3_errmsg(db));
@@ -587,11 +590,11 @@
587 int rc;
588 const char *zVfs;
589 sqlite3 *db;
590
591 zVfs = getenv("FOSSIL_VFS");
592 #ifdef __MINGW32__
593 zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
594 #endif
595 rc = sqlite3_open_v2(
596 zDbName, &db,
597 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
@@ -615,11 +618,11 @@
615 if( !g.db ){
616 g.db = openDatabase(zDbName);
617 g.zRepoDb = "main";
618 db_connection_init();
619 }else{
620 #ifdef __MINGW32__
621 zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
622 #endif
623 db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
624 g.zRepoDb = mprintf("%s", zLabel);
625 }
@@ -639,11 +642,11 @@
639 */
640 void db_open_config(int useAttach){
641 char *zDbName;
642 const char *zHome;
643 if( g.configOpen ) return;
644 #ifdef __MINGW32__
645 zHome = getenv("LOCALAPPDATA");
646 if( zHome==0 ){
647 zHome = getenv("APPDATA");
648 if( zHome==0 ){
649 zHome = getenv("HOMEPATH");
@@ -661,17 +664,17 @@
661 }
662 #endif
663 if( file_isdir(zHome)!=1 ){
664 fossil_fatal("invalid home directory: %s", zHome);
665 }
666 #ifndef __MINGW32__
667 if( access(zHome, W_OK) ){
668 fossil_fatal("home directory %s must be writeable", zHome);
669 }
670 #endif
671 g.zHome = mprintf("%/", zHome);
672 #ifdef __MINGW32__
673 /* . filenames give some window systems problems and many apps problems */
674 zDbName = mprintf("%//_fossil", zHome);
675 #else
676 zDbName = mprintf("%s/.fossil", zHome);
677 #endif
@@ -894,20 +897,30 @@
894
895 /*
896 ** Close the database connection.
897 */
898 void db_close(void){
 
899 if( g.db==0 ) return;
900 while( pAllStmt ){
901 db_finalize(pAllStmt);
902 }
903 db_end_transaction(1);
 
 
 
 
904 g.repositoryOpen = 0;
905 g.localOpen = 0;
906 g.configOpen = 0;
 
907 sqlite3_close(g.db);
908 g.db = 0;
 
 
 
 
909 }
910
911
912 /*
913 ** Create a new empty repository database with the given name.
@@ -934,11 +947,11 @@
934 zUser = db_get("default-user", 0);
935 if( zUser==0 ){
936 zUser = zDefaultUser;
937 }
938 if( zUser==0 ){
939 #ifdef __MINGW32__
940 zUser = getenv("USERNAME");
941 #else
942 zUser = getenv("USER");
943 #endif
944 }
@@ -1000,12 +1013,11 @@
1000
1001 if( zInitialDate ){
1002 int rid;
1003 blob_zero(&manifest);
1004 blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
1005 zDate = db_text(0, "SELECT datetime(%Q)", zInitialDate);
1006 zDate[10]='T';
1007 blob_appendf(&manifest, "D %s\n", zDate);
1008 blob_appendf(&manifest, "P\n");
1009 md5sum_init();
1010 blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
1011 blob_appendf(&manifest, "T *branch * trunk\n");
@@ -1253,11 +1265,10 @@
1253 ** so this routine is a no-op.
1254 */
1255 void db_swap_connections(void){
1256 if( !g.useAttach ){
1257 sqlite3 *dbTemp = g.db;
1258 assert( g.dbConfig!=0 );
1259 g.db = g.dbConfig;
1260 g.dbConfig = dbTemp;
1261 }
1262 }
1263
@@ -1374,11 +1385,11 @@
1374 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
1375 }
1376
1377 /*
1378 ** Record the name of a local repository in the global_config() database.
1379 ** The repostiroy filename %s is recorded as an entry with a "name" field
1380 ** of the following form:
1381 **
1382 ** repo:%s
1383 **
1384 ** The value field is set to 1.
@@ -1546,10 +1557,13 @@
1546 **
1547 ** proxy URL of the HTTP proxy. If undefined or "off" then
1548 ** the "http_proxy" environment variable is consulted.
1549 ** If the http_proxy environment variable is undefined
1550 ** then a direct HTTP connection is used.
 
 
 
1551 **
1552 ** web-browser A shell command used to launch your preferred
1553 ** web browser when given a URL as an argument.
1554 ** Defaults to "start" on windows, "open" on Mac,
1555 ** and "firefox" on Unix.
@@ -1569,10 +1583,11 @@
1569 "http-port",
1570 "localauth",
1571 "mtime-changes",
1572 "pgp-command",
1573 "proxy",
 
1574 "web-browser",
1575 };
1576 int i;
1577 int globalFlag = find_option("global","g",0)!=0;
1578 int unsetFlag = g.argv[1][0]=='u';
1579
--- src/db.c
+++ src/db.c
@@ -27,10 +27,13 @@
27 ** (3) A local checkout database named "_FOSSIL_" or ".fos"
28 ** and located at the root of the local copy of the source tree.
29 **
30 */
31 #include "config.h"
32 #if ! defined(_WIN32)
33 # include <pwd.h>
34 #endif
35 #include <sqlite3.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include "db.h"
@@ -134,16 +137,16 @@
137 if( busy || g.db==0 ) return;
138 busy = 1;
139 undo_rollback();
140 if( nBegin ){
141 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
142 nBegin = 0;
143 if( isNewRepo ){
144 db_close();
145 unlink(g.zRepositoryName);
146 }
147 }
 
148 busy = 0;
149 }
150
151 /*
152 ** Install a commit hook. Hooks are installed in sequence order.
@@ -534,11 +537,11 @@
537 }
538 db_finalize(&s);
539 return z;
540 }
541
542 #if defined(_WIN32)
543 extern char *sqlite3_win32_mbcs_to_utf8(const char*);
544 #endif
545
546 /*
547 ** Initialize a new database file with the given schema. If anything
@@ -552,11 +555,11 @@
555 sqlite3 *db;
556 int rc;
557 const char *zSql;
558 va_list ap;
559
560 #if defined(_WIN32)
561 zFileName = sqlite3_win32_mbcs_to_utf8(zFileName);
562 #endif
563 rc = sqlite3_open(zFileName, &db);
564 if( rc!=SQLITE_OK ){
565 db_err(sqlite3_errmsg(db));
@@ -587,11 +590,11 @@
590 int rc;
591 const char *zVfs;
592 sqlite3 *db;
593
594 zVfs = getenv("FOSSIL_VFS");
595 #if defined(_WIN32)
596 zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
597 #endif
598 rc = sqlite3_open_v2(
599 zDbName, &db,
600 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
@@ -615,11 +618,11 @@
618 if( !g.db ){
619 g.db = openDatabase(zDbName);
620 g.zRepoDb = "main";
621 db_connection_init();
622 }else{
623 #if defined(_WIN32)
624 zDbName = sqlite3_win32_mbcs_to_utf8(zDbName);
625 #endif
626 db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
627 g.zRepoDb = mprintf("%s", zLabel);
628 }
@@ -639,11 +642,11 @@
642 */
643 void db_open_config(int useAttach){
644 char *zDbName;
645 const char *zHome;
646 if( g.configOpen ) return;
647 #if defined(_WIN32)
648 zHome = getenv("LOCALAPPDATA");
649 if( zHome==0 ){
650 zHome = getenv("APPDATA");
651 if( zHome==0 ){
652 zHome = getenv("HOMEPATH");
@@ -661,17 +664,17 @@
664 }
665 #endif
666 if( file_isdir(zHome)!=1 ){
667 fossil_fatal("invalid home directory: %s", zHome);
668 }
669 #ifndef _WIN32
670 if( access(zHome, W_OK) ){
671 fossil_fatal("home directory %s must be writeable", zHome);
672 }
673 #endif
674 g.zHome = mprintf("%/", zHome);
675 #if defined(_WIN32)
676 /* . filenames give some window systems problems and many apps problems */
677 zDbName = mprintf("%//_fossil", zHome);
678 #else
679 zDbName = mprintf("%s/.fossil", zHome);
680 #endif
@@ -894,20 +897,30 @@
897
898 /*
899 ** Close the database connection.
900 */
901 void db_close(void){
902 sqlite3_stmt *pStmt;
903 if( g.db==0 ) return;
904 while( pAllStmt ){
905 db_finalize(pAllStmt);
906 }
907 db_end_transaction(1);
908 pStmt = 0;
909 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
910 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
911 }
912 g.repositoryOpen = 0;
913 g.localOpen = 0;
914 g.configOpen = 0;
915 sqlite3_wal_checkpoint(g.db, 0);
916 sqlite3_close(g.db);
917 g.db = 0;
918 if( g.dbConfig ){
919 sqlite3_close(g.dbConfig);
920 g.dbConfig = 0;
921 }
922 }
923
924
925 /*
926 ** Create a new empty repository database with the given name.
@@ -934,11 +947,11 @@
947 zUser = db_get("default-user", 0);
948 if( zUser==0 ){
949 zUser = zDefaultUser;
950 }
951 if( zUser==0 ){
952 #if defined(_WIN32)
953 zUser = getenv("USERNAME");
954 #else
955 zUser = getenv("USER");
956 #endif
957 }
@@ -1000,12 +1013,11 @@
1013
1014 if( zInitialDate ){
1015 int rid;
1016 blob_zero(&manifest);
1017 blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
1018 zDate = date_in_standard_format(zInitialDate);
 
1019 blob_appendf(&manifest, "D %s\n", zDate);
1020 blob_appendf(&manifest, "P\n");
1021 md5sum_init();
1022 blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
1023 blob_appendf(&manifest, "T *branch * trunk\n");
@@ -1253,11 +1265,10 @@
1265 ** so this routine is a no-op.
1266 */
1267 void db_swap_connections(void){
1268 if( !g.useAttach ){
1269 sqlite3 *dbTemp = g.db;
 
1270 g.db = g.dbConfig;
1271 g.dbConfig = dbTemp;
1272 }
1273 }
1274
@@ -1374,11 +1385,11 @@
1385 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
1386 }
1387
1388 /*
1389 ** Record the name of a local repository in the global_config() database.
1390 ** The repository filename %s is recorded as an entry with a "name" field
1391 ** of the following form:
1392 **
1393 ** repo:%s
1394 **
1395 ** The value field is set to 1.
@@ -1546,10 +1557,13 @@
1557 **
1558 ** proxy URL of the HTTP proxy. If undefined or "off" then
1559 ** the "http_proxy" environment variable is consulted.
1560 ** If the http_proxy environment variable is undefined
1561 ** then a direct HTTP connection is used.
1562 **
1563 ** ssh-command Command used to talk to a remote machine with
1564 ** the "ssh://" protocol.
1565 **
1566 ** web-browser A shell command used to launch your preferred
1567 ** web browser when given a URL as an argument.
1568 ** Defaults to "start" on windows, "open" on Mac,
1569 ** and "firefox" on Unix.
@@ -1569,10 +1583,11 @@
1583 "http-port",
1584 "localauth",
1585 "mtime-changes",
1586 "pgp-command",
1587 "proxy",
1588 "ssh-command",
1589 "web-browser",
1590 };
1591 int i;
1592 int globalFlag = find_option("global","g",0)!=0;
1593 int unsetFlag = g.argv[1][0]=='u';
1594
--- src/descendants.c
+++ src/descendants.c
@@ -318,14 +318,17 @@
318318
style_header("Leaves");
319319
login_anonymous_available();
320320
compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
321321
style_sidebox_begin("Nomenclature:", "33%");
322322
@ <ol>
323
- @ <li> A <b>leaf</b> is a check-in with no descendants.</li>
324
- @ <li> An <b>open leaf</b> is a leaf that does not have a "closed" tag
323
+ @ <li> A <div class="sideboxDescribed">leaf</div>
324
+ @ is a check-in with no descendants.</li>
325
+ @ <li> An <div class="sideboxDescribed">open leaf</div>
326
+ @ is a leaf that does not have a "closed" tag
325327
@ and is thus assumed to still be in use.</li>
326
- @ <li> A <b>closed leaf</b> has a "closed" tag and is thus assumed to
328
+ @ <li> A <div class="sideboxDescribed">closed leaf</div>
329
+ @ has a "closed" tag and is thus assumed to
327330
@ be historical and no longer in active use.</li>
328331
@ </ol>
329332
style_sidebox_end();
330333
331334
if( showAll ){
@@ -341,14 +344,14 @@
341344
" ORDER BY event.mtime DESC",
342345
timeline_query_for_www()
343346
);
344347
www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
345348
db_finalize(&q);
346
- @ <br clear="both">
347
- @ <script>
349
+ @ <br />
350
+ @ <script type="text/JavaScript">
348351
@ function xin(id){
349352
@ }
350353
@ function xout(id){
351354
@ }
352355
@ </script>
353356
style_footer();
354357
}
355358
--- src/descendants.c
+++ src/descendants.c
@@ -318,14 +318,17 @@
318 style_header("Leaves");
319 login_anonymous_available();
320 compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
321 style_sidebox_begin("Nomenclature:", "33%");
322 @ <ol>
323 @ <li> A <b>leaf</b> is a check-in with no descendants.</li>
324 @ <li> An <b>open leaf</b> is a leaf that does not have a "closed" tag
 
 
325 @ and is thus assumed to still be in use.</li>
326 @ <li> A <b>closed leaf</b> has a "closed" tag and is thus assumed to
 
327 @ be historical and no longer in active use.</li>
328 @ </ol>
329 style_sidebox_end();
330
331 if( showAll ){
@@ -341,14 +344,14 @@
341 " ORDER BY event.mtime DESC",
342 timeline_query_for_www()
343 );
344 www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
345 db_finalize(&q);
346 @ <br clear="both">
347 @ <script>
348 @ function xin(id){
349 @ }
350 @ function xout(id){
351 @ }
352 @ </script>
353 style_footer();
354 }
355
--- src/descendants.c
+++ src/descendants.c
@@ -318,14 +318,17 @@
318 style_header("Leaves");
319 login_anonymous_available();
320 compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
321 style_sidebox_begin("Nomenclature:", "33%");
322 @ <ol>
323 @ <li> A <div class="sideboxDescribed">leaf</div>
324 @ is a check-in with no descendants.</li>
325 @ <li> An <div class="sideboxDescribed">open leaf</div>
326 @ is a leaf that does not have a "closed" tag
327 @ and is thus assumed to still be in use.</li>
328 @ <li> A <div class="sideboxDescribed">closed leaf</div>
329 @ has a "closed" tag and is thus assumed to
330 @ be historical and no longer in active use.</li>
331 @ </ol>
332 style_sidebox_end();
333
334 if( showAll ){
@@ -341,14 +344,14 @@
344 " ORDER BY event.mtime DESC",
345 timeline_query_for_www()
346 );
347 www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
348 db_finalize(&q);
349 @ <br />
350 @ <script type="text/JavaScript">
351 @ function xin(id){
352 @ }
353 @ function xout(id){
354 @ }
355 @ </script>
356 style_footer();
357 }
358
+81 -14
--- src/diff.c
+++ src/diff.c
@@ -65,16 +65,18 @@
6565
/*
6666
** Return an array of DLine objects containing a pointer to the
6767
** start of each line and a hash of that line. The lower
6868
** bits of the hash store the length of each line.
6969
**
70
-** Trailing whitespace is removed from each line.
70
+** Trailing whitespace is removed from each line. 2010-08-20: Not any
71
+** more. If trailing whitespace is ignored, the "patch" command gets
72
+** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
7173
**
7274
** Return 0 if the file is binary or contains a line that is
7375
** too long.
7476
*/
75
-static DLine *break_into_lines(const char *z, int n, int *pnLine){
77
+static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
7678
int nLine, i, j, k, x;
7779
unsigned int h, h2;
7880
DLine *a;
7981
8082
/* Count the number of lines. Allocate space to hold
@@ -102,11 +104,12 @@
102104
103105
/* Fill in the array */
104106
for(i=0; i<nLine; i++){
105107
a[i].z = z;
106108
for(j=0; z[j] && z[j]!='\n'; j++){}
107
- for(k=j; k>0 && isspace(z[k-1]); k--){}
109
+ k = j;
110
+ while( ignoreWS && k>0 && isspace(z[k-1]) ){ k--; }
108111
for(h=0, x=0; x<k; x++){
109112
h = h ^ (h<<2) ^ z[x];
110113
}
111114
a[i].h = h = (h<<LENGTH_MASK_SZ) | k;;
112115
h2 = h % nLine;
@@ -279,16 +282,70 @@
279282
for(j=0; j<m; j++){
280283
appendDiffLine(pOut, " ", &B[b+j]);
281284
}
282285
}
283286
}
287
+
288
+/*
289
+** Compute the optimal longest common subsequence (LCS) using an
290
+** exhaustive search. This version of the LCS is only used for
291
+** shorter input strings since runtime is O(N*N) where N is the
292
+** input string length.
293
+*/
294
+static void optimalLCS(
295
+ DContext *p, /* Two files being compared */
296
+ int iS1, int iE1, /* Range of lines in p->aFrom[] */
297
+ int iS2, int iE2, /* Range of lines in p->aTo[] */
298
+ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
299
+ int *piSY, int *piEY /* Write p->aTo[] common segment here */
300
+){
301
+ int mxLength = 0; /* Length of longest common subsequence */
302
+ int i, j; /* Loop counters */
303
+ int k; /* Length of a candidate subsequence */
304
+ int iSXb = iS1; /* Best match so far */
305
+ int iSYb = iS2; /* Best match so far */
306
+
307
+ for(i=iS1; i<iE1-mxLength; i++){
308
+ for(j=iS2; j<iE2-mxLength; j++){
309
+ if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
310
+ if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
311
+ continue;
312
+ }
313
+ k = 1;
314
+ while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
315
+ k++;
316
+ }
317
+ if( k>mxLength ){
318
+ iSXb = i;
319
+ iSYb = j;
320
+ mxLength = k;
321
+ }
322
+ }
323
+ }
324
+ *piSX = iSXb;
325
+ *piEX = iSXb + mxLength;
326
+ *piSY = iSYb;
327
+ *piEY = iSYb + mxLength;
328
+}
284329
285330
/*
286331
** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
287332
** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence
288333
** of lines in these two blocks that are exactly the same. Return
289334
** the bounds of the matching sequence.
335
+**
336
+** If there are two or more possible answers of the same length, the
337
+** returned sequence should be the one closest to the center of the
338
+** input range.
339
+**
340
+** Ideally, the common sequence should be the longest possible common
341
+** sequence. However, an exact computation of LCS is O(N*N) which is
342
+** way too slow for larger files. So this routine uses an O(N)
343
+** heuristic approximation based on hashing that usually works about
344
+** as well. But if the O(N) algorithm doesn't get a good solution
345
+** and N is not too large, we fall back to an exact solution by
346
+** calling optimalLCS().
290347
*/
291348
static void longestCommonSequence(
292349
DContext *p, /* Two files being compared */
293350
int iS1, int iE1, /* Range of lines in p->aFrom[] */
294351
int iS2, int iE2, /* Range of lines in p->aTo[] */
@@ -302,10 +359,11 @@
302359
int skew; /* How lopsided is the match */
303360
int dist; /* Distance of match from center */
304361
int mid; /* Center of the span */
305362
int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
306363
int iSXp, iSYp, iEXp, iEYp; /* Previous match */
364
+
307365
308366
iSXb = iSXp = iS1;
309367
iEXb = iEXp = iS1;
310368
iSYb = iSYp = iS2;
311369
iEYb = iEYp = iS2;
@@ -354,14 +412,20 @@
354412
iSYp = iSY;
355413
iEXp = iEX;
356414
iEYp = iEY;
357415
}
358416
}
359
- *piSX = iSXb;
360
- *piSY = iSYb;
361
- *piEX = iEXb;
362
- *piEY = iEYb;
417
+ if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
418
+ /* If no common sequence is found using the hashing heuristic and
419
+ ** the input is not too big, use the expensive exact solution */
420
+ optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
421
+ }else{
422
+ *piSX = iSXb;
423
+ *piSY = iSYb;
424
+ *piEX = iEXb;
425
+ *piEY = iEYb;
426
+ }
363427
/* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
364428
iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
365429
}
366430
367431
/*
@@ -472,18 +536,21 @@
472536
*/
473537
int *text_diff(
474538
Blob *pA_Blob, /* FROM file */
475539
Blob *pB_Blob, /* TO file */
476540
Blob *pOut, /* Write unified diff here if not NULL */
477
- int nContext /* Amount of context to unified diff */
541
+ int nContext, /* Amount of context to unified diff */
542
+ int ignoreEolWs /* Ignore whitespace at the end of lines */
478543
){
479544
DContext c;
480545
481546
/* Prepare the input files */
482547
memset(&c, 0, sizeof(c));
483
- c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom);
484
- c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo);
548
+ c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
549
+ &c.nFrom, ignoreEolWs);
550
+ c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
551
+ &c.nTo, ignoreEolWs);
485552
if( c.aFrom==0 || c.aTo==0 ){
486553
free(c.aFrom);
487554
free(c.aTo);
488555
if( pOut ){
489556
blob_appendf(pOut, "cannot compute difference between binary files\n");
@@ -522,11 +589,11 @@
522589
if( g.argc<4 ) usage("FILE1 FILE2 ...");
523590
blob_read_from_file(&a, g.argv[2]);
524591
for(i=3; i<g.argc; i++){
525592
if( i>3 ) printf("-------------------------------\n");
526593
blob_read_from_file(&b, g.argv[i]);
527
- R = text_diff(&a, &b, 0, 0);
594
+ R = text_diff(&a, &b, 0, 0, 0);
528595
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
529596
printf(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
530597
}
531598
/* free(R); */
532599
blob_reset(&b);
@@ -540,11 +607,11 @@
540607
Blob a, b, out;
541608
if( g.argc!=4 ) usage("FILE1 FILE2");
542609
blob_read_from_file(&a, g.argv[2]);
543610
blob_read_from_file(&b, g.argv[3]);
544611
blob_zero(&out);
545
- text_diff(&a, &b, &out, 3);
612
+ text_diff(&a, &b, &out, 3, 0);
546613
blob_write_to_file(&out, "-");
547614
}
548615
549616
/**************************************************************************
550617
** The basic difference engine is above. What follows is the annotation
@@ -574,11 +641,11 @@
574641
*/
575642
static int annotation_start(Annotator *p, Blob *pInput){
576643
int i;
577644
578645
memset(p, 0, sizeof(*p));
579
- p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput), &p->c.nTo);
646
+ p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
580647
if( p->c.aTo==0 ){
581648
return 1;
582649
}
583650
p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo );
584651
if( p->aOrig==0 ) fossil_panic("out of memory");
@@ -602,11 +669,11 @@
602669
int i, j;
603670
int lnTo;
604671
605672
/* Prepare the parent file to be diffed */
606673
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
607
- &p->c.nFrom);
674
+ &p->c.nFrom, 1);
608675
if( p->c.aFrom==0 ){
609676
return 1;
610677
}
611678
612679
/* Compute the differences going from pParent to the file being
613680
--- src/diff.c
+++ src/diff.c
@@ -65,16 +65,18 @@
65 /*
66 ** Return an array of DLine objects containing a pointer to the
67 ** start of each line and a hash of that line. The lower
68 ** bits of the hash store the length of each line.
69 **
70 ** Trailing whitespace is removed from each line.
 
 
71 **
72 ** Return 0 if the file is binary or contains a line that is
73 ** too long.
74 */
75 static DLine *break_into_lines(const char *z, int n, int *pnLine){
76 int nLine, i, j, k, x;
77 unsigned int h, h2;
78 DLine *a;
79
80 /* Count the number of lines. Allocate space to hold
@@ -102,11 +104,12 @@
102
103 /* Fill in the array */
104 for(i=0; i<nLine; i++){
105 a[i].z = z;
106 for(j=0; z[j] && z[j]!='\n'; j++){}
107 for(k=j; k>0 && isspace(z[k-1]); k--){}
 
108 for(h=0, x=0; x<k; x++){
109 h = h ^ (h<<2) ^ z[x];
110 }
111 a[i].h = h = (h<<LENGTH_MASK_SZ) | k;;
112 h2 = h % nLine;
@@ -279,16 +282,70 @@
279 for(j=0; j<m; j++){
280 appendDiffLine(pOut, " ", &B[b+j]);
281 }
282 }
283 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
285 /*
286 ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
287 ** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence
288 ** of lines in these two blocks that are exactly the same. Return
289 ** the bounds of the matching sequence.
 
 
 
 
 
 
 
 
 
 
 
 
290 */
291 static void longestCommonSequence(
292 DContext *p, /* Two files being compared */
293 int iS1, int iE1, /* Range of lines in p->aFrom[] */
294 int iS2, int iE2, /* Range of lines in p->aTo[] */
@@ -302,10 +359,11 @@
302 int skew; /* How lopsided is the match */
303 int dist; /* Distance of match from center */
304 int mid; /* Center of the span */
305 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
306 int iSXp, iSYp, iEXp, iEYp; /* Previous match */
 
307
308 iSXb = iSXp = iS1;
309 iEXb = iEXp = iS1;
310 iSYb = iSYp = iS2;
311 iEYb = iEYp = iS2;
@@ -354,14 +412,20 @@
354 iSYp = iSY;
355 iEXp = iEX;
356 iEYp = iEY;
357 }
358 }
359 *piSX = iSXb;
360 *piSY = iSYb;
361 *piEX = iEXb;
362 *piEY = iEYb;
 
 
 
 
 
 
363 /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
364 iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
365 }
366
367 /*
@@ -472,18 +536,21 @@
472 */
473 int *text_diff(
474 Blob *pA_Blob, /* FROM file */
475 Blob *pB_Blob, /* TO file */
476 Blob *pOut, /* Write unified diff here if not NULL */
477 int nContext /* Amount of context to unified diff */
 
478 ){
479 DContext c;
480
481 /* Prepare the input files */
482 memset(&c, 0, sizeof(c));
483 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom);
484 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo);
 
 
485 if( c.aFrom==0 || c.aTo==0 ){
486 free(c.aFrom);
487 free(c.aTo);
488 if( pOut ){
489 blob_appendf(pOut, "cannot compute difference between binary files\n");
@@ -522,11 +589,11 @@
522 if( g.argc<4 ) usage("FILE1 FILE2 ...");
523 blob_read_from_file(&a, g.argv[2]);
524 for(i=3; i<g.argc; i++){
525 if( i>3 ) printf("-------------------------------\n");
526 blob_read_from_file(&b, g.argv[i]);
527 R = text_diff(&a, &b, 0, 0);
528 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
529 printf(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
530 }
531 /* free(R); */
532 blob_reset(&b);
@@ -540,11 +607,11 @@
540 Blob a, b, out;
541 if( g.argc!=4 ) usage("FILE1 FILE2");
542 blob_read_from_file(&a, g.argv[2]);
543 blob_read_from_file(&b, g.argv[3]);
544 blob_zero(&out);
545 text_diff(&a, &b, &out, 3);
546 blob_write_to_file(&out, "-");
547 }
548
549 /**************************************************************************
550 ** The basic difference engine is above. What follows is the annotation
@@ -574,11 +641,11 @@
574 */
575 static int annotation_start(Annotator *p, Blob *pInput){
576 int i;
577
578 memset(p, 0, sizeof(*p));
579 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput), &p->c.nTo);
580 if( p->c.aTo==0 ){
581 return 1;
582 }
583 p->aOrig = malloc( sizeof(p->aOrig[0])*p->c.nTo );
584 if( p->aOrig==0 ) fossil_panic("out of memory");
@@ -602,11 +669,11 @@
602 int i, j;
603 int lnTo;
604
605 /* Prepare the parent file to be diffed */
606 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
607 &p->c.nFrom);
608 if( p->c.aFrom==0 ){
609 return 1;
610 }
611
612 /* Compute the differences going from pParent to the file being
613
--- src/diff.c
+++ src/diff.c
@@ -65,16 +65,18 @@
65 /*
66 ** Return an array of DLine objects containing a pointer to the
67 ** start of each line and a hash of that line. The lower
68 ** bits of the hash store the length of each line.
69 **
70 ** Trailing whitespace is removed from each line. 2010-08-20: Not any
71 ** more. If trailing whitespace is ignored, the "patch" command gets
72 ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
73 **
74 ** Return 0 if the file is binary or contains a line that is
75 ** too long.
76 */
77 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
78 int nLine, i, j, k, x;
79 unsigned int h, h2;
80 DLine *a;
81
82 /* Count the number of lines. Allocate space to hold
@@ -102,11 +104,12 @@
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;
@@ -279,16 +282,70 @@
282 for(j=0; j<m; j++){
283 appendDiffLine(pOut, " ", &B[b+j]);
284 }
285 }
286 }
287
288 /*
289 ** Compute the optimal longest common subsequence (LCS) using an
290 ** exhaustive search. This version of the LCS is only used for
291 ** shorter input strings since runtime is O(N*N) where N is the
292 ** input string length.
293 */
294 static void optimalLCS(
295 DContext *p, /* Two files being compared */
296 int iS1, int iE1, /* Range of lines in p->aFrom[] */
297 int iS2, int iE2, /* Range of lines in p->aTo[] */
298 int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
299 int *piSY, int *piEY /* Write p->aTo[] common segment here */
300 ){
301 int mxLength = 0; /* Length of longest common subsequence */
302 int i, j; /* Loop counters */
303 int k; /* Length of a candidate subsequence */
304 int iSXb = iS1; /* Best match so far */
305 int iSYb = iS2; /* Best match so far */
306
307 for(i=iS1; i<iE1-mxLength; i++){
308 for(j=iS2; j<iE2-mxLength; j++){
309 if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
310 if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
311 continue;
312 }
313 k = 1;
314 while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
315 k++;
316 }
317 if( k>mxLength ){
318 iSXb = i;
319 iSYb = j;
320 mxLength = k;
321 }
322 }
323 }
324 *piSX = iSXb;
325 *piEX = iSXb + mxLength;
326 *piSY = iSYb;
327 *piEY = iSYb + mxLength;
328 }
329
330 /*
331 ** Compare two blocks of text on lines iS1 through iE1-1 of the aFrom[]
332 ** file and lines iS2 through iE2-1 of the aTo[] file. Locate a sequence
333 ** of lines in these two blocks that are exactly the same. Return
334 ** the bounds of the matching sequence.
335 **
336 ** If there are two or more possible answers of the same length, the
337 ** returned sequence should be the one closest to the center of the
338 ** input range.
339 **
340 ** Ideally, the common sequence should be the longest possible common
341 ** sequence. However, an exact computation of LCS is O(N*N) which is
342 ** way too slow for larger files. So this routine uses an O(N)
343 ** heuristic approximation based on hashing that usually works about
344 ** as well. But if the O(N) algorithm doesn't get a good solution
345 ** and N is not too large, we fall back to an exact solution by
346 ** calling optimalLCS().
347 */
348 static void longestCommonSequence(
349 DContext *p, /* Two files being compared */
350 int iS1, int iE1, /* Range of lines in p->aFrom[] */
351 int iS2, int iE2, /* Range of lines in p->aTo[] */
@@ -302,10 +359,11 @@
359 int skew; /* How lopsided is the match */
360 int dist; /* Distance of match from center */
361 int mid; /* Center of the span */
362 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
363 int iSXp, iSYp, iEXp, iEYp; /* Previous match */
364
365
366 iSXb = iSXp = iS1;
367 iEXb = iEXp = iS1;
368 iSYb = iSYp = iS2;
369 iEYb = iEYp = iS2;
@@ -354,14 +412,20 @@
412 iSYp = iSY;
413 iEXp = iEX;
414 iEYp = iEY;
415 }
416 }
417 if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
418 /* If no common sequence is found using the hashing heuristic and
419 ** the input is not too big, use the expensive exact solution */
420 optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
421 }else{
422 *piSX = iSXb;
423 *piSY = iSYb;
424 *piEX = iEXb;
425 *piEY = iEYb;
426 }
427 /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
428 iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
429 }
430
431 /*
@@ -472,18 +536,21 @@
536 */
537 int *text_diff(
538 Blob *pA_Blob, /* FROM file */
539 Blob *pB_Blob, /* TO file */
540 Blob *pOut, /* Write unified diff here if not NULL */
541 int nContext, /* Amount of context to unified diff */
542 int ignoreEolWs /* Ignore whitespace at the end of lines */
543 ){
544 DContext c;
545
546 /* Prepare the input files */
547 memset(&c, 0, sizeof(c));
548 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
549 &c.nFrom, ignoreEolWs);
550 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
551 &c.nTo, ignoreEolWs);
552 if( c.aFrom==0 || c.aTo==0 ){
553 free(c.aFrom);
554 free(c.aTo);
555 if( pOut ){
556 blob_appendf(pOut, "cannot compute difference between binary files\n");
@@ -522,11 +589,11 @@
589 if( g.argc<4 ) usage("FILE1 FILE2 ...");
590 blob_read_from_file(&a, g.argv[2]);
591 for(i=3; i<g.argc; i++){
592 if( i>3 ) printf("-------------------------------\n");
593 blob_read_from_file(&b, g.argv[i]);
594 R = text_diff(&a, &b, 0, 0, 0);
595 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
596 printf(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
597 }
598 /* free(R); */
599 blob_reset(&b);
@@ -540,11 +607,11 @@
607 Blob a, b, out;
608 if( g.argc!=4 ) usage("FILE1 FILE2");
609 blob_read_from_file(&a, g.argv[2]);
610 blob_read_from_file(&b, g.argv[3]);
611 blob_zero(&out);
612 text_diff(&a, &b, &out, 3, 0);
613 blob_write_to_file(&out, "-");
614 }
615
616 /**************************************************************************
617 ** The basic difference engine is above. What follows is the annotation
@@ -574,11 +641,11 @@
641 */
642 static int annotation_start(Annotator *p, Blob *pInput){
643 int i;
644
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");
@@ -602,11 +669,11 @@
669 int i, j;
670 int lnTo;
671
672 /* Prepare the parent file to be diffed */
673 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
674 &p->c.nFrom, 1);
675 if( p->c.aFrom==0 ){
676 return 1;
677 }
678
679 /* Compute the differences going from pParent to the file being
680
+35 -40
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,37 +19,16 @@
1919
*/
2020
#include "config.h"
2121
#include "diffcmd.h"
2222
#include <assert.h>
2323
24
-/*
25
-** Shell-escape the given string. Append the result to a blob.
26
-*/
27
-static void shell_escape(Blob *pBlob, const char *zIn){
28
- int n = blob_size(pBlob);
29
- int k = strlen(zIn);
30
- int i, c;
31
- char *z;
32
- for(i=0; (c = zIn[i])!=0; i++){
33
- if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
34
- blob_appendf(pBlob, "\"%s\"", zIn);
35
- z = blob_buffer(pBlob);
36
- for(i=n+1; i<=n+k; i++){
37
- if( z[i]=='"' ) z[i] = '_';
38
- }
39
- return;
40
- }
41
- }
42
- blob_append(pBlob, zIn, -1);
43
-}
44
-
4524
/*
4625
** This function implements a cross-platform "system()" interface.
4726
*/
4827
int portable_system(const char *zOrigCmd){
4928
int rc;
50
-#ifdef __MINGW32__
29
+#if defined(_WIN32)
5130
/* On windows, we have to put double-quotes around the entire command.
5231
** Who knows why - this is just the way windows works.
5332
*/
5433
char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
5534
rc = system(zNewCmd);
@@ -73,11 +52,12 @@
7352
*/
7453
static void diff_file(
7554
Blob *pFile1, /* In memory content to compare from */
7655
const char *zFile2, /* On disk content to compare to */
7756
const char *zName, /* Display name of the file */
78
- const char *zDiffCmd /* Command for comparison */
57
+ const char *zDiffCmd, /* Command for comparison */
58
+ int ignoreEolWs /* Ignore whitespace at end of lines */
7959
){
8060
if( zDiffCmd==0 ){
8161
Blob out; /* Diff output text */
8262
Blob file2; /* Content of zFile2 */
8363
@@ -85,11 +65,11 @@
8565
blob_zero(&file2);
8666
blob_read_from_file(&file2, zFile2);
8767
8868
/* Compute and output the differences */
8969
blob_zero(&out);
90
- text_diff(pFile1, &file2, &out, 5);
70
+ text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
9171
printf("--- %s\n+++ %s\n", zName, zName);
9272
printf("%s\n", blob_str(&out));
9373
9474
/* Release memory resources */
9575
blob_reset(&file2);
@@ -136,17 +116,18 @@
136116
*/
137117
static void diff_file_mem(
138118
Blob *pFile1, /* In memory content to compare from */
139119
Blob *pFile2, /* In memory content to compare to */
140120
const char *zName, /* Display name of the file */
141
- const char *zDiffCmd /* Command for comparison */
121
+ const char *zDiffCmd, /* Command for comparison */
122
+ int ignoreEolWs /* Ignore whitespace at end of lines */
142123
){
143124
if( zDiffCmd==0 ){
144125
Blob out; /* Diff output text */
145126
146127
blob_zero(&out);
147
- text_diff(pFile1, pFile2, &out, 5);
128
+ text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
148129
printf("--- %s\n+++ %s\n", zName, zName);
149130
printf("%s\n", blob_str(&out));
150131
151132
/* Release memory resources */
152133
blob_reset(&out);
@@ -180,26 +161,34 @@
180161
181162
/*
182163
** Do a diff against a single file named in g.argv[2] from version zFrom
183164
** against the same file on disk.
184165
*/
185
-static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){
166
+static void diff_one_against_disk(
167
+ const char *zFrom, /* Name of file */
168
+ const char *zDiffCmd, /* Use this "diff" command */
169
+ int ignoreEolWs /* Ignore whitespace changes at end of lines */
170
+){
186171
Blob fname;
187172
Blob content;
188173
file_tree_name(g.argv[2], &fname, 1);
189174
historical_version_of_file(zFrom, blob_str(&fname), &content, 0);
190
- diff_file(&content, g.argv[2], g.argv[2], zDiffCmd);
175
+ diff_file(&content, g.argv[2], g.argv[2], zDiffCmd, ignoreEolWs);
191176
blob_reset(&content);
192177
blob_reset(&fname);
193178
}
194179
195180
/*
196181
** Run a diff between the version zFrom and files on disk. zFrom might
197182
** be NULL which means to simply show the difference between the edited
198183
** files on disk and the check-out on which they are based.
199184
*/
200
-static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
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
+){
201190
int vid;
202191
Blob sql;
203192
Stmt q;
204193
205194
vid = db_lget_int("checkout", 0);
@@ -265,11 +254,11 @@
265254
content_get(srcid, &content);
266255
printf("Index: %s\n======================================="
267256
"============================\n",
268257
zPathname
269258
);
270
- diff_file(&content, zFullName, zPathname, zDiffCmd);
259
+ diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
271260
blob_reset(&content);
272261
}
273262
free(zFullName);
274263
}
275264
db_finalize(&q);
@@ -282,20 +271,21 @@
282271
** The filename is contained in g.argv[2].
283272
*/
284273
static void diff_one_two_versions(
285274
const char *zFrom,
286275
const char *zTo,
287
- const char *zDiffCmd
276
+ const char *zDiffCmd,
277
+ int ignoreEolWs
288278
){
289279
char *zName;
290280
Blob fname;
291281
Blob v1, v2;
292282
file_tree_name(g.argv[2], &fname, 1);
293283
zName = blob_str(&fname);
294284
historical_version_of_file(zFrom, zName, &v1, 0);
295285
historical_version_of_file(zTo, zName, &v2, 0);
296
- diff_file_mem(&v1, &v2, zName, zDiffCmd);
286
+ diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
297287
blob_reset(&v1);
298288
blob_reset(&v2);
299289
blob_reset(&fname);
300290
}
301291
@@ -303,11 +293,12 @@
303293
** Output the differences between two check-ins.
304294
*/
305295
static void diff_all_two_versions(
306296
const char *zFrom,
307297
const char *zTo,
308
- const char *zDiffCmd
298
+ const char *zDiffCmd,
299
+ int ignoreEolWs
309300
){
310301
Manifest mFrom, mTo;
311302
int iFrom, iTo;
312303
313304
manifest_from_name(zFrom, &mFrom);
@@ -334,15 +325,19 @@
334325
iTo++;
335326
}else{
336327
Blob f1, f2;
337328
int rid;
338329
printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
330
+ printf("Index: %s\n======================================="
331
+ "============================\n",
332
+ mFrom.aFile[iFrom].zName
333
+ );
339334
rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0);
340335
content_get(rid, &f1);
341336
rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0);
342337
content_get(rid, &f2);
343
- diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd);
338
+ diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd, ignoreEolWs);
344339
blob_reset(&f1);
345340
blob_reset(&f2);
346341
iFrom++;
347342
iTo++;
348343
}
@@ -389,28 +384,28 @@
389384
zTo = find_option("to", 0, 1);
390385
391386
if( zTo==0 ){
392387
db_must_be_within_tree();
393388
verify_all_options();
394
- if( !isInternDiff && g.argc==3 ){
389
+ if( !isInternDiff ){
395390
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
396391
}
397392
if( g.argc==3 ){
398
- diff_one_against_disk(zFrom, zDiffCmd);
393
+ diff_one_against_disk(zFrom, zDiffCmd, 0);
399394
}else{
400
- diff_all_against_disk(zFrom, zDiffCmd);
395
+ diff_all_against_disk(zFrom, zDiffCmd, 0);
401396
}
402397
}else if( zFrom==0 ){
403398
fossil_fatal("must use --from if --to is present");
404399
}else{
405400
db_find_and_open_repository(1);
406401
verify_all_options();
407
- if( !isInternDiff && g.argc==3 ){
402
+ if( !isInternDiff ){
408403
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
409404
}
410405
if( g.argc==3 ){
411
- diff_one_two_versions(zFrom, zTo, zDiffCmd);
406
+ diff_one_two_versions(zFrom, zTo, zDiffCmd, 0);
412407
}else{
413
- diff_all_two_versions(zFrom, zTo, zDiffCmd);
408
+ diff_all_two_versions(zFrom, zTo, zDiffCmd, 0);
414409
}
415410
}
416411
}
417412
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,37 +19,16 @@
19 */
20 #include "config.h"
21 #include "diffcmd.h"
22 #include <assert.h>
23
24 /*
25 ** Shell-escape the given string. Append the result to a blob.
26 */
27 static void shell_escape(Blob *pBlob, const char *zIn){
28 int n = blob_size(pBlob);
29 int k = strlen(zIn);
30 int i, c;
31 char *z;
32 for(i=0; (c = zIn[i])!=0; i++){
33 if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
34 blob_appendf(pBlob, "\"%s\"", zIn);
35 z = blob_buffer(pBlob);
36 for(i=n+1; i<=n+k; i++){
37 if( z[i]=='"' ) z[i] = '_';
38 }
39 return;
40 }
41 }
42 blob_append(pBlob, zIn, -1);
43 }
44
45 /*
46 ** This function implements a cross-platform "system()" interface.
47 */
48 int portable_system(const char *zOrigCmd){
49 int rc;
50 #ifdef __MINGW32__
51 /* On windows, we have to put double-quotes around the entire command.
52 ** Who knows why - this is just the way windows works.
53 */
54 char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
55 rc = system(zNewCmd);
@@ -73,11 +52,12 @@
73 */
74 static void diff_file(
75 Blob *pFile1, /* In memory content to compare from */
76 const char *zFile2, /* On disk content to compare to */
77 const char *zName, /* Display name of the file */
78 const char *zDiffCmd /* Command for comparison */
 
79 ){
80 if( zDiffCmd==0 ){
81 Blob out; /* Diff output text */
82 Blob file2; /* Content of zFile2 */
83
@@ -85,11 +65,11 @@
85 blob_zero(&file2);
86 blob_read_from_file(&file2, zFile2);
87
88 /* Compute and output the differences */
89 blob_zero(&out);
90 text_diff(pFile1, &file2, &out, 5);
91 printf("--- %s\n+++ %s\n", zName, zName);
92 printf("%s\n", blob_str(&out));
93
94 /* Release memory resources */
95 blob_reset(&file2);
@@ -136,17 +116,18 @@
136 */
137 static void diff_file_mem(
138 Blob *pFile1, /* In memory content to compare from */
139 Blob *pFile2, /* In memory content to compare to */
140 const char *zName, /* Display name of the file */
141 const char *zDiffCmd /* Command for comparison */
 
142 ){
143 if( zDiffCmd==0 ){
144 Blob out; /* Diff output text */
145
146 blob_zero(&out);
147 text_diff(pFile1, pFile2, &out, 5);
148 printf("--- %s\n+++ %s\n", zName, zName);
149 printf("%s\n", blob_str(&out));
150
151 /* Release memory resources */
152 blob_reset(&out);
@@ -180,26 +161,34 @@
180
181 /*
182 ** Do a diff against a single file named in g.argv[2] from version zFrom
183 ** against the same file on disk.
184 */
185 static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){
 
 
 
 
186 Blob fname;
187 Blob content;
188 file_tree_name(g.argv[2], &fname, 1);
189 historical_version_of_file(zFrom, blob_str(&fname), &content, 0);
190 diff_file(&content, g.argv[2], g.argv[2], zDiffCmd);
191 blob_reset(&content);
192 blob_reset(&fname);
193 }
194
195 /*
196 ** Run a diff between the version zFrom and files on disk. zFrom might
197 ** be NULL which means to simply show the difference between the edited
198 ** files on disk and the check-out on which they are based.
199 */
200 static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
 
 
 
 
201 int vid;
202 Blob sql;
203 Stmt q;
204
205 vid = db_lget_int("checkout", 0);
@@ -265,11 +254,11 @@
265 content_get(srcid, &content);
266 printf("Index: %s\n======================================="
267 "============================\n",
268 zPathname
269 );
270 diff_file(&content, zFullName, zPathname, zDiffCmd);
271 blob_reset(&content);
272 }
273 free(zFullName);
274 }
275 db_finalize(&q);
@@ -282,20 +271,21 @@
282 ** The filename is contained in g.argv[2].
283 */
284 static void diff_one_two_versions(
285 const char *zFrom,
286 const char *zTo,
287 const char *zDiffCmd
 
288 ){
289 char *zName;
290 Blob fname;
291 Blob v1, v2;
292 file_tree_name(g.argv[2], &fname, 1);
293 zName = blob_str(&fname);
294 historical_version_of_file(zFrom, zName, &v1, 0);
295 historical_version_of_file(zTo, zName, &v2, 0);
296 diff_file_mem(&v1, &v2, zName, zDiffCmd);
297 blob_reset(&v1);
298 blob_reset(&v2);
299 blob_reset(&fname);
300 }
301
@@ -303,11 +293,12 @@
303 ** Output the differences between two check-ins.
304 */
305 static void diff_all_two_versions(
306 const char *zFrom,
307 const char *zTo,
308 const char *zDiffCmd
 
309 ){
310 Manifest mFrom, mTo;
311 int iFrom, iTo;
312
313 manifest_from_name(zFrom, &mFrom);
@@ -334,15 +325,19 @@
334 iTo++;
335 }else{
336 Blob f1, f2;
337 int rid;
338 printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
 
 
 
 
339 rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0);
340 content_get(rid, &f1);
341 rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0);
342 content_get(rid, &f2);
343 diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd);
344 blob_reset(&f1);
345 blob_reset(&f2);
346 iFrom++;
347 iTo++;
348 }
@@ -389,28 +384,28 @@
389 zTo = find_option("to", 0, 1);
390
391 if( zTo==0 ){
392 db_must_be_within_tree();
393 verify_all_options();
394 if( !isInternDiff && g.argc==3 ){
395 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
396 }
397 if( g.argc==3 ){
398 diff_one_against_disk(zFrom, zDiffCmd);
399 }else{
400 diff_all_against_disk(zFrom, zDiffCmd);
401 }
402 }else if( zFrom==0 ){
403 fossil_fatal("must use --from if --to is present");
404 }else{
405 db_find_and_open_repository(1);
406 verify_all_options();
407 if( !isInternDiff && g.argc==3 ){
408 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
409 }
410 if( g.argc==3 ){
411 diff_one_two_versions(zFrom, zTo, zDiffCmd);
412 }else{
413 diff_all_two_versions(zFrom, zTo, zDiffCmd);
414 }
415 }
416 }
417
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,37 +19,16 @@
19 */
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);
@@ -73,11 +52,12 @@
52 */
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
@@ -85,11 +65,11 @@
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);
@@ -136,17 +116,18 @@
116 */
117 static void diff_file_mem(
118 Blob *pFile1, /* In memory content to compare from */
119 Blob *pFile2, /* In memory content to compare to */
120 const char *zName, /* Display name of the file */
121 const char *zDiffCmd, /* Command for comparison */
122 int ignoreEolWs /* Ignore whitespace at end of lines */
123 ){
124 if( zDiffCmd==0 ){
125 Blob out; /* Diff output text */
126
127 blob_zero(&out);
128 text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
129 printf("--- %s\n+++ %s\n", zName, zName);
130 printf("%s\n", blob_str(&out));
131
132 /* Release memory resources */
133 blob_reset(&out);
@@ -180,26 +161,34 @@
161
162 /*
163 ** Do a diff against a single file named in g.argv[2] from version zFrom
164 ** against the same file on disk.
165 */
166 static void diff_one_against_disk(
167 const char *zFrom, /* Name of file */
168 const char *zDiffCmd, /* Use this "diff" command */
169 int ignoreEolWs /* Ignore whitespace changes at end of lines */
170 ){
171 Blob fname;
172 Blob content;
173 file_tree_name(g.argv[2], &fname, 1);
174 historical_version_of_file(zFrom, blob_str(&fname), &content, 0);
175 diff_file(&content, g.argv[2], g.argv[2], zDiffCmd, ignoreEolWs);
176 blob_reset(&content);
177 blob_reset(&fname);
178 }
179
180 /*
181 ** Run a diff between the version zFrom and files on disk. zFrom might
182 ** be NULL which means to simply show the difference between the edited
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);
@@ -265,11 +254,11 @@
254 content_get(srcid, &content);
255 printf("Index: %s\n======================================="
256 "============================\n",
257 zPathname
258 );
259 diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
260 blob_reset(&content);
261 }
262 free(zFullName);
263 }
264 db_finalize(&q);
@@ -282,20 +271,21 @@
271 ** The filename is contained in g.argv[2].
272 */
273 static void diff_one_two_versions(
274 const char *zFrom,
275 const char *zTo,
276 const char *zDiffCmd,
277 int ignoreEolWs
278 ){
279 char *zName;
280 Blob fname;
281 Blob v1, v2;
282 file_tree_name(g.argv[2], &fname, 1);
283 zName = blob_str(&fname);
284 historical_version_of_file(zFrom, zName, &v1, 0);
285 historical_version_of_file(zTo, zName, &v2, 0);
286 diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
287 blob_reset(&v1);
288 blob_reset(&v2);
289 blob_reset(&fname);
290 }
291
@@ -303,11 +293,12 @@
293 ** Output the differences between two check-ins.
294 */
295 static void diff_all_two_versions(
296 const char *zFrom,
297 const char *zTo,
298 const char *zDiffCmd,
299 int ignoreEolWs
300 ){
301 Manifest mFrom, mTo;
302 int iFrom, iTo;
303
304 manifest_from_name(zFrom, &mFrom);
@@ -334,15 +325,19 @@
325 iTo++;
326 }else{
327 Blob f1, f2;
328 int rid;
329 printf("CHANGED %s\n", mFrom.aFile[iFrom].zName);
330 printf("Index: %s\n======================================="
331 "============================\n",
332 mFrom.aFile[iFrom].zName
333 );
334 rid = uuid_to_rid(mFrom.aFile[iFrom].zUuid, 0);
335 content_get(rid, &f1);
336 rid = uuid_to_rid(mTo.aFile[iTo].zUuid, 0);
337 content_get(rid, &f2);
338 diff_file_mem(&f1, &f2, mFrom.aFile[iFrom].zName, zDiffCmd, ignoreEolWs);
339 blob_reset(&f1);
340 blob_reset(&f2);
341 iFrom++;
342 iTo++;
343 }
@@ -389,28 +384,28 @@
384 zTo = find_option("to", 0, 1);
385
386 if( zTo==0 ){
387 db_must_be_within_tree();
388 verify_all_options();
389 if( !isInternDiff ){
390 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
391 }
392 if( g.argc==3 ){
393 diff_one_against_disk(zFrom, zDiffCmd, 0);
394 }else{
395 diff_all_against_disk(zFrom, zDiffCmd, 0);
396 }
397 }else if( zFrom==0 ){
398 fossil_fatal("must use --from if --to is present");
399 }else{
400 db_find_and_open_repository(1);
401 verify_all_options();
402 if( !isInternDiff ){
403 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
404 }
405 if( g.argc==3 ){
406 diff_one_two_versions(zFrom, zTo, zDiffCmd, 0);
407 }else{
408 diff_all_two_versions(zFrom, zTo, zDiffCmd, 0);
409 }
410 }
411 }
412
+2 -1
--- src/encode.c
+++ src/encode.c
@@ -100,11 +100,12 @@
100100
int i = 0;
101101
int count = 0;
102102
char *zOut;
103103
int other;
104104
# define IsSafeChar(X) \
105
- (isalnum(X) || (X)=='.' || (X)=='$' || (X)=='-' || (X)=='_' || (X)==other)
105
+ (isalnum(X) || (X)=='.' || (X)=='$' \
106
+ || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other)
106107
107108
if( zIn==0 ) return 0;
108109
if( n<0 ) n = strlen(zIn);
109110
other = encodeSlash ? 'a' : '/';
110111
while( i<n && (c = zIn[i])!=0 ){
111112
--- src/encode.c
+++ src/encode.c
@@ -100,11 +100,12 @@
100 int i = 0;
101 int count = 0;
102 char *zOut;
103 int other;
104 # define IsSafeChar(X) \
105 (isalnum(X) || (X)=='.' || (X)=='$' || (X)=='-' || (X)=='_' || (X)==other)
 
106
107 if( zIn==0 ) return 0;
108 if( n<0 ) n = strlen(zIn);
109 other = encodeSlash ? 'a' : '/';
110 while( i<n && (c = zIn[i])!=0 ){
111
--- src/encode.c
+++ src/encode.c
@@ -100,11 +100,12 @@
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' : '/';
111 while( i<n && (c = zIn[i])!=0 ){
112
+10 -7
--- src/file.c
+++ src/file.c
@@ -83,11 +83,14 @@
8383
** Return TRUE if the named file is an executable. Return false
8484
** for directories, devices, fifos, symlinks, etc.
8585
*/
8686
int file_isexe(const char *zFilename){
8787
if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
88
-#ifdef __MINGW32__
88
+#if defined(_WIN32)
89
+# if defined(__DMC__) || defined(_MSC_VER)
90
+# define S_IXUSR _S_IEXEC
91
+# endif
8992
return ((S_IXUSR)&fileStat.st_mode)!=0;
9093
#else
9194
return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
9295
#endif
9396
}
@@ -145,11 +148,11 @@
145148
146149
/*
147150
** Set or clear the execute bit on a file.
148151
*/
149152
void file_setexe(const char *zFilename, int onoff){
150
-#ifndef __MINGW32__
153
+#if !defined(_WIN32)
151154
struct stat buf;
152155
if( stat(zFilename, &buf)!=0 ) return;
153156
if( onoff ){
154157
if( (buf.st_mode & 0111)!=0111 ){
155158
chmod(zFilename, buf.st_mode | 0111);
@@ -157,11 +160,11 @@
157160
}else{
158161
if( (buf.st_mode & 0111)!=0 ){
159162
chmod(zFilename, buf.st_mode & ~0111);
160163
}
161164
}
162
-#endif /* __MINGW32__ */
165
+#endif /* _WIN32 */
163166
}
164167
165168
/*
166169
** Create the directory named in the argument, if it does not already
167170
** exist. If forceFlag is 1, delete any prior non-directory object
@@ -174,11 +177,11 @@
174177
if( rc==2 ){
175178
if( !forceFlag ) return 1;
176179
unlink(zName);
177180
}
178181
if( rc!=1 ){
179
-#ifdef __MINGW32__
182
+#if defined(_WIN32)
180183
return mkdir(zName);
181184
#else
182185
return mkdir(zName, 0755);
183186
#endif
184187
}
@@ -231,11 +234,11 @@
231234
** Changes are made in-place. Return the new name length.
232235
*/
233236
int file_simplify_name(char *z, int n){
234237
int i, j;
235238
if( n<0 ) n = strlen(z);
236
-#ifdef __MINGW32__
239
+#if defined(_WIN32)
237240
for(i=0; i<n; i++){
238241
if( z[i]=='\\' ) z[i] = '/';
239242
}
240243
#endif
241244
while( n>1 && z[n-1]=='/' ){ n--; }
@@ -266,11 +269,11 @@
266269
** Remove all /./ path elements.
267270
** Convert /A/../ to just /
268271
*/
269272
void file_canonical_name(const char *zOrigName, Blob *pOut){
270273
if( zOrigName[0]=='/'
271
-#ifdef __MINGW32__
274
+#if defined(_WIN32)
272275
|| zOrigName[0]=='\\'
273276
|| (strlen(zOrigName)>3 && zOrigName[1]==':'
274277
&& (zOrigName[2]=='\\' || zOrigName[2]=='/'))
275278
#endif
276279
){
@@ -311,11 +314,11 @@
311314
** contain no "/./" or "/../" terms.
312315
*/
313316
int file_is_canonical(const char *z){
314317
int i;
315318
if( z[0]!='/'
316
-#ifdef __MINGW32__
319
+#if defined(_WIN32)
317320
&& (z[0]==0 || z[1]!=':' || z[2]!='/')
318321
#endif
319322
) return 0;
320323
321324
for(i=0; z[i]; i++){
322325
--- src/file.c
+++ src/file.c
@@ -83,11 +83,14 @@
83 ** Return TRUE if the named file is an executable. Return false
84 ** for directories, devices, fifos, symlinks, etc.
85 */
86 int file_isexe(const char *zFilename){
87 if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
88 #ifdef __MINGW32__
 
 
 
89 return ((S_IXUSR)&fileStat.st_mode)!=0;
90 #else
91 return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
92 #endif
93 }
@@ -145,11 +148,11 @@
145
146 /*
147 ** Set or clear the execute bit on a file.
148 */
149 void file_setexe(const char *zFilename, int onoff){
150 #ifndef __MINGW32__
151 struct stat buf;
152 if( stat(zFilename, &buf)!=0 ) return;
153 if( onoff ){
154 if( (buf.st_mode & 0111)!=0111 ){
155 chmod(zFilename, buf.st_mode | 0111);
@@ -157,11 +160,11 @@
157 }else{
158 if( (buf.st_mode & 0111)!=0 ){
159 chmod(zFilename, buf.st_mode & ~0111);
160 }
161 }
162 #endif /* __MINGW32__ */
163 }
164
165 /*
166 ** Create the directory named in the argument, if it does not already
167 ** exist. If forceFlag is 1, delete any prior non-directory object
@@ -174,11 +177,11 @@
174 if( rc==2 ){
175 if( !forceFlag ) return 1;
176 unlink(zName);
177 }
178 if( rc!=1 ){
179 #ifdef __MINGW32__
180 return mkdir(zName);
181 #else
182 return mkdir(zName, 0755);
183 #endif
184 }
@@ -231,11 +234,11 @@
231 ** Changes are made in-place. Return the new name length.
232 */
233 int file_simplify_name(char *z, int n){
234 int i, j;
235 if( n<0 ) n = strlen(z);
236 #ifdef __MINGW32__
237 for(i=0; i<n; i++){
238 if( z[i]=='\\' ) z[i] = '/';
239 }
240 #endif
241 while( n>1 && z[n-1]=='/' ){ n--; }
@@ -266,11 +269,11 @@
266 ** Remove all /./ path elements.
267 ** Convert /A/../ to just /
268 */
269 void file_canonical_name(const char *zOrigName, Blob *pOut){
270 if( zOrigName[0]=='/'
271 #ifdef __MINGW32__
272 || zOrigName[0]=='\\'
273 || (strlen(zOrigName)>3 && zOrigName[1]==':'
274 && (zOrigName[2]=='\\' || zOrigName[2]=='/'))
275 #endif
276 ){
@@ -311,11 +314,11 @@
311 ** contain no "/./" or "/../" terms.
312 */
313 int file_is_canonical(const char *z){
314 int i;
315 if( z[0]!='/'
316 #ifdef __MINGW32__
317 && (z[0]==0 || z[1]!=':' || z[2]!='/')
318 #endif
319 ) return 0;
320
321 for(i=0; z[i]; i++){
322
--- src/file.c
+++ src/file.c
@@ -83,11 +83,14 @@
83 ** Return TRUE if the named file is an executable. Return false
84 ** for directories, devices, fifos, symlinks, etc.
85 */
86 int file_isexe(const char *zFilename){
87 if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
88 #if defined(_WIN32)
89 # if defined(__DMC__) || defined(_MSC_VER)
90 # define S_IXUSR _S_IEXEC
91 # endif
92 return ((S_IXUSR)&fileStat.st_mode)!=0;
93 #else
94 return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
95 #endif
96 }
@@ -145,11 +148,11 @@
148
149 /*
150 ** Set or clear the execute bit on a file.
151 */
152 void file_setexe(const char *zFilename, int onoff){
153 #if !defined(_WIN32)
154 struct stat buf;
155 if( stat(zFilename, &buf)!=0 ) return;
156 if( onoff ){
157 if( (buf.st_mode & 0111)!=0111 ){
158 chmod(zFilename, buf.st_mode | 0111);
@@ -157,11 +160,11 @@
160 }else{
161 if( (buf.st_mode & 0111)!=0 ){
162 chmod(zFilename, buf.st_mode & ~0111);
163 }
164 }
165 #endif /* _WIN32 */
166 }
167
168 /*
169 ** Create the directory named in the argument, if it does not already
170 ** exist. If forceFlag is 1, delete any prior non-directory object
@@ -174,11 +177,11 @@
177 if( rc==2 ){
178 if( !forceFlag ) return 1;
179 unlink(zName);
180 }
181 if( rc!=1 ){
182 #if defined(_WIN32)
183 return mkdir(zName);
184 #else
185 return mkdir(zName, 0755);
186 #endif
187 }
@@ -231,11 +234,11 @@
234 ** Changes are made in-place. Return the new name length.
235 */
236 int file_simplify_name(char *z, int n){
237 int i, j;
238 if( n<0 ) n = strlen(z);
239 #if defined(_WIN32)
240 for(i=0; i<n; i++){
241 if( z[i]=='\\' ) z[i] = '/';
242 }
243 #endif
244 while( n>1 && z[n-1]=='/' ){ n--; }
@@ -266,11 +269,11 @@
269 ** Remove all /./ path elements.
270 ** Convert /A/../ to just /
271 */
272 void file_canonical_name(const char *zOrigName, Blob *pOut){
273 if( zOrigName[0]=='/'
274 #if defined(_WIN32)
275 || zOrigName[0]=='\\'
276 || (strlen(zOrigName)>3 && zOrigName[1]==':'
277 && (zOrigName[2]=='\\' || zOrigName[2]=='/'))
278 #endif
279 ){
@@ -311,11 +314,11 @@
314 ** contain no "/./" or "/../" terms.
315 */
316 int file_is_canonical(const char *z){
317 int i;
318 if( z[0]!='/'
319 #if defined(_WIN32)
320 && (z[0]==0 || z[1]!=':' || z[2]!='/')
321 #endif
322 ) return 0;
323
324 for(i=0; z[i]; i++){
325
+11 -9
--- src/finfo.c
+++ src/finfo.c
@@ -136,11 +136,11 @@
136136
hyperlinked_path(zFilename, &title);
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>
141
- @ <table cellspacing=0 border=0 cellpadding=0>
141
+ @ <table class="timelineTable">
142142
while( db_step(&q)==SQLITE_ROW ){
143143
const char *zDate = db_column_text(&q, 0);
144144
const char *zCom = db_column_text(&q, 1);
145145
const char *zUser = db_column_text(&q, 2);
146146
int fpid = db_column_int(&q, 3);
@@ -157,22 +157,22 @@
157157
if( zBr==0 ) zBr = "trunk";
158158
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
159159
if( memcmp(zDate, zPrevDate, 10) ){
160160
sprintf(zPrevDate, "%.10s", zDate);
161161
@ <tr><td>
162
- @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
162
+ @ <div class="divider">%s(zPrevDate)</div>
163163
@ </td></tr>
164164
}
165165
memcpy(zTime, &zDate[11], 5);
166166
zTime[5] = 0;
167
- @ <tr><td valign="top" align="right">
167
+ @ <tr><td class="timelineTime">
168168
@ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
169
- @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
169
+ @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
170170
if( zBgClr && zBgClr[0] ){
171
- @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
171
+ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
172172
}else{
173
- @ <td valign="top" align="left">
173
+ @ <td class="timelineTableCell">
174174
}
175175
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
176176
sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
177177
if( zUuid ){
178178
if( g.okHistory ){
@@ -187,27 +187,29 @@
187187
hyperlink_to_uuid(zShortCkin);
188188
@ %h(zCom) (user:
189189
hyperlink_to_user(zUser, zDate, "");
190190
@ branch: %h(zBr))
191191
if( g.okHistory && zUuid ){
192
+ const char *z = zFilename;
192193
if( fpid ){
193194
@ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&amp;v2=%s(zUuid)">[diff]</a>
194195
}
195
- @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(zFilename)">
196
+ @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(z)">
196197
@ [annotate]</a>
197198
}
198
- @ </td>
199
+ @ </td></tr>
199200
}
200201
db_finalize(&q);
201202
if( pGraph ){
202203
graph_finish(pGraph, 1);
203204
if( pGraph->nErr ){
204205
graph_free(pGraph);
205206
pGraph = 0;
206207
}else{
207
- @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
208
+ @ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
209
+ @ </td></tr>
208210
}
209211
}
210212
@ </table>
211213
timeline_output_graph_javascript(pGraph);
212214
style_footer();
213215
}
214216
--- src/finfo.c
+++ src/finfo.c
@@ -136,11 +136,11 @@
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 cellspacing=0 border=0 cellpadding=0>
142 while( db_step(&q)==SQLITE_ROW ){
143 const char *zDate = db_column_text(&q, 0);
144 const char *zCom = db_column_text(&q, 1);
145 const char *zUser = db_column_text(&q, 2);
146 int fpid = db_column_int(&q, 3);
@@ -157,22 +157,22 @@
157 if( zBr==0 ) zBr = "trunk";
158 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
159 if( memcmp(zDate, zPrevDate, 10) ){
160 sprintf(zPrevDate, "%.10s", zDate);
161 @ <tr><td>
162 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
163 @ </td></tr>
164 }
165 memcpy(zTime, &zDate[11], 5);
166 zTime[5] = 0;
167 @ <tr><td valign="top" align="right">
168 @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
169 @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
170 if( zBgClr && zBgClr[0] ){
171 @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
172 }else{
173 @ <td valign="top" align="left">
174 }
175 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
176 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
177 if( zUuid ){
178 if( g.okHistory ){
@@ -187,27 +187,29 @@
187 hyperlink_to_uuid(zShortCkin);
188 @ %h(zCom) (user:
189 hyperlink_to_user(zUser, zDate, "");
190 @ branch: %h(zBr))
191 if( g.okHistory && zUuid ){
 
192 if( fpid ){
193 @ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&amp;v2=%s(zUuid)">[diff]</a>
194 }
195 @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(zFilename)">
196 @ [annotate]</a>
197 }
198 @ </td>
199 }
200 db_finalize(&q);
201 if( pGraph ){
202 graph_finish(pGraph, 1);
203 if( pGraph->nErr ){
204 graph_free(pGraph);
205 pGraph = 0;
206 }else{
207 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
 
208 }
209 }
210 @ </table>
211 timeline_output_graph_javascript(pGraph);
212 style_footer();
213 }
214
--- src/finfo.c
+++ src/finfo.c
@@ -136,11 +136,11 @@
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 while( db_step(&q)==SQLITE_ROW ){
143 const char *zDate = db_column_text(&q, 0);
144 const char *zCom = db_column_text(&q, 1);
145 const char *zUser = db_column_text(&q, 2);
146 int fpid = db_column_int(&q, 3);
@@ -157,22 +157,22 @@
157 if( zBr==0 ) zBr = "trunk";
158 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
159 if( memcmp(zDate, zPrevDate, 10) ){
160 sprintf(zPrevDate, "%.10s", zDate);
161 @ <tr><td>
162 @ <div class="divider">%s(zPrevDate)</div>
163 @ </td></tr>
164 }
165 memcpy(zTime, &zDate[11], 5);
166 zTime[5] = 0;
167 @ <tr><td class="timelineTime">
168 @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
169 @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
170 if( zBgClr && zBgClr[0] ){
171 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
172 }else{
173 @ <td class="timelineTableCell">
174 }
175 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
176 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
177 if( zUuid ){
178 if( g.okHistory ){
@@ -187,27 +187,29 @@
187 hyperlink_to_uuid(zShortCkin);
188 @ %h(zCom) (user:
189 hyperlink_to_user(zUser, zDate, "");
190 @ branch: %h(zBr))
191 if( g.okHistory && zUuid ){
192 const char *z = zFilename;
193 if( fpid ){
194 @ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&amp;v2=%s(zUuid)">[diff]</a>
195 }
196 @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(z)">
197 @ [annotate]</a>
198 }
199 @ </td></tr>
200 }
201 db_finalize(&q);
202 if( pGraph ){
203 graph_finish(pGraph, 1);
204 if( pGraph->nErr ){
205 graph_free(pGraph);
206 pGraph = 0;
207 }else{
208 @ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
209 @ </td></tr>
210 }
211 }
212 @ </table>
213 timeline_output_graph_javascript(pGraph);
214 style_footer();
215 }
216
+104 -58
--- src/graph.c
+++ src/graph.c
@@ -21,12 +21,12 @@
2121
#include "graph.h"
2222
#include <assert.h>
2323
2424
#if INTERFACE
2525
26
-#define GR_MAX_PARENT 10
27
-#define GR_MAX_RAIL 32
26
+#define GR_MAX_PARENT 10 /* Most parents for any one node */
27
+#define GR_MAX_RAIL 32 /* Max number of "rails" to display */
2828
2929
/* The graph appears vertically beside a timeline. Each row in the
3030
** timeline corresponds to a row in the graph.
3131
*/
3232
struct GraphRow {
@@ -38,11 +38,12 @@
3838
3939
GraphRow *pNext; /* Next row down in the list of all rows */
4040
GraphRow *pPrev; /* Previous row */
4141
4242
int idx; /* Row index. First is 1. 0 used for "none" */
43
- u8 isLeaf; /* True if no direct child nodes */
43
+ int idxTop; /* Direct descendent highest up on the graph */
44
+ GraphRow *pChild; /* Child immediately above this node */
4445
u8 isDup; /* True if this is duplicate of a prior entry */
4546
int iRail; /* Which rail this check-in appears on. 0-based.*/
4647
int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
4748
int bDescender; /* Raiser from bottom of graph to here. */
4849
u32 mergeIn; /* Merge in from other rails */
@@ -53,20 +54,19 @@
5354
};
5455
5556
/* Context while building a graph
5657
*/
5758
struct GraphContext {
58
- int nErr; /* Number of errors encountered */
59
- int mxRail; /* Number of rails required to render the graph */
60
- GraphRow *pFirst; /* First row in the list */
61
- GraphRow *pLast; /* Last row in the list */
62
- int nBranch; /* Number of distinct branches */
63
- char **azBranch; /* Names of the branches */
59
+ int nErr; /* Number of errors encountered */
60
+ int mxRail; /* Number of rails required to render the graph */
61
+ GraphRow *pFirst; /* First row in the list */
62
+ GraphRow *pLast; /* Last row in the list */
63
+ int nBranch; /* Number of distinct branches */
64
+ char **azBranch; /* Names of the branches */
6465
int nRow; /* Number of rows */
65
- int railMap[GR_MAX_RAIL]; /* Rail order mapping */
6666
int nHash; /* Number of slots in apHash[] */
67
- GraphRow **apHash; /* Hash table of rows */
67
+ GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
6868
};
6969
7070
#endif
7171
7272
/*
@@ -103,13 +103,13 @@
103103
free(p->apHash);
104104
free(p);
105105
}
106106
107107
/*
108
-** Insert a row into the hash table. If there is already another
109
-** row with the same rid, overwrite the prior entry if the overwrite
110
-** flag is set.
108
+** Insert a row into the hash table. pRow->rid is the key. Keys must
109
+** be unique. If there is already another row with the same rid,
110
+** overwrite the prior entry if and only if the overwrite flag is set.
111111
*/
112112
static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){
113113
int h;
114114
h = pRow->rid % p->nHash;
115115
while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
@@ -135,10 +135,14 @@
135135
136136
/*
137137
** Return the canonical pointer for a given branch name.
138138
** Multiple calls to this routine with equivalent strings
139139
** will return the same pointer.
140
+**
141
+** The returned value is a pointer to a (readonly) string that
142
+** has the useful property that strings can be checked for
143
+** equality by comparing pointers.
140144
**
141145
** Note: also used for background color names.
142146
*/
143147
static char *persistBranchName(GraphContext *p, const char *zBranch){
144148
int i;
@@ -151,11 +155,11 @@
151155
p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
152156
return p->azBranch[p->nBranch-1];
153157
}
154158
155159
/*
156
-** Add a new row t the graph context. Rows are added from top to bottom.
160
+** Add a new row to the graph context. Rows are added from top to bottom.
157161
*/
158162
int graph_add_row(
159163
GraphContext *p, /* The context to which the row is added */
160164
int rid, /* RID for the check-in */
161165
int nParent, /* Number of parents */
@@ -179,11 +183,11 @@
179183
}else{
180184
p->pLast->pNext = pRow;
181185
}
182186
p->pLast = pRow;
183187
p->nRow++;
184
- pRow->idx = p->nRow;
188
+ pRow->idx = pRow->idxTop = p->nRow;
185189
return pRow->idx;
186190
}
187191
188192
/*
189193
** Return the index of a rail currently not in use for any row between
@@ -219,16 +223,46 @@
219223
}
220224
}
221225
if( iBestDist>1000 ) p->nErr++;
222226
return iBest;
223227
}
228
+
229
+/*
230
+** Assign all children of node pBottom to the same rail as pBottom.
231
+*/
232
+static void assignChildrenToRail(GraphRow *pBottom){
233
+ int iRail = pBottom->iRail;
234
+ GraphRow *pCurrent;
235
+ GraphRow *pPrior;
236
+ u32 mask = 1<<iRail;
237
+
238
+ pBottom->iRail = iRail;
239
+ pBottom->railInUse |= mask;
240
+ pPrior = pBottom;
241
+ for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){
242
+ assert( pPrior->idx > pCurrent->idx );
243
+ assert( pCurrent->iRail<0 );
244
+ pCurrent->iRail = iRail;
245
+ pCurrent->railInUse |= mask;
246
+ pPrior->aiRaiser[iRail] = pCurrent->idx;
247
+ while( pPrior->idx > pCurrent->idx ){
248
+ pPrior->railInUse |= mask;
249
+ pPrior = pPrior->pPrev;
250
+ assert( pPrior!=0 );
251
+ }
252
+ if( pCurrent->pPrev ){
253
+ pCurrent->pPrev->railInUse |= mask;
254
+ }
255
+ }
256
+}
257
+
224258
225259
/*
226260
** Compute the complete graph
227261
*/
228262
void graph_finish(GraphContext *p, int omitDescenders){
229
- GraphRow *pRow, *pDesc, *pDup, *pLoop;
263
+ GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
230264
int i;
231265
u32 mask;
232266
u32 inUse;
233267
int hasDup = 0; /* True if one or more isDup entries */
234268
const char *zTrunk;
@@ -248,11 +282,17 @@
248282
}
249283
hashInsert(p, pRow, 1);
250284
}
251285
p->mxRail = -1;
252286
253
- /* Purge merge-parents that are out-of-graph
287
+ /* Purge merge-parents that are out-of-graph.
288
+ **
289
+ ** Each node has one primary parent and zero or more "merge" parents.
290
+ ** A merge parent is a prior checkin from which changes were merged into
291
+ ** the current check-in. If a merge parent is not in the visible section
292
+ ** of this graph, then no arrows will be drawn for it, so remove it from
293
+ ** the aParent[] array.
254294
*/
255295
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
256296
for(i=1; i<pRow->nParent; i++){
257297
if( hashFind(p, pRow->aParent[i])==0 ){
258298
pRow->aParent[i] = pRow->aParent[--pRow->nParent];
@@ -259,99 +299,106 @@
259299
i--;
260300
}
261301
}
262302
}
263303
264
- /* Figure out which nodes have no direct children (children on
265
- ** the same rail). Mark such nodes as isLeaf.
304
+ /* Find the pChild pointer for each node.
305
+ **
306
+ ** The pChild points to node directly above on the same rail.
307
+ ** The pChild must be in the same branch. Leaf nodes have a NULL
308
+ ** pChild.
309
+ **
310
+ ** In the case of a fork, choose the pChild that results in the
311
+ ** longest rail.
266312
*/
267
- memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
268
- for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
269
- for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
270
- GraphRow *pParent;
271
- hashInsert(p, pRow, 0);
272
- if( !pRow->isDup
273
- && pRow->nParent>0
274
- && (pParent = hashFind(p, pRow->aParent[0]))!=0
275
- && pRow->zBranch==pParent->zBranch
276
- ){
277
- pParent->isLeaf = 0;
313
+ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
314
+ if( pRow->isDup ) continue;
315
+ if( pRow->nParent==0 ) continue;
316
+ pParent = hashFind(p, pRow->aParent[0]);
317
+ if( pParent==0 ) continue;
318
+ if( pParent->zBranch!=pRow->zBranch ) continue;
319
+ if( pParent->idx <= pRow->idx ) continue;
320
+ if( pRow->idxTop < pParent->idxTop ){
321
+ pParent->pChild = pRow;
322
+ pParent->idxTop = pRow->idxTop;
278323
}
279324
}
280325
281326
/* Identify rows where the primary parent is off screen. Assign
282327
** each to a rail and draw descenders to the bottom of the screen.
328
+ **
329
+ ** Strive to put the "trunk" branch on far left.
283330
*/
284331
zTrunk = persistBranchName(p, "trunk");
285332
for(i=0; i<2; i++){
286
- for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
333
+ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
287334
if( i==0 ){
288335
if( pRow->zBranch!=zTrunk ) continue;
289336
}else {
290337
if( pRow->iRail>=0 ) continue;
291338
}
292339
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
293340
if( omitDescenders ){
294
- pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
341
+ pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0, 0);
295342
}else{
296343
pRow->iRail = ++p->mxRail;
297344
}
298345
mask = 1<<(pRow->iRail);
299346
if( omitDescenders ){
300347
if( pRow->pNext ) pRow->pNext->railInUse |= mask;
301
- for(pDesc=pRow; pDesc; pDesc=pDesc->pPrev){
302
- pDesc->railInUse |= mask;
303
- if( pDesc->zBranch==pRow->zBranch && pDesc->isLeaf ) break;
304
- }
305348
}else{
306349
pRow->bDescender = pRow->nParent>0;
307
- for(pDesc=pRow; pDesc; pDesc=pDesc->pNext){
308
- pDesc->railInUse |= mask;
350
+ for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
351
+ pLoop->railInUse |= mask;
309352
}
310353
}
354
+ assignChildrenToRail(pRow);
311355
}
312356
}
313357
}
314358
315359
/* Assign rails to all rows that are still unassigned.
316
- ** The first primary child of a row goes on the same rail as
317
- ** that row.
318360
*/
319361
inUse = (1<<(p->mxRail+1))-1;
320362
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
321363
int parentRid;
322
- if( pRow->iRail>=0 ) continue;
364
+
365
+ if( pRow->iRail>=0 ){
366
+ if( pRow->pChild==0 ) inUse &= ~(1<<pRow->iRail);
367
+ continue;
368
+ }
323369
if( pRow->isDup ){
324370
pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
325371
pDesc = pRow;
372
+ pParent = 0;
326373
}else{
327374
assert( pRow->nParent>0 );
328375
parentRid = pRow->aParent[0];
329
- pDesc = hashFind(p, parentRid);
330
- if( pDesc==0 ){
376
+ pParent = hashFind(p, parentRid);
377
+ if( pParent==0 ){
331378
/* Time skew */
332379
pRow->iRail = ++p->mxRail;
333380
pRow->railInUse = 1<<pRow->iRail;
334381
continue;
335382
}
336
- if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){
337
- pRow->iRail = pDesc->iRail;
338
- }else{
339
- pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, pDesc->iRail);
340
- }
341
- pDesc->aiRaiser[pRow->iRail] = pRow->idx;
383
+ pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail);
384
+ pParent->aiRaiser[pRow->iRail] = pRow->idx;
342385
}
343386
mask = 1<<pRow->iRail;
344
- if( pRow->isLeaf ){
387
+ if( pRow->pPrev ) pRow->pPrev->railInUse |= mask;
388
+ if( pRow->pNext ) pRow->pNext->railInUse |= mask;
389
+ if( pRow->pChild==0 ){
345390
inUse &= ~mask;
346391
}else{
347392
inUse |= mask;
393
+ assignChildrenToRail(pRow);
348394
}
349
- for(pLoop=pRow; pLoop && pLoop!=pDesc; pLoop=pLoop->pNext){
350
- pLoop->railInUse |= mask;
395
+ if( pParent ){
396
+ for(pLoop=pParent; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
397
+ pLoop->railInUse |= mask;
398
+ }
351399
}
352
- pDesc->railInUse |= mask;
353400
}
354401
355402
/*
356403
** Insert merge rails and merge arrows
357404
*/
@@ -364,13 +411,13 @@
364411
int iTarget = (pRow->iRail + pDesc->iRail)/2;
365412
pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
366413
pDesc->mergeUpto = pRow->idx;
367414
mask = 1<<pDesc->mergeOut;
368415
pDesc->railInUse |= mask;
369
- for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid;
370
- pDesc=pDesc->pNext){
371
- pDesc->railInUse |= mask;
416
+ for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid;
417
+ pLoop=pLoop->pNext){
418
+ pLoop->railInUse |= mask;
372419
}
373420
}
374421
pRow->mergeIn |= 1<<pDesc->mergeOut;
375422
}
376423
}
@@ -398,12 +445,11 @@
398445
}
399446
400447
/*
401448
** Find the maximum rail number.
402449
*/
403
- for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
404450
p->mxRail = 0;
405451
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
406452
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
407453
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
408454
}
409455
}
410456
--- src/graph.c
+++ src/graph.c
@@ -21,12 +21,12 @@
21 #include "graph.h"
22 #include <assert.h>
23
24 #if INTERFACE
25
26 #define GR_MAX_PARENT 10
27 #define GR_MAX_RAIL 32
28
29 /* The graph appears vertically beside a timeline. Each row in the
30 ** timeline corresponds to a row in the graph.
31 */
32 struct GraphRow {
@@ -38,11 +38,12 @@
38
39 GraphRow *pNext; /* Next row down in the list of all rows */
40 GraphRow *pPrev; /* Previous row */
41
42 int idx; /* Row index. First is 1. 0 used for "none" */
43 u8 isLeaf; /* True if no direct child nodes */
 
44 u8 isDup; /* True if this is duplicate of a prior entry */
45 int iRail; /* Which rail this check-in appears on. 0-based.*/
46 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
47 int bDescender; /* Raiser from bottom of graph to here. */
48 u32 mergeIn; /* Merge in from other rails */
@@ -53,20 +54,19 @@
53 };
54
55 /* Context while building a graph
56 */
57 struct GraphContext {
58 int nErr; /* Number of errors encountered */
59 int mxRail; /* Number of rails required to render the graph */
60 GraphRow *pFirst; /* First row in the list */
61 GraphRow *pLast; /* Last row in the list */
62 int nBranch; /* Number of distinct branches */
63 char **azBranch; /* Names of the branches */
64 int nRow; /* Number of rows */
65 int railMap[GR_MAX_RAIL]; /* Rail order mapping */
66 int nHash; /* Number of slots in apHash[] */
67 GraphRow **apHash; /* Hash table of rows */
68 };
69
70 #endif
71
72 /*
@@ -103,13 +103,13 @@
103 free(p->apHash);
104 free(p);
105 }
106
107 /*
108 ** Insert a row into the hash table. If there is already another
109 ** row with the same rid, overwrite the prior entry if the overwrite
110 ** flag is set.
111 */
112 static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){
113 int h;
114 h = pRow->rid % p->nHash;
115 while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
@@ -135,10 +135,14 @@
135
136 /*
137 ** Return the canonical pointer for a given branch name.
138 ** Multiple calls to this routine with equivalent strings
139 ** will return the same pointer.
 
 
 
 
140 **
141 ** Note: also used for background color names.
142 */
143 static char *persistBranchName(GraphContext *p, const char *zBranch){
144 int i;
@@ -151,11 +155,11 @@
151 p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
152 return p->azBranch[p->nBranch-1];
153 }
154
155 /*
156 ** Add a new row t the graph context. Rows are added from top to bottom.
157 */
158 int graph_add_row(
159 GraphContext *p, /* The context to which the row is added */
160 int rid, /* RID for the check-in */
161 int nParent, /* Number of parents */
@@ -179,11 +183,11 @@
179 }else{
180 p->pLast->pNext = pRow;
181 }
182 p->pLast = pRow;
183 p->nRow++;
184 pRow->idx = p->nRow;
185 return pRow->idx;
186 }
187
188 /*
189 ** Return the index of a rail currently not in use for any row between
@@ -219,16 +223,46 @@
219 }
220 }
221 if( iBestDist>1000 ) p->nErr++;
222 return iBest;
223 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
225 /*
226 ** Compute the complete graph
227 */
228 void graph_finish(GraphContext *p, int omitDescenders){
229 GraphRow *pRow, *pDesc, *pDup, *pLoop;
230 int i;
231 u32 mask;
232 u32 inUse;
233 int hasDup = 0; /* True if one or more isDup entries */
234 const char *zTrunk;
@@ -248,11 +282,17 @@
248 }
249 hashInsert(p, pRow, 1);
250 }
251 p->mxRail = -1;
252
253 /* Purge merge-parents that are out-of-graph
 
 
 
 
 
 
254 */
255 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
256 for(i=1; i<pRow->nParent; i++){
257 if( hashFind(p, pRow->aParent[i])==0 ){
258 pRow->aParent[i] = pRow->aParent[--pRow->nParent];
@@ -259,99 +299,106 @@
259 i--;
260 }
261 }
262 }
263
264 /* Figure out which nodes have no direct children (children on
265 ** the same rail). Mark such nodes as isLeaf.
 
 
 
 
 
 
266 */
267 memset(p->apHash, 0, sizeof(p->apHash[0])*p->nHash);
268 for(pRow=p->pLast; pRow; pRow=pRow->pPrev) pRow->isLeaf = 1;
269 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
270 GraphRow *pParent;
271 hashInsert(p, pRow, 0);
272 if( !pRow->isDup
273 && pRow->nParent>0
274 && (pParent = hashFind(p, pRow->aParent[0]))!=0
275 && pRow->zBranch==pParent->zBranch
276 ){
277 pParent->isLeaf = 0;
278 }
279 }
280
281 /* Identify rows where the primary parent is off screen. Assign
282 ** each to a rail and draw descenders to the bottom of the screen.
 
 
283 */
284 zTrunk = persistBranchName(p, "trunk");
285 for(i=0; i<2; i++){
286 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
287 if( i==0 ){
288 if( pRow->zBranch!=zTrunk ) continue;
289 }else {
290 if( pRow->iRail>=0 ) continue;
291 }
292 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
293 if( omitDescenders ){
294 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, 0, 0);
295 }else{
296 pRow->iRail = ++p->mxRail;
297 }
298 mask = 1<<(pRow->iRail);
299 if( omitDescenders ){
300 if( pRow->pNext ) pRow->pNext->railInUse |= mask;
301 for(pDesc=pRow; pDesc; pDesc=pDesc->pPrev){
302 pDesc->railInUse |= mask;
303 if( pDesc->zBranch==pRow->zBranch && pDesc->isLeaf ) break;
304 }
305 }else{
306 pRow->bDescender = pRow->nParent>0;
307 for(pDesc=pRow; pDesc; pDesc=pDesc->pNext){
308 pDesc->railInUse |= mask;
309 }
310 }
 
311 }
312 }
313 }
314
315 /* Assign rails to all rows that are still unassigned.
316 ** The first primary child of a row goes on the same rail as
317 ** that row.
318 */
319 inUse = (1<<(p->mxRail+1))-1;
320 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
321 int parentRid;
322 if( pRow->iRail>=0 ) continue;
 
 
 
 
323 if( pRow->isDup ){
324 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
325 pDesc = pRow;
 
326 }else{
327 assert( pRow->nParent>0 );
328 parentRid = pRow->aParent[0];
329 pDesc = hashFind(p, parentRid);
330 if( pDesc==0 ){
331 /* Time skew */
332 pRow->iRail = ++p->mxRail;
333 pRow->railInUse = 1<<pRow->iRail;
334 continue;
335 }
336 if( pDesc->aiRaiser[pDesc->iRail]==0 && pDesc->zBranch==pRow->zBranch ){
337 pRow->iRail = pDesc->iRail;
338 }else{
339 pRow->iRail = findFreeRail(p, 0, pDesc->idx, inUse, pDesc->iRail);
340 }
341 pDesc->aiRaiser[pRow->iRail] = pRow->idx;
342 }
343 mask = 1<<pRow->iRail;
344 if( pRow->isLeaf ){
 
 
345 inUse &= ~mask;
346 }else{
347 inUse |= mask;
 
348 }
349 for(pLoop=pRow; pLoop && pLoop!=pDesc; pLoop=pLoop->pNext){
350 pLoop->railInUse |= mask;
 
 
351 }
352 pDesc->railInUse |= mask;
353 }
354
355 /*
356 ** Insert merge rails and merge arrows
357 */
@@ -364,13 +411,13 @@
364 int iTarget = (pRow->iRail + pDesc->iRail)/2;
365 pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
366 pDesc->mergeUpto = pRow->idx;
367 mask = 1<<pDesc->mergeOut;
368 pDesc->railInUse |= mask;
369 for(pDesc=pRow->pNext; pDesc && pDesc->rid!=parentRid;
370 pDesc=pDesc->pNext){
371 pDesc->railInUse |= mask;
372 }
373 }
374 pRow->mergeIn |= 1<<pDesc->mergeOut;
375 }
376 }
@@ -398,12 +445,11 @@
398 }
399
400 /*
401 ** Find the maximum rail number.
402 */
403 for(i=0; i<GR_MAX_RAIL; i++) p->railMap[i] = i;
404 p->mxRail = 0;
405 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
406 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
407 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
408 }
409 }
410
--- src/graph.c
+++ src/graph.c
@@ -21,12 +21,12 @@
21 #include "graph.h"
22 #include <assert.h>
23
24 #if INTERFACE
25
26 #define GR_MAX_PARENT 10 /* Most parents for any one node */
27 #define GR_MAX_RAIL 32 /* Max number of "rails" to display */
28
29 /* The graph appears vertically beside a timeline. Each row in the
30 ** timeline corresponds to a row in the graph.
31 */
32 struct GraphRow {
@@ -38,11 +38,12 @@
38
39 GraphRow *pNext; /* Next row down in the list of all rows */
40 GraphRow *pPrev; /* Previous row */
41
42 int idx; /* Row index. First is 1. 0 used for "none" */
43 int idxTop; /* Direct descendent highest up on the graph */
44 GraphRow *pChild; /* Child immediately above this node */
45 u8 isDup; /* True if this is duplicate of a prior entry */
46 int iRail; /* Which rail this check-in appears on. 0-based.*/
47 int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */
48 int bDescender; /* Raiser from bottom of graph to here. */
49 u32 mergeIn; /* Merge in from other rails */
@@ -53,20 +54,19 @@
54 };
55
56 /* Context while building a graph
57 */
58 struct GraphContext {
59 int nErr; /* Number of errors encountered */
60 int mxRail; /* Number of rails required to render the graph */
61 GraphRow *pFirst; /* First row in the list */
62 GraphRow *pLast; /* Last row in the list */
63 int nBranch; /* Number of distinct branches */
64 char **azBranch; /* Names of the branches */
65 int nRow; /* Number of rows */
 
66 int nHash; /* Number of slots in apHash[] */
67 GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
68 };
69
70 #endif
71
72 /*
@@ -103,13 +103,13 @@
103 free(p->apHash);
104 free(p);
105 }
106
107 /*
108 ** Insert a row into the hash table. pRow->rid is the key. Keys must
109 ** be unique. If there is already another row with the same rid,
110 ** overwrite the prior entry if and only if the overwrite flag is set.
111 */
112 static void hashInsert(GraphContext *p, GraphRow *pRow, int overwrite){
113 int h;
114 h = pRow->rid % p->nHash;
115 while( p->apHash[h] && p->apHash[h]->rid!=pRow->rid ){
@@ -135,10 +135,14 @@
135
136 /*
137 ** Return the canonical pointer for a given branch name.
138 ** Multiple calls to this routine with equivalent strings
139 ** will return the same pointer.
140 **
141 ** The returned value is a pointer to a (readonly) string that
142 ** has the useful property that strings can be checked for
143 ** equality by comparing pointers.
144 **
145 ** Note: also used for background color names.
146 */
147 static char *persistBranchName(GraphContext *p, const char *zBranch){
148 int i;
@@ -151,11 +155,11 @@
155 p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
156 return p->azBranch[p->nBranch-1];
157 }
158
159 /*
160 ** Add a new row to the graph context. Rows are added from top to bottom.
161 */
162 int graph_add_row(
163 GraphContext *p, /* The context to which the row is added */
164 int rid, /* RID for the check-in */
165 int nParent, /* Number of parents */
@@ -179,11 +183,11 @@
183 }else{
184 p->pLast->pNext = pRow;
185 }
186 p->pLast = pRow;
187 p->nRow++;
188 pRow->idx = pRow->idxTop = p->nRow;
189 return pRow->idx;
190 }
191
192 /*
193 ** Return the index of a rail currently not in use for any row between
@@ -219,16 +223,46 @@
223 }
224 }
225 if( iBestDist>1000 ) p->nErr++;
226 return iBest;
227 }
228
229 /*
230 ** Assign all children of node pBottom to the same rail as pBottom.
231 */
232 static void assignChildrenToRail(GraphRow *pBottom){
233 int iRail = pBottom->iRail;
234 GraphRow *pCurrent;
235 GraphRow *pPrior;
236 u32 mask = 1<<iRail;
237
238 pBottom->iRail = iRail;
239 pBottom->railInUse |= mask;
240 pPrior = pBottom;
241 for(pCurrent=pBottom->pChild; pCurrent; pCurrent=pCurrent->pChild){
242 assert( pPrior->idx > pCurrent->idx );
243 assert( pCurrent->iRail<0 );
244 pCurrent->iRail = iRail;
245 pCurrent->railInUse |= mask;
246 pPrior->aiRaiser[iRail] = pCurrent->idx;
247 while( pPrior->idx > pCurrent->idx ){
248 pPrior->railInUse |= mask;
249 pPrior = pPrior->pPrev;
250 assert( pPrior!=0 );
251 }
252 if( pCurrent->pPrev ){
253 pCurrent->pPrev->railInUse |= mask;
254 }
255 }
256 }
257
258
259 /*
260 ** Compute the complete graph
261 */
262 void graph_finish(GraphContext *p, int omitDescenders){
263 GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
264 int i;
265 u32 mask;
266 u32 inUse;
267 int hasDup = 0; /* True if one or more isDup entries */
268 const char *zTrunk;
@@ -248,11 +282,17 @@
282 }
283 hashInsert(p, pRow, 1);
284 }
285 p->mxRail = -1;
286
287 /* Purge merge-parents that are out-of-graph.
288 **
289 ** Each node has one primary parent and zero or more "merge" parents.
290 ** A merge parent is a prior checkin from which changes were merged into
291 ** the current check-in. If a merge parent is not in the visible section
292 ** of this graph, then no arrows will be drawn for it, so remove it from
293 ** the aParent[] array.
294 */
295 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
296 for(i=1; i<pRow->nParent; i++){
297 if( hashFind(p, pRow->aParent[i])==0 ){
298 pRow->aParent[i] = pRow->aParent[--pRow->nParent];
@@ -259,99 +299,106 @@
299 i--;
300 }
301 }
302 }
303
304 /* Find the pChild pointer for each node.
305 **
306 ** The pChild points to node directly above on the same rail.
307 ** The pChild must be in the same branch. Leaf nodes have a NULL
308 ** pChild.
309 **
310 ** In the case of a fork, choose the pChild that results in the
311 ** longest rail.
312 */
313 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
314 if( pRow->isDup ) continue;
315 if( pRow->nParent==0 ) continue;
316 pParent = hashFind(p, pRow->aParent[0]);
317 if( pParent==0 ) continue;
318 if( pParent->zBranch!=pRow->zBranch ) continue;
319 if( pParent->idx <= pRow->idx ) continue;
320 if( pRow->idxTop < pParent->idxTop ){
321 pParent->pChild = pRow;
322 pParent->idxTop = pRow->idxTop;
 
323 }
324 }
325
326 /* Identify rows where the primary parent is off screen. Assign
327 ** each to a rail and draw descenders to the bottom of the screen.
328 **
329 ** Strive to put the "trunk" branch on far left.
330 */
331 zTrunk = persistBranchName(p, "trunk");
332 for(i=0; i<2; i++){
333 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
334 if( i==0 ){
335 if( pRow->zBranch!=zTrunk ) continue;
336 }else {
337 if( pRow->iRail>=0 ) continue;
338 }
339 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
340 if( omitDescenders ){
341 pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0, 0);
342 }else{
343 pRow->iRail = ++p->mxRail;
344 }
345 mask = 1<<(pRow->iRail);
346 if( omitDescenders ){
347 if( pRow->pNext ) pRow->pNext->railInUse |= mask;
 
 
 
 
348 }else{
349 pRow->bDescender = pRow->nParent>0;
350 for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
351 pLoop->railInUse |= mask;
352 }
353 }
354 assignChildrenToRail(pRow);
355 }
356 }
357 }
358
359 /* Assign rails to all rows that are still unassigned.
 
 
360 */
361 inUse = (1<<(p->mxRail+1))-1;
362 for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
363 int parentRid;
364
365 if( pRow->iRail>=0 ){
366 if( pRow->pChild==0 ) inUse &= ~(1<<pRow->iRail);
367 continue;
368 }
369 if( pRow->isDup ){
370 pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
371 pDesc = pRow;
372 pParent = 0;
373 }else{
374 assert( pRow->nParent>0 );
375 parentRid = pRow->aParent[0];
376 pParent = hashFind(p, parentRid);
377 if( pParent==0 ){
378 /* Time skew */
379 pRow->iRail = ++p->mxRail;
380 pRow->railInUse = 1<<pRow->iRail;
381 continue;
382 }
383 pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail);
384 pParent->aiRaiser[pRow->iRail] = pRow->idx;
 
 
 
 
385 }
386 mask = 1<<pRow->iRail;
387 if( pRow->pPrev ) pRow->pPrev->railInUse |= mask;
388 if( pRow->pNext ) pRow->pNext->railInUse |= mask;
389 if( pRow->pChild==0 ){
390 inUse &= ~mask;
391 }else{
392 inUse |= mask;
393 assignChildrenToRail(pRow);
394 }
395 if( pParent ){
396 for(pLoop=pParent; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
397 pLoop->railInUse |= mask;
398 }
399 }
 
400 }
401
402 /*
403 ** Insert merge rails and merge arrows
404 */
@@ -364,13 +411,13 @@
411 int iTarget = (pRow->iRail + pDesc->iRail)/2;
412 pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx, 0, iTarget);
413 pDesc->mergeUpto = pRow->idx;
414 mask = 1<<pDesc->mergeOut;
415 pDesc->railInUse |= mask;
416 for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid;
417 pLoop=pLoop->pNext){
418 pLoop->railInUse |= mask;
419 }
420 }
421 pRow->mergeIn |= 1<<pDesc->mergeOut;
422 }
423 }
@@ -398,12 +445,11 @@
445 }
446
447 /*
448 ** Find the maximum rail number.
449 */
 
450 p->mxRail = 0;
451 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
452 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
453 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
454 }
455 }
456
+21
--- src/http.c
+++ src/http.c
@@ -42,10 +42,13 @@
4242
4343
blob_zero(pLogin);
4444
if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
4545
return; /* If no login card for users "nobody" and "anonymous" */
4646
}
47
+ if( g.urlIsSsh ){
48
+ return; /* If no login card for SSH: */
49
+ }
4750
blob_zero(&nonce);
4851
blob_zero(&pw);
4952
sha1sum_blob(pPayload, &nonce);
5053
blob_copy(&pw, &nonce);
5154
zLogin = g.urlUser;
@@ -134,10 +137,11 @@
134137
int iLength; /* Length of the reply payload */
135138
int rc; /* Result code */
136139
int iHttpVersion; /* Which version of HTTP protocol server uses */
137140
char *zLine; /* A single line of the reply header */
138141
int i; /* Loop counter */
142
+ int isError = 0; /* True if the reply is an error message */
139143
140144
if( transport_open() ){
141145
fossil_fatal(transport_errmsg());
142146
}
143147
@@ -190,10 +194,11 @@
190194
** Read and interpret the server reply
191195
*/
192196
closeConnection = 1;
193197
iLength = -1;
194198
while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
199
+ /* printf("[%s]\n", zLine); fflush(stdout); */
195200
if( strncasecmp(zLine, "http/1.", 7)==0 ){
196201
if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
197202
if( rc!=200 && rc!=302 ){
198203
int ii;
199204
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
@@ -227,10 +232,12 @@
227232
fossil_print("redirect to %s\n", &zLine[i]);
228233
url_parse(&zLine[i]);
229234
transport_close();
230235
http_exchange(pSend, pReply, useLogin);
231236
return;
237
+ }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){
238
+ isError = 1;
232239
}
233240
}
234241
if( rc!=200 ){
235242
fossil_fatal("\"location:\" missing from 302 redirect reply");
236243
goto write_err;
@@ -245,10 +252,24 @@
245252
}
246253
blob_zero(pReply);
247254
blob_resize(pReply, iLength);
248255
iLength = transport_receive(blob_buffer(pReply), iLength);
249256
blob_resize(pReply, iLength);
257
+ if( isError ){
258
+ char *z;
259
+ int i, j;
260
+ z = blob_str(pReply);
261
+ for(i=j=0; z[i]; i++, j++){
262
+ if( z[i]=='<' ){
263
+ while( z[i] && z[i]!='>' ) i++;
264
+ if( z[i]==0 ) break;
265
+ }
266
+ z[j] = z[i];
267
+ }
268
+ z[j] = 0;
269
+ fossil_fatal("server sends error: %s", z);
270
+ }
250271
if( g.fHttpTrace ){
251272
printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
252273
}else{
253274
blob_uncompress(pReply, pReply);
254275
}
255276
--- src/http.c
+++ src/http.c
@@ -42,10 +42,13 @@
42
43 blob_zero(pLogin);
44 if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
45 return; /* If no login card for users "nobody" and "anonymous" */
46 }
 
 
 
47 blob_zero(&nonce);
48 blob_zero(&pw);
49 sha1sum_blob(pPayload, &nonce);
50 blob_copy(&pw, &nonce);
51 zLogin = g.urlUser;
@@ -134,10 +137,11 @@
134 int iLength; /* Length of the reply payload */
135 int rc; /* Result code */
136 int iHttpVersion; /* Which version of HTTP protocol server uses */
137 char *zLine; /* A single line of the reply header */
138 int i; /* Loop counter */
 
139
140 if( transport_open() ){
141 fossil_fatal(transport_errmsg());
142 }
143
@@ -190,10 +194,11 @@
190 ** Read and interpret the server reply
191 */
192 closeConnection = 1;
193 iLength = -1;
194 while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
 
195 if( strncasecmp(zLine, "http/1.", 7)==0 ){
196 if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
197 if( rc!=200 && rc!=302 ){
198 int ii;
199 for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
@@ -227,10 +232,12 @@
227 fossil_print("redirect to %s\n", &zLine[i]);
228 url_parse(&zLine[i]);
229 transport_close();
230 http_exchange(pSend, pReply, useLogin);
231 return;
 
 
232 }
233 }
234 if( rc!=200 ){
235 fossil_fatal("\"location:\" missing from 302 redirect reply");
236 goto write_err;
@@ -245,10 +252,24 @@
245 }
246 blob_zero(pReply);
247 blob_resize(pReply, iLength);
248 iLength = transport_receive(blob_buffer(pReply), iLength);
249 blob_resize(pReply, iLength);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250 if( g.fHttpTrace ){
251 printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pReply));
252 }else{
253 blob_uncompress(pReply, pReply);
254 }
255
--- src/http.c
+++ src/http.c
@@ -42,10 +42,13 @@
42
43 blob_zero(pLogin);
44 if( g.urlUser==0 || strcmp(g.urlUser, "anonymous")==0 ){
45 return; /* If no login card for users "nobody" and "anonymous" */
46 }
47 if( g.urlIsSsh ){
48 return; /* If no login card for SSH: */
49 }
50 blob_zero(&nonce);
51 blob_zero(&pw);
52 sha1sum_blob(pPayload, &nonce);
53 blob_copy(&pw, &nonce);
54 zLogin = g.urlUser;
@@ -134,10 +137,11 @@
137 int iLength; /* Length of the reply payload */
138 int rc; /* Result code */
139 int iHttpVersion; /* Which version of HTTP protocol server uses */
140 char *zLine; /* A single line of the reply header */
141 int i; /* Loop counter */
142 int isError = 0; /* True if the reply is an error message */
143
144 if( transport_open() ){
145 fossil_fatal(transport_errmsg());
146 }
147
@@ -190,10 +194,11 @@
194 ** Read and interpret the server reply
195 */
196 closeConnection = 1;
197 iLength = -1;
198 while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){
199 /* printf("[%s]\n", zLine); fflush(stdout); */
200 if( strncasecmp(zLine, "http/1.", 7)==0 ){
201 if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
202 if( rc!=200 && rc!=302 ){
203 int ii;
204 for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
@@ -227,10 +232,12 @@
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;
237 }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){
238 isError = 1;
239 }
240 }
241 if( rc!=200 ){
242 fossil_fatal("\"location:\" missing from 302 redirect reply");
243 goto write_err;
@@ -245,10 +252,24 @@
252 }
253 blob_zero(pReply);
254 blob_resize(pReply, iLength);
255 iLength = transport_receive(blob_buffer(pReply), iLength);
256 blob_resize(pReply, iLength);
257 if( isError ){
258 char *z;
259 int i, j;
260 z = blob_str(pReply);
261 for(i=j=0; z[i]; i++, j++){
262 if( z[i]=='<' ){
263 while( z[i] && z[i]!='>' ) i++;
264 if( z[i]==0 ) break;
265 }
266 z[j] = z[i];
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
--- src/http_socket.c
+++ src/http_socket.c
@@ -26,13 +26,16 @@
2626
** are handled different on Unix and windows.
2727
*/
2828
2929
#include "config.h"
3030
#include "http_socket.h"
31
-#ifdef __MINGW32__
32
-# include <windows.h>
33
-# include <winsock2.h>
31
+#if defined(_WIN32)
32
+# include <windows.h> /* for Sleep once server works again */
33
+# define sleep Sleep /* windows does not have sleep, but Sleep */
34
+# if defined(__MINGW32__)
35
+# include <ws2tcpip.h>
36
+# endif
3437
#else
3538
# include <arpa/inet.h>
3639
# include <sys/socket.h>
3740
# include <netdb.h>
3841
# include <netinet/in.h>
@@ -45,11 +48,11 @@
4548
** There can only be a single socket connection open at a time.
4649
** State information about that socket is stored in the following
4750
** local variables:
4851
*/
4952
static int socketIsInit = 0; /* True after global initialization */
50
-#ifdef __MINGW32__
53
+#if defined(_WIN32)
5154
static WSADATA socketInfo; /* Windows socket initialize data */
5255
#endif
5356
static int iSocket = -1; /* The socket on which we talk to the server */
5457
static char *socketErrMsg = 0; /* Text of most recent socket error */
5558
@@ -84,11 +87,11 @@
8487
** Call this routine once before any other use of the socket interface.
8588
** This routine does initial configuration of the socket module.
8689
*/
8790
void socket_global_init(void){
8891
if( socketIsInit==0 ){
89
-#ifdef __MINGW32__
92
+#if defined(_WIN32)
9093
if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){
9194
fossil_panic("can't initialize winsock");
9295
}
9396
#endif
9497
socketIsInit = 1;
@@ -99,11 +102,11 @@
99102
** Call this routine to shutdown the socket module prior to program
100103
** exit.
101104
*/
102105
void socket_global_shutdown(void){
103106
if( socketIsInit ){
104
-#ifdef __MINGW32__
107
+#if defined(_WIN32)
105108
WSACleanup();
106109
#endif
107110
socket_clear_errmsg();
108111
socketIsInit = 0;
109112
}
@@ -113,11 +116,11 @@
113116
** Close the currently open socket. If no socket is open, this routine
114117
** is a no-op.
115118
*/
116119
void socket_close(void){
117120
if( iSocket>=0 ){
118
-#ifdef __MINGW32__
121
+#if defined(_WIN32)
119122
closesocket(iSocket);
120123
#else
121124
close(iSocket);
122125
#endif
123126
iSocket = -1;
@@ -171,11 +174,11 @@
171174
if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
172175
socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
173176
socket_close();
174177
return 1;
175178
}
176
-#ifndef __MINGW32__
179
+#if !defined(_WIN32)
177180
signal(SIGPIPE, SIG_IGN);
178181
#endif
179182
return 0;
180183
}
181184
182185
--- src/http_socket.c
+++ src/http_socket.c
@@ -26,13 +26,16 @@
26 ** are handled different on Unix and windows.
27 */
28
29 #include "config.h"
30 #include "http_socket.h"
31 #ifdef __MINGW32__
32 # include <windows.h>
33 # include <winsock2.h>
 
 
 
34 #else
35 # include <arpa/inet.h>
36 # include <sys/socket.h>
37 # include <netdb.h>
38 # include <netinet/in.h>
@@ -45,11 +48,11 @@
45 ** There can only be a single socket connection open at a time.
46 ** State information about that socket is stored in the following
47 ** local variables:
48 */
49 static int socketIsInit = 0; /* True after global initialization */
50 #ifdef __MINGW32__
51 static WSADATA socketInfo; /* Windows socket initialize data */
52 #endif
53 static int iSocket = -1; /* The socket on which we talk to the server */
54 static char *socketErrMsg = 0; /* Text of most recent socket error */
55
@@ -84,11 +87,11 @@
84 ** Call this routine once before any other use of the socket interface.
85 ** This routine does initial configuration of the socket module.
86 */
87 void socket_global_init(void){
88 if( socketIsInit==0 ){
89 #ifdef __MINGW32__
90 if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){
91 fossil_panic("can't initialize winsock");
92 }
93 #endif
94 socketIsInit = 1;
@@ -99,11 +102,11 @@
99 ** Call this routine to shutdown the socket module prior to program
100 ** exit.
101 */
102 void socket_global_shutdown(void){
103 if( socketIsInit ){
104 #ifdef __MINGW32__
105 WSACleanup();
106 #endif
107 socket_clear_errmsg();
108 socketIsInit = 0;
109 }
@@ -113,11 +116,11 @@
113 ** Close the currently open socket. If no socket is open, this routine
114 ** is a no-op.
115 */
116 void socket_close(void){
117 if( iSocket>=0 ){
118 #ifdef __MINGW32__
119 closesocket(iSocket);
120 #else
121 close(iSocket);
122 #endif
123 iSocket = -1;
@@ -171,11 +174,11 @@
171 if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
172 socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
173 socket_close();
174 return 1;
175 }
176 #ifndef __MINGW32__
177 signal(SIGPIPE, SIG_IGN);
178 #endif
179 return 0;
180 }
181
182
--- src/http_socket.c
+++ src/http_socket.c
@@ -26,13 +26,16 @@
26 ** are handled different on Unix and windows.
27 */
28
29 #include "config.h"
30 #include "http_socket.h"
31 #if defined(_WIN32)
32 # include <windows.h> /* for Sleep once server works again */
33 # define sleep Sleep /* windows does not have sleep, but Sleep */
34 # if defined(__MINGW32__)
35 # include <ws2tcpip.h>
36 # endif
37 #else
38 # include <arpa/inet.h>
39 # include <sys/socket.h>
40 # include <netdb.h>
41 # include <netinet/in.h>
@@ -45,11 +48,11 @@
48 ** There can only be a single socket connection open at a time.
49 ** State information about that socket is stored in the following
50 ** local variables:
51 */
52 static int socketIsInit = 0; /* True after global initialization */
53 #if defined(_WIN32)
54 static WSADATA socketInfo; /* Windows socket initialize data */
55 #endif
56 static int iSocket = -1; /* The socket on which we talk to the server */
57 static char *socketErrMsg = 0; /* Text of most recent socket error */
58
@@ -84,11 +87,11 @@
87 ** Call this routine once before any other use of the socket interface.
88 ** This routine does initial configuration of the socket module.
89 */
90 void socket_global_init(void){
91 if( socketIsInit==0 ){
92 #if defined(_WIN32)
93 if( WSAStartup(MAKEWORD(2,0), &socketInfo)!=0 ){
94 fossil_panic("can't initialize winsock");
95 }
96 #endif
97 socketIsInit = 1;
@@ -99,11 +102,11 @@
102 ** Call this routine to shutdown the socket module prior to program
103 ** exit.
104 */
105 void socket_global_shutdown(void){
106 if( socketIsInit ){
107 #if defined(_WIN32)
108 WSACleanup();
109 #endif
110 socket_clear_errmsg();
111 socketIsInit = 0;
112 }
@@ -113,11 +116,11 @@
116 ** Close the currently open socket. If no socket is open, this routine
117 ** is a no-op.
118 */
119 void socket_close(void){
120 if( iSocket>=0 ){
121 #if defined(_WIN32)
122 closesocket(iSocket);
123 #else
124 close(iSocket);
125 #endif
126 iSocket = -1;
@@ -171,11 +174,11 @@
174 if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
175 socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
176 socket_close();
177 return 1;
178 }
179 #if !defined(_WIN32)
180 signal(SIGPIPE, SIG_IGN);
181 #endif
182 return 0;
183 }
184
185
+4 -4
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -128,11 +128,11 @@
128128
** Return the number of errors.
129129
*/
130130
int ssl_open(void){
131131
X509 *cert;
132132
int hasSavedCertificate = 0;
133
-
133
+char *connStr ;
134134
ssl_global_init();
135135
136136
/* Get certificate for current server from global config and
137137
* (if we have it in config) add it to certificate store.
138138
*/
@@ -150,11 +150,11 @@
150150
ssl_set_errmsg("SSL: cannot open SSL (%s)",
151151
ERR_reason_error_string(ERR_get_error()));
152152
return 1;
153153
}
154154
155
- char *connStr = mprintf("%s:%d", g.urlName, g.urlPort);
155
+ connStr = mprintf("%s:%d", g.urlName, g.urlPort);
156156
BIO_set_conn_hostname(iBio, connStr);
157157
free(connStr);
158158
159159
if( BIO_do_connect(iBio)<=0 ){
160160
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
@@ -178,29 +178,29 @@
178178
return 1;
179179
}
180180
181181
if( SSL_get_verify_result(ssl) != X509_V_OK ){
182182
char *desc, *prompt;
183
+ char *warning = "";
184
+ Blob ans;
183185
BIO *mem;
184186
185187
mem = BIO_new(BIO_s_mem());
186188
X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
187189
BIO_puts(mem, "\n\nIssued By:\n\n");
188190
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
189191
BIO_write(mem, "", 1); // null-terminate mem buffer
190192
BIO_get_mem_data(mem, &desc);
191193
192
- char *warning = "";
193194
if( hasSavedCertificate ){
194195
warning = "WARNING: Certificate doesn't match the "
195196
"saved certificate for this host!";
196197
}
197198
prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n"
198199
"Accept certificate [a=always/y/N]? ", desc, warning);
199200
BIO_free(mem);
200201
201
- Blob ans;
202202
prompt_user(prompt, &ans);
203203
free(prompt);
204204
if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) {
205205
X509_free(cert);
206206
ssl_set_errmsg("SSL certificate declined");
207207
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -128,11 +128,11 @@
128 ** Return the number of errors.
129 */
130 int ssl_open(void){
131 X509 *cert;
132 int hasSavedCertificate = 0;
133
134 ssl_global_init();
135
136 /* Get certificate for current server from global config and
137 * (if we have it in config) add it to certificate store.
138 */
@@ -150,11 +150,11 @@
150 ssl_set_errmsg("SSL: cannot open SSL (%s)",
151 ERR_reason_error_string(ERR_get_error()));
152 return 1;
153 }
154
155 char *connStr = mprintf("%s:%d", g.urlName, g.urlPort);
156 BIO_set_conn_hostname(iBio, connStr);
157 free(connStr);
158
159 if( BIO_do_connect(iBio)<=0 ){
160 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
@@ -178,29 +178,29 @@
178 return 1;
179 }
180
181 if( SSL_get_verify_result(ssl) != X509_V_OK ){
182 char *desc, *prompt;
 
 
183 BIO *mem;
184
185 mem = BIO_new(BIO_s_mem());
186 X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
187 BIO_puts(mem, "\n\nIssued By:\n\n");
188 X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
189 BIO_write(mem, "", 1); // null-terminate mem buffer
190 BIO_get_mem_data(mem, &desc);
191
192 char *warning = "";
193 if( hasSavedCertificate ){
194 warning = "WARNING: Certificate doesn't match the "
195 "saved certificate for this host!";
196 }
197 prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n"
198 "Accept certificate [a=always/y/N]? ", desc, warning);
199 BIO_free(mem);
200
201 Blob ans;
202 prompt_user(prompt, &ans);
203 free(prompt);
204 if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) {
205 X509_free(cert);
206 ssl_set_errmsg("SSL certificate declined");
207
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -128,11 +128,11 @@
128 ** Return the number of errors.
129 */
130 int ssl_open(void){
131 X509 *cert;
132 int hasSavedCertificate = 0;
133 char *connStr ;
134 ssl_global_init();
135
136 /* Get certificate for current server from global config and
137 * (if we have it in config) add it to certificate store.
138 */
@@ -150,11 +150,11 @@
150 ssl_set_errmsg("SSL: cannot open SSL (%s)",
151 ERR_reason_error_string(ERR_get_error()));
152 return 1;
153 }
154
155 connStr = mprintf("%s:%d", g.urlName, g.urlPort);
156 BIO_set_conn_hostname(iBio, connStr);
157 free(connStr);
158
159 if( BIO_do_connect(iBio)<=0 ){
160 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
@@ -178,29 +178,29 @@
178 return 1;
179 }
180
181 if( SSL_get_verify_result(ssl) != X509_V_OK ){
182 char *desc, *prompt;
183 char *warning = "";
184 Blob ans;
185 BIO *mem;
186
187 mem = BIO_new(BIO_s_mem());
188 X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
189 BIO_puts(mem, "\n\nIssued By:\n\n");
190 X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
191 BIO_write(mem, "", 1); // null-terminate mem buffer
192 BIO_get_mem_data(mem, &desc);
193
 
194 if( hasSavedCertificate ){
195 warning = "WARNING: Certificate doesn't match the "
196 "saved certificate for this host!";
197 }
198 prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n"
199 "Accept certificate [a=always/y/N]? ", desc, warning);
200 BIO_free(mem);
201
 
202 prompt_user(prompt, &ans);
203 free(prompt);
204 if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) {
205 X509_free(cert);
206 ssl_set_errmsg("SSL certificate declined");
207
--- src/http_transport.c
+++ src/http_transport.c
@@ -38,10 +38,19 @@
3838
char *zOutFile; /* Name of outbound file for FILE: */
3939
char *zInFile; /* Name of inbound file for FILE: */
4040
} transport = {
4141
0, 0, 0, 0, 0, 0, 0
4242
};
43
+
44
+/*
45
+** Information about the connection to the SSH subprocess when
46
+** using the ssh:// sync method.
47
+*/
48
+static int sshPid; /* Process id of ssh subprocess */
49
+static int sshIn; /* From ssh subprocess to this process */
50
+static FILE *sshOut; /* From this to ssh subprocess */
51
+
4352
4453
/*
4554
** Return the current transport error message.
4655
*/
4756
const char *transport_errmsg(void){
@@ -63,10 +72,102 @@
6372
if( resetFlag ){
6473
transport.nSent = 0;
6574
transport.nRcvd = 0;
6675
}
6776
}
77
+
78
+/*
79
+** Read text from sshIn. Zero-terminate and remove trailing
80
+** whitespace.
81
+*/
82
+static void sshin_read(char *zBuf, int szBuf){
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
+/*
94
+** Default SSH command
95
+*/
96
+#ifdef __MINGW32__
97
+static char zDefaultSshCmd[] = "ssh -T";
98
+#else
99
+static char zDefaultSshCmd[] = "ssh -e none -T";
100
+#endif
101
+
102
+/*
103
+** Global initialization of the transport layer
104
+*/
105
+void transport_global_startup(void){
106
+ if( g.urlIsSsh ){
107
+ /* Only SSH requires a global initialization. For SSH we need to create
108
+ ** and run an SSH command to talk to the remote machine.
109
+ */
110
+ const char *zSsh; /* The base SSH command */
111
+ Blob zCmd; /* The SSH command */
112
+ char *zHost; /* The host name to contact */
113
+ char zIn[200]; /* An input line received back from remote */
114
+
115
+ zSsh = db_get("ssh-command", zDefaultSshCmd);
116
+ blob_init(&zCmd, zSsh, -1);
117
+ if( g.urlPort!=g.urlDfltPort ){
118
+#ifdef __MINGW32__
119
+ blob_appendf(&zCmd, " -P %d", g.urlPort);
120
+#else
121
+ blob_appendf(&zCmd, " -p %d", g.urlPort);
122
+#endif
123
+ }
124
+ if( g.urlUser && g.urlUser[0] ){
125
+ zHost = mprintf("%s@%s", g.urlUser, g.urlName);
126
+#ifdef __MINGW32__
127
+ /* Only win32 (and specifically PLINK.EXE support the -pw option */
128
+ if( g.urlPasswd && g.urlPasswd[0] ){
129
+ Blob pw;
130
+ blob_zero(&pw);
131
+ if( g.urlPasswd[0]=='*' ){
132
+ char *zPrompt;
133
+ zPrompt = mprintf("Password for [%s]: ", zHost);
134
+ prompt_for_password(zPrompt, &pw, 0);
135
+ free(zPrompt);
136
+ }else{
137
+ blob_init(&pw, g.urlPasswd, -1);
138
+ }
139
+ blob_append(&zCmd, " -pw ", -1);
140
+ shell_escape(&zCmd, blob_str(&pw));
141
+ blob_reset(&pw);
142
+ }
143
+#endif
144
+ }else{
145
+ zHost = mprintf("%s", g.urlName);
146
+ }
147
+ blob_append(&zCmd, " ", 1);
148
+ shell_escape(&zCmd, zHost);
149
+ free(zHost);
150
+ /* printf("%s\n", blob_str(&zCmd)); */
151
+ popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
152
+ if( sshPid==0 ){
153
+ fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
154
+ }
155
+ blob_reset(&zCmd);
156
+
157
+ /* Send an "echo" command to the other side to make sure that the
158
+ ** connection is up and working.
159
+ */
160
+ fprintf(sshOut, "echo test\n");
161
+ fflush(sshOut);
162
+ sshin_read(zIn, sizeof(zIn));
163
+ if( memcmp(zIn, "test", 4)!=0 ){
164
+ pclose2(sshIn, sshOut, sshPid);
165
+ fossil_fatal("ssh connection failed: [%s]", zIn);
166
+ }
167
+ }
168
+}
68169
69170
/*
70171
** Open a connection to the server. The server is defined by the following
71172
** global variables:
72173
**
@@ -77,11 +178,21 @@
77178
** Return the number of errors.
78179
*/
79180
int transport_open(void){
80181
int rc = 0;
81182
if( transport.isOpen==0 ){
82
- if( g.urlIsHttps ){
183
+ if( g.urlIsSsh ){
184
+ Blob cmd;
185
+ blob_zero(&cmd);
186
+ shell_escape(&cmd, g.urlFossil);
187
+ blob_append(&cmd, " test-http ", -1);
188
+ shell_escape(&cmd, g.urlPath);
189
+ /* fprintf(stdout, "%s\n", blob_str(&cmd)); */
190
+ fprintf(sshOut, "%s\n", blob_str(&cmd));
191
+ fflush(sshOut);
192
+ blob_reset(&cmd);
193
+ }else if( g.urlIsHttps ){
83194
#ifdef FOSSIL_ENABLE_SSL
84195
rc = ssl_open();
85196
if( rc==0 ) transport.isOpen = 1;
86197
#else
87198
socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
@@ -115,11 +226,13 @@
115226
free(transport.pBuf);
116227
transport.pBuf = 0;
117228
transport.nAlloc = 0;
118229
transport.nUsed = 0;
119230
transport.iCursor = 0;
120
- if( g.urlIsHttps ){
231
+ if( g.urlIsSsh ){
232
+ /* No-op */
233
+ }else if( g.urlIsHttps ){
121234
#ifdef FOSSIL_ENABLE_SSL
122235
ssl_close();
123236
#endif
124237
}else if( g.urlIsFile ){
125238
if( transport.pFile ){
@@ -142,11 +255,16 @@
142255
*/
143256
void transport_send(Blob *toSend){
144257
char *z = blob_buffer(toSend);
145258
int n = blob_size(toSend);
146259
transport.nSent += n;
147
- if( g.urlIsHttps ){
260
+ if( g.urlIsSsh ){
261
+ int sent;
262
+ sent = fwrite(z, 1, n, sshOut);
263
+ fflush(sshOut);
264
+ /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */
265
+ }else if( g.urlIsHttps ){
148266
#ifdef FOSSIL_ENABLE_SSL
149267
int sent;
150268
while( n>0 ){
151269
sent = ssl_send(0, z, n);
152270
/* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
@@ -170,11 +288,13 @@
170288
/*
171289
** This routine is called when the outbound message is complete and
172290
** it is time to being recieving a reply.
173291
*/
174292
void transport_flip(void){
175
- if( g.urlIsFile ){
293
+ if( g.urlIsSsh ){
294
+ fprintf(sshOut, "\n\n");
295
+ }else if( g.urlIsFile ){
176296
char *zCmd;
177297
fclose(transport.pFile);
178298
zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
179299
g.argv[0], g.urlName, transport.zOutFile, transport.zInFile
180300
);
@@ -191,10 +311,41 @@
191311
void transport_rewind(void){
192312
if( g.urlIsFile ){
193313
transport_close();
194314
}
195315
}
316
+
317
+/*
318
+** Read N bytes of content directly from the wire and write into
319
+** the buffer.
320
+*/
321
+static int transport_fetch(char *zBuf, int N){
322
+ int got;
323
+ if( sshIn ){
324
+ int x;
325
+ int wanted = N;
326
+ got = 0;
327
+ while( wanted>0 ){
328
+ x = read(sshIn, &zBuf[got], wanted);
329
+ if( x<=0 ) break;
330
+ got += x;
331
+ wanted -= x;
332
+ }
333
+ }else if( g.urlIsHttps ){
334
+ #ifdef FOSSIL_ENABLE_SSL
335
+ got = ssl_receive(0, zBuf, N);
336
+ #else
337
+ got = 0;
338
+ #endif
339
+ }else if( g.urlIsFile ){
340
+ got = fread(zBuf, 1, N, transport.pFile);
341
+ }else{
342
+ got = socket_receive(0, zBuf, N);
343
+ }
344
+ /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
345
+ return got;
346
+}
196347
197348
/*
198349
** Read N bytes of content from the wire and store in the supplied buffer.
199350
** Return the number of bytes actually received.
200351
*/
@@ -201,10 +352,11 @@
201352
int transport_receive(char *zBuf, int N){
202353
int onHand; /* Bytes current held in the transport buffer */
203354
int nByte = 0; /* Bytes of content received */
204355
205356
onHand = transport.nUsed - transport.iCursor;
357
+ /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */
206358
if( onHand>0 ){
207359
int toMove = onHand;
208360
if( toMove>N ) toMove = N;
209361
/* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
210362
memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
@@ -216,24 +368,11 @@
216368
N -= toMove;
217369
zBuf += toMove;
218370
nByte += toMove;
219371
}
220372
if( N>0 ){
221
- int got;
222
- if( g.urlIsHttps ){
223
- #ifdef FOSSIL_ENABLE_SSL
224
- got = ssl_receive(0, zBuf, N);
225
- /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
226
- #else
227
- got = 0;
228
- #endif
229
- }else if( g.urlIsFile ){
230
- got = fread(zBuf, 1, N, transport.pFile);
231
- }else{
232
- got = socket_receive(0, zBuf, N);
233
- /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
234
- }
373
+ int got = transport_fetch(zBuf, N);
235374
if( got>0 ){
236375
nByte += got;
237376
transport.nRcvd += got;
238377
}
239378
}
@@ -267,11 +406,11 @@
267406
pNew = realloc(transport.pBuf, transport.nAlloc);
268407
if( pNew==0 ) fossil_panic("out of memory");
269408
transport.pBuf = pNew;
270409
}
271410
if( N>0 ){
272
- i = transport_receive(&transport.pBuf[transport.nUsed], N);
411
+ i = transport_fetch(&transport.pBuf[transport.nUsed], N);
273412
if( i>0 ){
274413
transport.nUsed += i;
275414
}
276415
}
277416
}
@@ -313,13 +452,19 @@
313452
/* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
314453
return &transport.pBuf[iStart];
315454
}
316455
317456
void transport_global_shutdown(void){
457
+ if( g.urlIsSsh && sshPid ){
458
+ printf("Closing SSH tunnel: ");
459
+ fflush(stdout);
460
+ pclose2(sshIn, sshOut, sshPid);
461
+ sshPid = 0;
462
+ }
318463
if( g.urlIsHttps ){
319464
#ifdef FOSSIL_ENABLE_SSL
320465
ssl_global_shutdown();
321466
#endif
322467
}else{
323468
socket_global_shutdown();
324469
}
325470
}
326471
--- src/http_transport.c
+++ src/http_transport.c
@@ -38,10 +38,19 @@
38 char *zOutFile; /* Name of outbound file for FILE: */
39 char *zInFile; /* Name of inbound file for FILE: */
40 } transport = {
41 0, 0, 0, 0, 0, 0, 0
42 };
 
 
 
 
 
 
 
 
 
43
44 /*
45 ** Return the current transport error message.
46 */
47 const char *transport_errmsg(void){
@@ -63,10 +72,102 @@
63 if( resetFlag ){
64 transport.nSent = 0;
65 transport.nRcvd = 0;
66 }
67 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
69 /*
70 ** Open a connection to the server. The server is defined by the following
71 ** global variables:
72 **
@@ -77,11 +178,21 @@
77 ** Return the number of errors.
78 */
79 int transport_open(void){
80 int rc = 0;
81 if( transport.isOpen==0 ){
82 if( g.urlIsHttps ){
 
 
 
 
 
 
 
 
 
 
83 #ifdef FOSSIL_ENABLE_SSL
84 rc = ssl_open();
85 if( rc==0 ) transport.isOpen = 1;
86 #else
87 socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
@@ -115,11 +226,13 @@
115 free(transport.pBuf);
116 transport.pBuf = 0;
117 transport.nAlloc = 0;
118 transport.nUsed = 0;
119 transport.iCursor = 0;
120 if( g.urlIsHttps ){
 
 
121 #ifdef FOSSIL_ENABLE_SSL
122 ssl_close();
123 #endif
124 }else if( g.urlIsFile ){
125 if( transport.pFile ){
@@ -142,11 +255,16 @@
142 */
143 void transport_send(Blob *toSend){
144 char *z = blob_buffer(toSend);
145 int n = blob_size(toSend);
146 transport.nSent += n;
147 if( g.urlIsHttps ){
 
 
 
 
 
148 #ifdef FOSSIL_ENABLE_SSL
149 int sent;
150 while( n>0 ){
151 sent = ssl_send(0, z, n);
152 /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
@@ -170,11 +288,13 @@
170 /*
171 ** This routine is called when the outbound message is complete and
172 ** it is time to being recieving a reply.
173 */
174 void transport_flip(void){
175 if( g.urlIsFile ){
 
 
176 char *zCmd;
177 fclose(transport.pFile);
178 zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
179 g.argv[0], g.urlName, transport.zOutFile, transport.zInFile
180 );
@@ -191,10 +311,41 @@
191 void transport_rewind(void){
192 if( g.urlIsFile ){
193 transport_close();
194 }
195 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
197 /*
198 ** Read N bytes of content from the wire and store in the supplied buffer.
199 ** Return the number of bytes actually received.
200 */
@@ -201,10 +352,11 @@
201 int transport_receive(char *zBuf, int N){
202 int onHand; /* Bytes current held in the transport buffer */
203 int nByte = 0; /* Bytes of content received */
204
205 onHand = transport.nUsed - transport.iCursor;
 
206 if( onHand>0 ){
207 int toMove = onHand;
208 if( toMove>N ) toMove = N;
209 /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
210 memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
@@ -216,24 +368,11 @@
216 N -= toMove;
217 zBuf += toMove;
218 nByte += toMove;
219 }
220 if( N>0 ){
221 int got;
222 if( g.urlIsHttps ){
223 #ifdef FOSSIL_ENABLE_SSL
224 got = ssl_receive(0, zBuf, N);
225 /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
226 #else
227 got = 0;
228 #endif
229 }else if( g.urlIsFile ){
230 got = fread(zBuf, 1, N, transport.pFile);
231 }else{
232 got = socket_receive(0, zBuf, N);
233 /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
234 }
235 if( got>0 ){
236 nByte += got;
237 transport.nRcvd += got;
238 }
239 }
@@ -267,11 +406,11 @@
267 pNew = realloc(transport.pBuf, transport.nAlloc);
268 if( pNew==0 ) fossil_panic("out of memory");
269 transport.pBuf = pNew;
270 }
271 if( N>0 ){
272 i = transport_receive(&transport.pBuf[transport.nUsed], N);
273 if( i>0 ){
274 transport.nUsed += i;
275 }
276 }
277 }
@@ -313,13 +452,19 @@
313 /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
314 return &transport.pBuf[iStart];
315 }
316
317 void transport_global_shutdown(void){
 
 
 
 
 
 
318 if( g.urlIsHttps ){
319 #ifdef FOSSIL_ENABLE_SSL
320 ssl_global_shutdown();
321 #endif
322 }else{
323 socket_global_shutdown();
324 }
325 }
326
--- src/http_transport.c
+++ src/http_transport.c
@@ -38,10 +38,19 @@
38 char *zOutFile; /* Name of outbound file for FILE: */
39 char *zInFile; /* Name of inbound file for FILE: */
40 } transport = {
41 0, 0, 0, 0, 0, 0, 0
42 };
43
44 /*
45 ** Information about the connection to the SSH subprocess when
46 ** using the ssh:// sync method.
47 */
48 static int sshPid; /* Process id of ssh subprocess */
49 static int sshIn; /* From ssh subprocess to this process */
50 static FILE *sshOut; /* From this to ssh subprocess */
51
52
53 /*
54 ** Return the current transport error message.
55 */
56 const char *transport_errmsg(void){
@@ -63,10 +72,102 @@
72 if( resetFlag ){
73 transport.nSent = 0;
74 transport.nRcvd = 0;
75 }
76 }
77
78 /*
79 ** Read text from sshIn. Zero-terminate and remove trailing
80 ** whitespace.
81 */
82 static void sshin_read(char *zBuf, int szBuf){
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 /*
94 ** Default SSH command
95 */
96 #ifdef __MINGW32__
97 static char zDefaultSshCmd[] = "ssh -T";
98 #else
99 static char zDefaultSshCmd[] = "ssh -e none -T";
100 #endif
101
102 /*
103 ** Global initialization of the transport layer
104 */
105 void transport_global_startup(void){
106 if( g.urlIsSsh ){
107 /* Only SSH requires a global initialization. For SSH we need to create
108 ** and run an SSH command to talk to the remote machine.
109 */
110 const char *zSsh; /* The base SSH command */
111 Blob zCmd; /* The SSH command */
112 char *zHost; /* The host name to contact */
113 char zIn[200]; /* An input line received back from remote */
114
115 zSsh = db_get("ssh-command", zDefaultSshCmd);
116 blob_init(&zCmd, zSsh, -1);
117 if( g.urlPort!=g.urlDfltPort ){
118 #ifdef __MINGW32__
119 blob_appendf(&zCmd, " -P %d", g.urlPort);
120 #else
121 blob_appendf(&zCmd, " -p %d", g.urlPort);
122 #endif
123 }
124 if( g.urlUser && g.urlUser[0] ){
125 zHost = mprintf("%s@%s", g.urlUser, g.urlName);
126 #ifdef __MINGW32__
127 /* Only win32 (and specifically PLINK.EXE support the -pw option */
128 if( g.urlPasswd && g.urlPasswd[0] ){
129 Blob pw;
130 blob_zero(&pw);
131 if( g.urlPasswd[0]=='*' ){
132 char *zPrompt;
133 zPrompt = mprintf("Password for [%s]: ", zHost);
134 prompt_for_password(zPrompt, &pw, 0);
135 free(zPrompt);
136 }else{
137 blob_init(&pw, g.urlPasswd, -1);
138 }
139 blob_append(&zCmd, " -pw ", -1);
140 shell_escape(&zCmd, blob_str(&pw));
141 blob_reset(&pw);
142 }
143 #endif
144 }else{
145 zHost = mprintf("%s", g.urlName);
146 }
147 blob_append(&zCmd, " ", 1);
148 shell_escape(&zCmd, zHost);
149 free(zHost);
150 /* printf("%s\n", blob_str(&zCmd)); */
151 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
152 if( sshPid==0 ){
153 fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
154 }
155 blob_reset(&zCmd);
156
157 /* Send an "echo" command to the other side to make sure that the
158 ** connection is up and working.
159 */
160 fprintf(sshOut, "echo test\n");
161 fflush(sshOut);
162 sshin_read(zIn, sizeof(zIn));
163 if( memcmp(zIn, "test", 4)!=0 ){
164 pclose2(sshIn, sshOut, sshPid);
165 fossil_fatal("ssh connection failed: [%s]", zIn);
166 }
167 }
168 }
169
170 /*
171 ** Open a connection to the server. The server is defined by the following
172 ** global variables:
173 **
@@ -77,11 +178,21 @@
178 ** Return the number of errors.
179 */
180 int transport_open(void){
181 int rc = 0;
182 if( transport.isOpen==0 ){
183 if( g.urlIsSsh ){
184 Blob cmd;
185 blob_zero(&cmd);
186 shell_escape(&cmd, g.urlFossil);
187 blob_append(&cmd, " test-http ", -1);
188 shell_escape(&cmd, g.urlPath);
189 /* fprintf(stdout, "%s\n", blob_str(&cmd)); */
190 fprintf(sshOut, "%s\n", blob_str(&cmd));
191 fflush(sshOut);
192 blob_reset(&cmd);
193 }else if( g.urlIsHttps ){
194 #ifdef FOSSIL_ENABLE_SSL
195 rc = ssl_open();
196 if( rc==0 ) transport.isOpen = 1;
197 #else
198 socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support");
@@ -115,11 +226,13 @@
226 free(transport.pBuf);
227 transport.pBuf = 0;
228 transport.nAlloc = 0;
229 transport.nUsed = 0;
230 transport.iCursor = 0;
231 if( g.urlIsSsh ){
232 /* No-op */
233 }else if( g.urlIsHttps ){
234 #ifdef FOSSIL_ENABLE_SSL
235 ssl_close();
236 #endif
237 }else if( g.urlIsFile ){
238 if( transport.pFile ){
@@ -142,11 +255,16 @@
255 */
256 void transport_send(Blob *toSend){
257 char *z = blob_buffer(toSend);
258 int n = blob_size(toSend);
259 transport.nSent += n;
260 if( g.urlIsSsh ){
261 int sent;
262 sent = fwrite(z, 1, n, sshOut);
263 fflush(sshOut);
264 /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */
265 }else if( g.urlIsHttps ){
266 #ifdef FOSSIL_ENABLE_SSL
267 int sent;
268 while( n>0 ){
269 sent = ssl_send(0, z, n);
270 /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */
@@ -170,11 +288,13 @@
288 /*
289 ** This routine is called when the outbound message is complete and
290 ** it is time to being recieving a reply.
291 */
292 void transport_flip(void){
293 if( g.urlIsSsh ){
294 fprintf(sshOut, "\n\n");
295 }else if( g.urlIsFile ){
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 );
@@ -191,10 +311,41 @@
311 void transport_rewind(void){
312 if( g.urlIsFile ){
313 transport_close();
314 }
315 }
316
317 /*
318 ** Read N bytes of content directly from the wire and write into
319 ** the buffer.
320 */
321 static int transport_fetch(char *zBuf, int N){
322 int got;
323 if( sshIn ){
324 int x;
325 int wanted = N;
326 got = 0;
327 while( wanted>0 ){
328 x = read(sshIn, &zBuf[got], wanted);
329 if( x<=0 ) break;
330 got += x;
331 wanted -= x;
332 }
333 }else if( g.urlIsHttps ){
334 #ifdef FOSSIL_ENABLE_SSL
335 got = ssl_receive(0, zBuf, N);
336 #else
337 got = 0;
338 #endif
339 }else if( g.urlIsFile ){
340 got = fread(zBuf, 1, N, transport.pFile);
341 }else{
342 got = socket_receive(0, zBuf, N);
343 }
344 /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */
345 return got;
346 }
347
348 /*
349 ** Read N bytes of content from the wire and store in the supplied buffer.
350 ** Return the number of bytes actually received.
351 */
@@ -201,10 +352,11 @@
352 int transport_receive(char *zBuf, int N){
353 int onHand; /* Bytes current held in the transport buffer */
354 int nByte = 0; /* Bytes of content received */
355
356 onHand = transport.nUsed - transport.iCursor;
357 /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */
358 if( onHand>0 ){
359 int toMove = onHand;
360 if( toMove>N ) toMove = N;
361 /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */
362 memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove);
@@ -216,24 +368,11 @@
368 N -= toMove;
369 zBuf += toMove;
370 nByte += toMove;
371 }
372 if( N>0 ){
373 int got = transport_fetch(zBuf, N);
 
 
 
 
 
 
 
 
 
 
 
 
 
374 if( got>0 ){
375 nByte += got;
376 transport.nRcvd += got;
377 }
378 }
@@ -267,11 +406,11 @@
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 ){
413 transport.nUsed += i;
414 }
415 }
416 }
@@ -313,13 +452,19 @@
452 /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */
453 return &transport.pBuf[iStart];
454 }
455
456 void transport_global_shutdown(void){
457 if( g.urlIsSsh && sshPid ){
458 printf("Closing SSH tunnel: ");
459 fflush(stdout);
460 pclose2(sshIn, sshOut, sshPid);
461 sshPid = 0;
462 }
463 if( g.urlIsHttps ){
464 #ifdef FOSSIL_ENABLE_SSL
465 ssl_global_shutdown();
466 #endif
467 }else{
468 socket_global_shutdown();
469 }
470 }
471
+57 -39
--- src/info.c
+++ src/info.c
@@ -65,10 +65,16 @@
6565
);
6666
/* 01234567890123 */
6767
printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
6868
free(zUuid);
6969
free(zDate);
70
+ }
71
+ if( zUuid && showComment ){
72
+ zComment = db_text(0,
73
+ "SELECT coalesce(ecomment,comment) || ' (user: ' || coalesce(euser,user,'?') || ')' FROM event WHERE objid=%d",
74
+ rid
75
+ );
7076
}
7177
db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
7278
" WHERE cid=%d", rid);
7379
while( db_step(&q)==SQLITE_ROW ){
7480
const char *zUuid = db_column_text(&q, 0);
@@ -96,11 +102,12 @@
96102
if( zTags && zTags[0] ){
97103
printf("tags: %s\n", zTags);
98104
}
99105
free(zTags);
100106
if( zComment ){
101
- printf("comment:\n%s\n", zComment);
107
+ printf("comment: ");
108
+ comment_print(zComment, 14, 79);
102109
free(zComment);
103110
}
104111
}
105112
106113
@@ -135,11 +142,11 @@
135142
/* 012345678901234 */
136143
db_record_repository_filename(0);
137144
printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
138145
printf("repository: %s\n", db_lget("repository", ""));
139146
printf("local-root: %s\n", g.zLocalRoot);
140
-#ifdef __MINGW32__
147
+#if defined(_WIN32)
141148
if( g.zHome ){
142149
printf("user-home: %s\n", g.zHome);
143150
}
144151
#endif
145152
printf("project-code: %s\n", db_get("project-code", ""));
@@ -187,15 +194,15 @@
187194
@ <div class="section">Tags And Properties</div>
188195
@ <ul>
189196
}
190197
@ <li>
191198
if( tagtype==0 ){
192
- @ <b><s>%h(zTagname)</s></b> cancelled
199
+ @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
193200
}else if( zValue ){
194
- @ <b>%h(zTagname)=%h(zValue)</b>
201
+ @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
195202
}else {
196
- @ <b>%h(zTagname)</b>
203
+ @ <span class="infoTag">%h(zTagname)</span>
197204
}
198205
if( tagtype==2 ){
199206
if( zOrigUuid && zOrigUuid[0] ){
200207
@ inherited from
201208
hyperlink_to_uuid(zOrigUuid);
@@ -215,10 +222,11 @@
215222
}
216223
hyperlink_to_uuid(zSrcUuid);
217224
@ on
218225
hyperlink_to_date(zDate,0);
219226
}
227
+ @ </li>
220228
}
221229
db_finalize(&q);
222230
if( cnt ){
223231
@ </ul>
224232
}
@@ -231,11 +239,11 @@
231239
static void append_diff(int fromid, int toid){
232240
Blob from, to, out;
233241
content_get(fromid, &from);
234242
content_get(toid, &to);
235243
blob_zero(&out);
236
- text_diff(&from, &to, &out, 5);
244
+ text_diff(&from, &to, &out, 5, 1);
237245
@ %h(blob_str(&out))
238246
blob_reset(&from);
239247
blob_reset(&to);
240248
blob_reset(&out);
241249
}
@@ -255,25 +263,33 @@
255263
@ <p>Deleted %h(zName)</p>
256264
}else if( zOld==0 ){
257265
@ <p>Added %h(zName)</p>
258266
}else{
259267
@ <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
+ }
260275
}
261276
}else if( zOld && zNew ){
262277
@ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
263278
@ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
264279
@ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
265280
if( !showDiff ){
266281
@ &nbsp;&nbsp;
267
- @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a>
282
+ @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
268283
}else{
269284
int rid1 = uuid_to_rid(zOld, 0);
270285
int rid2 = uuid_to_rid(zNew, 0);
271286
@ <blockquote><pre>
272287
append_diff(rid1, rid2);
273288
@ </pre></blockquote>
274289
}
290
+ @ </p>
275291
}else if( zOld ){
276292
@ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
277293
@ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p>
278294
}else{
279295
@ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -340,11 +356,11 @@
340356
TAG_COMMENT, rid);
341357
zUser = db_column_text(&q, 2);
342358
zComment = db_column_text(&q, 3);
343359
zDate = db_column_text(&q,1);
344360
@ <div class="section">Overview</div>
345
- @ <p><table class="label-value">
361
+ @ <table class="label-value">
346362
@ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
347363
if( g.okSetup ){
348364
@ (Record ID: %d(rid))
349365
}
350366
@ </td></tr>
@@ -383,13 +399,13 @@
383399
db_finalize(&q);
384400
}
385401
if( g.okHistory ){
386402
const char *zProjName = db_get("project-name", "unnamed");
387403
@ <tr><th>Timelines:</th><td>
388
- @ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
389
- @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
390
- @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a>
404
+ @ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
405
+ @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
406
+ @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&amp;p=%S(zUuid)">both</a>
391407
db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
392408
" WHERE rid=%d AND tagtype>0 "
393409
" AND tag.tagid=tagxref.tagid "
394410
" AND +tag.tagname GLOB 'sym-*'", rid);
395411
while( db_step(&q)==SQLITE_ROW ){
@@ -399,20 +415,22 @@
399415
db_finalize(&q);
400416
@ </td></tr>
401417
@ <tr><th>Other&nbsp;Links:</th>
402418
@ <td>
403419
@ <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a>
404
- @ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
405
- @ ZIP archive</a>
420
+ if( g.okZip ){
421
+ @ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
422
+ @ ZIP archive</a>
423
+ }
406424
@ | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
407425
if( g.okWrite ){
408426
@ | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
409427
}
410428
@ </td>
411429
@ </tr>
412430
}
413
- @ </table></p>
431
+ @ </table>
414432
}else{
415433
style_header("Check-in Information");
416434
login_anonymous_available();
417435
}
418436
db_finalize(&q);
@@ -600,11 +618,11 @@
600618
}
601619
602620
603621
/*
604622
** WEBPAGE: vdiff
605
-** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN
623
+** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
606624
**
607625
** Show all differences between two checkins.
608626
*/
609627
void vdiff_page(void){
610628
int ridFrom, ridTo;
@@ -622,11 +640,11 @@
622640
style_header("Check-in Differences");
623641
@ <h2>Difference From:</h2><blockquote>
624642
checkin_description(ridFrom);
625643
@ </blockquote><h2>To:</h2><blockquote>
626644
checkin_description(ridTo);
627
- @ </blockquote><hr><p>
645
+ @ </blockquote><hr /><p>
628646
629647
iFrom = iTo = 0;
630648
while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
631649
int cmp;
632650
if( iFrom>=mFrom.nFile ){
@@ -864,23 +882,23 @@
864882
v1 = name_to_rid_www("v1");
865883
v2 = name_to_rid_www("v2");
866884
if( v1==0 || v2==0 ) fossil_redirect_home();
867885
style_header("Diff");
868886
@ <h2>Differences From:</h2>
869
- @ <blockquote>
887
+ @ <blockquote><p>
870888
object_description(v1, 1, 0);
871
- @ </blockquote>
889
+ @ </p></blockquote>
872890
@ <h2>To:</h2>
873
- @ <blockquote>
891
+ @ <blockquote><p>
874892
object_description(v2, 1, 0);
875
- @ </blockquote>
876
- @ <hr>
893
+ @ </p></blockquote>
894
+ @ <hr />
877895
@ <blockquote><pre>
878896
content_get(v1, &c1);
879897
content_get(v2, &c2);
880898
blob_zero(&diff);
881
- text_diff(&c1, &c2, &diff, 4);
899
+ text_diff(&c1, &c2, &diff, 4, 1);
882900
blob_reset(&c1);
883901
blob_reset(&c2);
884902
@ %h(blob_str(&diff))
885903
@ </pre></blockquote>
886904
blob_reset(&diff);
@@ -978,27 +996,27 @@
978996
if( !g.okRead ){ login_needed(); return; }
979997
if( rid==0 ) fossil_redirect_home();
980998
if( g.okAdmin ){
981999
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
9821000
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
983
- style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
1001
+ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
9841002
g.zTop, zUuid);
9851003
}else{
9861004
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
9871005
g.zTop, zUuid);
9881006
}
9891007
}
9901008
style_header("Hex Artifact Content");
9911009
zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
9921010
@ <h2>Artifact %s(zUuid):</h2>
993
- @ <blockquote>
1011
+ @ <blockquote><p>
9941012
blob_zero(&downloadName);
9951013
object_description(rid, 0, &downloadName);
9961014
style_submenu_element("Download", "Download",
9971015
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
998
- @ </blockquote>
999
- @ <hr>
1016
+ @ </p></blockquote>
1017
+ @ <hr />
10001018
content_get(rid, &content);
10011019
@ <blockquote><pre>
10021020
hexdump(&content);
10031021
@ </pre></blockquote>
10041022
style_footer();
@@ -1060,21 +1078,21 @@
10601078
if( !g.okRead ){ login_needed(); return; }
10611079
if( rid==0 ) fossil_redirect_home();
10621080
if( g.okAdmin ){
10631081
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
10641082
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1065
- style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
1083
+ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
10661084
g.zTop, zUuid);
10671085
}else{
10681086
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
10691087
g.zTop, zUuid);
10701088
}
10711089
}
10721090
style_header("Artifact Content");
10731091
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
10741092
@ <h2>Artifact %s(zUuid)</h2>
1075
- @ <blockquote>
1093
+ @ <blockquote><p>
10761094
blob_zero(&downloadName);
10771095
object_description(rid, 0, &downloadName);
10781096
style_submenu_element("Download", "Download",
10791097
"%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
10801098
zMime = mimetype_from_name(blob_str(&downloadName));
@@ -1084,25 +1102,25 @@
10841102
style_submenu_element("Html", "Html",
10851103
"%s/artifact?name=%s", g.zTop, zUuid);
10861104
}else{
10871105
renderAsHtml = 1;
10881106
style_submenu_element("Text", "Text",
1089
- "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
1107
+ "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
10901108
}
10911109
}else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
10921110
if( P("txt") ){
10931111
style_submenu_element("Wiki", "Wiki",
10941112
"%s/artifact?name=%s", g.zTop, zUuid);
10951113
}else{
10961114
renderAsWiki = 1;
10971115
style_submenu_element("Text", "Text",
1098
- "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
1116
+ "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
10991117
}
11001118
}
11011119
}
1102
- @ </blockquote>
1103
- @ <hr>
1120
+ @ </p></blockquote>
1121
+ @ <hr />
11041122
content_get(rid, &content);
11051123
if( renderAsWiki ){
11061124
wiki_convert(&content, 0, 0);
11071125
}else if( renderAsHtml ){
11081126
@ <div>
@@ -1115,11 +1133,11 @@
11151133
@ <pre>
11161134
@ %h(blob_str(&content))
11171135
@ </pre>
11181136
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
11191137
}else if( strncmp(zMime, "image/", 6)==0 ){
1120
- @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img>
1138
+ @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&amp;m=%s(zMime)"></img>
11211139
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
11221140
}else{
11231141
@ <pre>
11241142
hexdump(&content);
11251143
@ </pre>
@@ -1148,11 +1166,11 @@
11481166
rid = name_to_rid_www("name");
11491167
if( rid==0 ){ fossil_redirect_home(); }
11501168
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
11511169
if( g.okAdmin ){
11521170
if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1153
- style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
1171
+ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
11541172
g.zTop, zUuid);
11551173
}else{
11561174
style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
11571175
g.zTop, zUuid);
11581176
}
@@ -1426,11 +1444,11 @@
14261444
int nTag = 0;
14271445
@ <b>Preview:</b>
14281446
@ <blockquote>
14291447
@ <table border=0>
14301448
if( zNewColor && zNewColor[0] ){
1431
- @ <tr><td bgcolor="%h(zNewColor)">
1449
+ @ <tr><td style="background-color: %h(zNewColor);">
14321450
}else{
14331451
@ <tr><td>
14341452
}
14351453
wiki_convert(&comment, 0, WIKI_INLINE);
14361454
blob_zero(&suffix);
@@ -1451,11 +1469,11 @@
14511469
db_finalize(&q);
14521470
blob_appendf(&suffix, ")");
14531471
@ %s(blob_str(&suffix))
14541472
@ </td></tr></table>
14551473
@ </blockquote>
1456
- @ <hr>
1474
+ @ <hr />
14571475
blob_reset(&suffix);
14581476
}
14591477
@ <p>Make changes to attributes of check-in
14601478
@ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
14611479
@ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
@@ -1489,11 +1507,11 @@
14891507
}
14901508
@ Propagate color to descendants</input></td></tr>
14911509
@ <tr>
14921510
for(i=0; i<nColor; i++){
14931511
if( aColor[i].zColor[0] ){
1494
- @ <td bgcolor="%h(aColor[i].zColor)">
1512
+ @ <td style="background-color: %h(aColor[i].zColor);">
14951513
}else{
14961514
@ <td>
14971515
}
14981516
if( strcmp(zNewColor, aColor[i].zColor)==0 ){
14991517
@ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
@@ -1525,13 +1543,13 @@
15251543
int tagid = db_column_int(&q, 0);
15261544
const char *zTagName = db_column_text(&q, 1);
15271545
char zLabel[30];
15281546
sprintf(zLabel, "c%d", tagid);
15291547
if( P(zLabel) ){
1530
- @ <br><input type="checkbox" name="c%d(tagid)" checked>
1548
+ @ <br /><input type="checkbox" name="c%d(tagid)" checked>
15311549
}else{
1532
- @ <br><input type="checkbox" name="c%d(tagid)">
1550
+ @ <br /><input type="checkbox" name="c%d(tagid)">
15331551
}
15341552
if( strncmp(zTagName, "sym-", 4)==0 ){
15351553
@ Cancel tag <b>%h(&zTagName[4])</b>
15361554
}else{
15371555
@ Cancel special tag <b>%h(zTagName)</b>
15381556
--- src/info.c
+++ src/info.c
@@ -65,10 +65,16 @@
65 );
66 /* 01234567890123 */
67 printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
68 free(zUuid);
69 free(zDate);
 
 
 
 
 
 
70 }
71 db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
72 " WHERE cid=%d", rid);
73 while( db_step(&q)==SQLITE_ROW ){
74 const char *zUuid = db_column_text(&q, 0);
@@ -96,11 +102,12 @@
96 if( zTags && zTags[0] ){
97 printf("tags: %s\n", zTags);
98 }
99 free(zTags);
100 if( zComment ){
101 printf("comment:\n%s\n", zComment);
 
102 free(zComment);
103 }
104 }
105
106
@@ -135,11 +142,11 @@
135 /* 012345678901234 */
136 db_record_repository_filename(0);
137 printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
138 printf("repository: %s\n", db_lget("repository", ""));
139 printf("local-root: %s\n", g.zLocalRoot);
140 #ifdef __MINGW32__
141 if( g.zHome ){
142 printf("user-home: %s\n", g.zHome);
143 }
144 #endif
145 printf("project-code: %s\n", db_get("project-code", ""));
@@ -187,15 +194,15 @@
187 @ <div class="section">Tags And Properties</div>
188 @ <ul>
189 }
190 @ <li>
191 if( tagtype==0 ){
192 @ <b><s>%h(zTagname)</s></b> cancelled
193 }else if( zValue ){
194 @ <b>%h(zTagname)=%h(zValue)</b>
195 }else {
196 @ <b>%h(zTagname)</b>
197 }
198 if( tagtype==2 ){
199 if( zOrigUuid && zOrigUuid[0] ){
200 @ inherited from
201 hyperlink_to_uuid(zOrigUuid);
@@ -215,10 +222,11 @@
215 }
216 hyperlink_to_uuid(zSrcUuid);
217 @ on
218 hyperlink_to_date(zDate,0);
219 }
 
220 }
221 db_finalize(&q);
222 if( cnt ){
223 @ </ul>
224 }
@@ -231,11 +239,11 @@
231 static void append_diff(int fromid, int toid){
232 Blob from, to, out;
233 content_get(fromid, &from);
234 content_get(toid, &to);
235 blob_zero(&out);
236 text_diff(&from, &to, &out, 5);
237 @ %h(blob_str(&out))
238 blob_reset(&from);
239 blob_reset(&to);
240 blob_reset(&out);
241 }
@@ -255,25 +263,33 @@
255 @ <p>Deleted %h(zName)</p>
256 }else if( zOld==0 ){
257 @ <p>Added %h(zName)</p>
258 }else{
259 @ <p>Changes to %h(zName)</p>
 
 
 
 
 
 
 
260 }
261 }else if( zOld && zNew ){
262 @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
263 @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
264 @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
265 if( !showDiff ){
266 @ &nbsp;&nbsp;
267 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a>
268 }else{
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 }else if( zOld ){
276 @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
277 @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p>
278 }else{
279 @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -340,11 +356,11 @@
340 TAG_COMMENT, rid);
341 zUser = db_column_text(&q, 2);
342 zComment = db_column_text(&q, 3);
343 zDate = db_column_text(&q,1);
344 @ <div class="section">Overview</div>
345 @ <p><table class="label-value">
346 @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
347 if( g.okSetup ){
348 @ (Record ID: %d(rid))
349 }
350 @ </td></tr>
@@ -383,13 +399,13 @@
383 db_finalize(&q);
384 }
385 if( g.okHistory ){
386 const char *zProjName = db_get("project-name", "unnamed");
387 @ <tr><th>Timelines:</th><td>
388 @ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
389 @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
390 @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a>
391 db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
392 " WHERE rid=%d AND tagtype>0 "
393 " AND tag.tagid=tagxref.tagid "
394 " AND +tag.tagname GLOB 'sym-*'", rid);
395 while( db_step(&q)==SQLITE_ROW ){
@@ -399,20 +415,22 @@
399 db_finalize(&q);
400 @ </td></tr>
401 @ <tr><th>Other&nbsp;Links:</th>
402 @ <td>
403 @ <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a>
404 @ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
405 @ ZIP archive</a>
 
 
406 @ | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
407 if( g.okWrite ){
408 @ | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
409 }
410 @ </td>
411 @ </tr>
412 }
413 @ </table></p>
414 }else{
415 style_header("Check-in Information");
416 login_anonymous_available();
417 }
418 db_finalize(&q);
@@ -600,11 +618,11 @@
600 }
601
602
603 /*
604 ** WEBPAGE: vdiff
605 ** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN
606 **
607 ** Show all differences between two checkins.
608 */
609 void vdiff_page(void){
610 int ridFrom, ridTo;
@@ -622,11 +640,11 @@
622 style_header("Check-in Differences");
623 @ <h2>Difference From:</h2><blockquote>
624 checkin_description(ridFrom);
625 @ </blockquote><h2>To:</h2><blockquote>
626 checkin_description(ridTo);
627 @ </blockquote><hr><p>
628
629 iFrom = iTo = 0;
630 while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
631 int cmp;
632 if( iFrom>=mFrom.nFile ){
@@ -864,23 +882,23 @@
864 v1 = name_to_rid_www("v1");
865 v2 = name_to_rid_www("v2");
866 if( v1==0 || v2==0 ) fossil_redirect_home();
867 style_header("Diff");
868 @ <h2>Differences From:</h2>
869 @ <blockquote>
870 object_description(v1, 1, 0);
871 @ </blockquote>
872 @ <h2>To:</h2>
873 @ <blockquote>
874 object_description(v2, 1, 0);
875 @ </blockquote>
876 @ <hr>
877 @ <blockquote><pre>
878 content_get(v1, &c1);
879 content_get(v2, &c2);
880 blob_zero(&diff);
881 text_diff(&c1, &c2, &diff, 4);
882 blob_reset(&c1);
883 blob_reset(&c2);
884 @ %h(blob_str(&diff))
885 @ </pre></blockquote>
886 blob_reset(&diff);
@@ -978,27 +996,27 @@
978 if( !g.okRead ){ login_needed(); return; }
979 if( rid==0 ) fossil_redirect_home();
980 if( g.okAdmin ){
981 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
982 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
983 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
984 g.zTop, zUuid);
985 }else{
986 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
987 g.zTop, zUuid);
988 }
989 }
990 style_header("Hex Artifact Content");
991 zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
992 @ <h2>Artifact %s(zUuid):</h2>
993 @ <blockquote>
994 blob_zero(&downloadName);
995 object_description(rid, 0, &downloadName);
996 style_submenu_element("Download", "Download",
997 "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
998 @ </blockquote>
999 @ <hr>
1000 content_get(rid, &content);
1001 @ <blockquote><pre>
1002 hexdump(&content);
1003 @ </pre></blockquote>
1004 style_footer();
@@ -1060,21 +1078,21 @@
1060 if( !g.okRead ){ login_needed(); return; }
1061 if( rid==0 ) fossil_redirect_home();
1062 if( g.okAdmin ){
1063 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1064 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1065 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
1066 g.zTop, zUuid);
1067 }else{
1068 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1069 g.zTop, zUuid);
1070 }
1071 }
1072 style_header("Artifact Content");
1073 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
1074 @ <h2>Artifact %s(zUuid)</h2>
1075 @ <blockquote>
1076 blob_zero(&downloadName);
1077 object_description(rid, 0, &downloadName);
1078 style_submenu_element("Download", "Download",
1079 "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
1080 zMime = mimetype_from_name(blob_str(&downloadName));
@@ -1084,25 +1102,25 @@
1084 style_submenu_element("Html", "Html",
1085 "%s/artifact?name=%s", g.zTop, zUuid);
1086 }else{
1087 renderAsHtml = 1;
1088 style_submenu_element("Text", "Text",
1089 "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
1090 }
1091 }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
1092 if( P("txt") ){
1093 style_submenu_element("Wiki", "Wiki",
1094 "%s/artifact?name=%s", g.zTop, zUuid);
1095 }else{
1096 renderAsWiki = 1;
1097 style_submenu_element("Text", "Text",
1098 "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
1099 }
1100 }
1101 }
1102 @ </blockquote>
1103 @ <hr>
1104 content_get(rid, &content);
1105 if( renderAsWiki ){
1106 wiki_convert(&content, 0, 0);
1107 }else if( renderAsHtml ){
1108 @ <div>
@@ -1115,11 +1133,11 @@
1115 @ <pre>
1116 @ %h(blob_str(&content))
1117 @ </pre>
1118 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1119 }else if( strncmp(zMime, "image/", 6)==0 ){
1120 @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img>
1121 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1122 }else{
1123 @ <pre>
1124 hexdump(&content);
1125 @ </pre>
@@ -1148,11 +1166,11 @@
1148 rid = name_to_rid_www("name");
1149 if( rid==0 ){ fossil_redirect_home(); }
1150 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1151 if( g.okAdmin ){
1152 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1153 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
1154 g.zTop, zUuid);
1155 }else{
1156 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1157 g.zTop, zUuid);
1158 }
@@ -1426,11 +1444,11 @@
1426 int nTag = 0;
1427 @ <b>Preview:</b>
1428 @ <blockquote>
1429 @ <table border=0>
1430 if( zNewColor && zNewColor[0] ){
1431 @ <tr><td bgcolor="%h(zNewColor)">
1432 }else{
1433 @ <tr><td>
1434 }
1435 wiki_convert(&comment, 0, WIKI_INLINE);
1436 blob_zero(&suffix);
@@ -1451,11 +1469,11 @@
1451 db_finalize(&q);
1452 blob_appendf(&suffix, ")");
1453 @ %s(blob_str(&suffix))
1454 @ </td></tr></table>
1455 @ </blockquote>
1456 @ <hr>
1457 blob_reset(&suffix);
1458 }
1459 @ <p>Make changes to attributes of check-in
1460 @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
1461 @ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
@@ -1489,11 +1507,11 @@
1489 }
1490 @ Propagate color to descendants</input></td></tr>
1491 @ <tr>
1492 for(i=0; i<nColor; i++){
1493 if( aColor[i].zColor[0] ){
1494 @ <td bgcolor="%h(aColor[i].zColor)">
1495 }else{
1496 @ <td>
1497 }
1498 if( strcmp(zNewColor, aColor[i].zColor)==0 ){
1499 @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
@@ -1525,13 +1543,13 @@
1525 int tagid = db_column_int(&q, 0);
1526 const char *zTagName = db_column_text(&q, 1);
1527 char zLabel[30];
1528 sprintf(zLabel, "c%d", tagid);
1529 if( P(zLabel) ){
1530 @ <br><input type="checkbox" name="c%d(tagid)" checked>
1531 }else{
1532 @ <br><input type="checkbox" name="c%d(tagid)">
1533 }
1534 if( strncmp(zTagName, "sym-", 4)==0 ){
1535 @ Cancel tag <b>%h(&zTagName[4])</b>
1536 }else{
1537 @ Cancel special tag <b>%h(zTagName)</b>
1538
--- src/info.c
+++ src/info.c
@@ -65,10 +65,16 @@
65 );
66 /* 01234567890123 */
67 printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : "");
68 free(zUuid);
69 free(zDate);
70 }
71 if( zUuid && showComment ){
72 zComment = db_text(0,
73 "SELECT coalesce(ecomment,comment) || ' (user: ' || coalesce(euser,user,'?') || ')' FROM event WHERE objid=%d",
74 rid
75 );
76 }
77 db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid "
78 " WHERE cid=%d", rid);
79 while( db_step(&q)==SQLITE_ROW ){
80 const char *zUuid = db_column_text(&q, 0);
@@ -96,11 +102,12 @@
102 if( zTags && zTags[0] ){
103 printf("tags: %s\n", zTags);
104 }
105 free(zTags);
106 if( zComment ){
107 printf("comment: ");
108 comment_print(zComment, 14, 79);
109 free(zComment);
110 }
111 }
112
113
@@ -135,11 +142,11 @@
142 /* 012345678901234 */
143 db_record_repository_filename(0);
144 printf("project-name: %s\n", db_get("project-name", "<unnamed>"));
145 printf("repository: %s\n", db_lget("repository", ""));
146 printf("local-root: %s\n", g.zLocalRoot);
147 #if defined(_WIN32)
148 if( g.zHome ){
149 printf("user-home: %s\n", g.zHome);
150 }
151 #endif
152 printf("project-code: %s\n", db_get("project-code", ""));
@@ -187,15 +194,15 @@
194 @ <div class="section">Tags And Properties</div>
195 @ <ul>
196 }
197 @ <li>
198 if( tagtype==0 ){
199 @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
200 }else if( zValue ){
201 @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
202 }else {
203 @ <span class="infoTag">%h(zTagname)</span>
204 }
205 if( tagtype==2 ){
206 if( zOrigUuid && zOrigUuid[0] ){
207 @ inherited from
208 hyperlink_to_uuid(zOrigUuid);
@@ -215,10 +222,11 @@
222 }
223 hyperlink_to_uuid(zSrcUuid);
224 @ on
225 hyperlink_to_date(zDate,0);
226 }
227 @ </li>
228 }
229 db_finalize(&q);
230 if( cnt ){
231 @ </ul>
232 }
@@ -231,11 +239,11 @@
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);
248 blob_reset(&out);
249 }
@@ -255,25 +263,33 @@
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>
@@ -340,11 +356,11 @@
356 TAG_COMMENT, rid);
357 zUser = db_column_text(&q, 2);
358 zComment = db_column_text(&q, 3);
359 zDate = db_column_text(&q,1);
360 @ <div class="section">Overview</div>
361 @ <table class="label-value">
362 @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
363 if( g.okSetup ){
364 @ (Record ID: %d(rid))
365 }
366 @ </td></tr>
@@ -383,13 +399,13 @@
399 db_finalize(&q);
400 }
401 if( g.okHistory ){
402 const char *zProjName = db_get("project-name", "unnamed");
403 @ <tr><th>Timelines:</th><td>
404 @ <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
405 @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
406 @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&amp;p=%S(zUuid)">both</a>
407 db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
408 " WHERE rid=%d AND tagtype>0 "
409 " AND tag.tagid=tagxref.tagid "
410 " AND +tag.tagname GLOB 'sym-*'", rid);
411 while( db_step(&q)==SQLITE_ROW ){
@@ -399,20 +415,22 @@
415 db_finalize(&q);
416 @ </td></tr>
417 @ <tr><th>Other&nbsp;Links:</th>
418 @ <td>
419 @ <a href="%s(g.zTop)/dir?ci=%S(zUuid)">files</a>
420 if( g.okZip ){
421 @ | <a href="%s(g.zTop)/zip/%s(zProjName)-%S(zUuid).zip?uuid=%s(zUuid)">
422 @ ZIP archive</a>
423 }
424 @ | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
425 if( g.okWrite ){
426 @ | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
427 }
428 @ </td>
429 @ </tr>
430 }
431 @ </table>
432 }else{
433 style_header("Check-in Information");
434 login_anonymous_available();
435 }
436 db_finalize(&q);
@@ -600,11 +618,11 @@
618 }
619
620
621 /*
622 ** WEBPAGE: vdiff
623 ** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
624 **
625 ** Show all differences between two checkins.
626 */
627 void vdiff_page(void){
628 int ridFrom, ridTo;
@@ -622,11 +640,11 @@
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 ){
@@ -864,23 +882,23 @@
882 v1 = name_to_rid_www("v1");
883 v2 = name_to_rid_www("v2");
884 if( v1==0 || v2==0 ) fossil_redirect_home();
885 style_header("Diff");
886 @ <h2>Differences From:</h2>
887 @ <blockquote><p>
888 object_description(v1, 1, 0);
889 @ </p></blockquote>
890 @ <h2>To:</h2>
891 @ <blockquote><p>
892 object_description(v2, 1, 0);
893 @ </p></blockquote>
894 @ <hr />
895 @ <blockquote><pre>
896 content_get(v1, &c1);
897 content_get(v2, &c2);
898 blob_zero(&diff);
899 text_diff(&c1, &c2, &diff, 4, 1);
900 blob_reset(&c1);
901 blob_reset(&c2);
902 @ %h(blob_str(&diff))
903 @ </pre></blockquote>
904 blob_reset(&diff);
@@ -978,27 +996,27 @@
996 if( !g.okRead ){ login_needed(); return; }
997 if( rid==0 ) fossil_redirect_home();
998 if( g.okAdmin ){
999 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1000 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1001 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
1002 g.zTop, zUuid);
1003 }else{
1004 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1005 g.zTop, zUuid);
1006 }
1007 }
1008 style_header("Hex Artifact Content");
1009 zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
1010 @ <h2>Artifact %s(zUuid):</h2>
1011 @ <blockquote><p>
1012 blob_zero(&downloadName);
1013 object_description(rid, 0, &downloadName);
1014 style_submenu_element("Download", "Download",
1015 "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
1016 @ </p></blockquote>
1017 @ <hr />
1018 content_get(rid, &content);
1019 @ <blockquote><pre>
1020 hexdump(&content);
1021 @ </pre></blockquote>
1022 style_footer();
@@ -1060,21 +1078,21 @@
1078 if( !g.okRead ){ login_needed(); return; }
1079 if( rid==0 ) fossil_redirect_home();
1080 if( g.okAdmin ){
1081 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1082 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1083 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
1084 g.zTop, zUuid);
1085 }else{
1086 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1087 g.zTop, zUuid);
1088 }
1089 }
1090 style_header("Artifact Content");
1091 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
1092 @ <h2>Artifact %s(zUuid)</h2>
1093 @ <blockquote><p>
1094 blob_zero(&downloadName);
1095 object_description(rid, 0, &downloadName);
1096 style_submenu_element("Download", "Download",
1097 "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
1098 zMime = mimetype_from_name(blob_str(&downloadName));
@@ -1084,25 +1102,25 @@
1102 style_submenu_element("Html", "Html",
1103 "%s/artifact?name=%s", g.zTop, zUuid);
1104 }else{
1105 renderAsHtml = 1;
1106 style_submenu_element("Text", "Text",
1107 "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
1108 }
1109 }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
1110 if( P("txt") ){
1111 style_submenu_element("Wiki", "Wiki",
1112 "%s/artifact?name=%s", g.zTop, zUuid);
1113 }else{
1114 renderAsWiki = 1;
1115 style_submenu_element("Text", "Text",
1116 "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
1117 }
1118 }
1119 }
1120 @ </p></blockquote>
1121 @ <hr />
1122 content_get(rid, &content);
1123 if( renderAsWiki ){
1124 wiki_convert(&content, 0, 0);
1125 }else if( renderAsHtml ){
1126 @ <div>
@@ -1115,11 +1133,11 @@
1133 @ <pre>
1134 @ %h(blob_str(&content))
1135 @ </pre>
1136 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1137 }else if( strncmp(zMime, "image/", 6)==0 ){
1138 @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&amp;m=%s(zMime)"></img>
1139 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1140 }else{
1141 @ <pre>
1142 hexdump(&content);
1143 @ </pre>
@@ -1148,11 +1166,11 @@
1166 rid = name_to_rid_www("name");
1167 if( rid==0 ){ fossil_redirect_home(); }
1168 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1169 if( g.okAdmin ){
1170 if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
1171 style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
1172 g.zTop, zUuid);
1173 }else{
1174 style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
1175 g.zTop, zUuid);
1176 }
@@ -1426,11 +1444,11 @@
1444 int nTag = 0;
1445 @ <b>Preview:</b>
1446 @ <blockquote>
1447 @ <table border=0>
1448 if( zNewColor && zNewColor[0] ){
1449 @ <tr><td style="background-color: %h(zNewColor);">
1450 }else{
1451 @ <tr><td>
1452 }
1453 wiki_convert(&comment, 0, WIKI_INLINE);
1454 blob_zero(&suffix);
@@ -1451,11 +1469,11 @@
1469 db_finalize(&q);
1470 blob_appendf(&suffix, ")");
1471 @ %s(blob_str(&suffix))
1472 @ </td></tr></table>
1473 @ </blockquote>
1474 @ <hr />
1475 blob_reset(&suffix);
1476 }
1477 @ <p>Make changes to attributes of check-in
1478 @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
1479 @ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
@@ -1489,11 +1507,11 @@
1507 }
1508 @ Propagate color to descendants</input></td></tr>
1509 @ <tr>
1510 for(i=0; i<nColor; i++){
1511 if( aColor[i].zColor[0] ){
1512 @ <td style="background-color: %h(aColor[i].zColor);">
1513 }else{
1514 @ <td>
1515 }
1516 if( strcmp(zNewColor, aColor[i].zColor)==0 ){
1517 @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
@@ -1525,13 +1543,13 @@
1543 int tagid = db_column_int(&q, 0);
1544 const char *zTagName = db_column_text(&q, 1);
1545 char zLabel[30];
1546 sprintf(zLabel, "c%d", tagid);
1547 if( P(zLabel) ){
1548 @ <br /><input type="checkbox" name="c%d(tagid)" checked>
1549 }else{
1550 @ <br /><input type="checkbox" name="c%d(tagid)">
1551 }
1552 if( strncmp(zTagName, "sym-", 4)==0 ){
1553 @ Cancel tag <b>%h(&zTagName[4])</b>
1554 }else{
1555 @ Cancel special tag <b>%h(zTagName)</b>
1556
+40 -38
--- src/login.c
+++ src/login.c
@@ -37,13 +37,15 @@
3737
** logs and downloading diffs of very version of the archive that
3838
** has ever existed, and things like that.
3939
*/
4040
#include "config.h"
4141
#include "login.h"
42
-#ifdef __MINGW32__
42
+#if defined(_WIN32)
4343
# include <windows.h> /* for Sleep */
44
-# define sleep Sleep /* windows does not have sleep, but Sleep */
44
+# if defined(__MINGW32__) || defined(_MSC_VER)
45
+# define sleep Sleep /* windows does not have sleep, but Sleep */
46
+# endif
4547
#endif
4648
#include <time.h>
4749
4850
/*
4951
** Return the name of the login cookie
@@ -151,21 +153,21 @@
151153
if( db_int(1, "SELECT 0 FROM user"
152154
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
153155
g.userUid, zPasswd, zSha1Pw) ){
154156
sleep(1);
155157
zErrMsg =
156
- @ <p><font color="red">
158
+ @ <p><span class="loginError">
157159
@ You entered an incorrect old password while attempting to change
158160
@ your password. Your password is unchanged.
159
- @ </font></p>
161
+ @ </span></p>
160162
;
161163
}else if( strcmp(zNew1,zNew2)!=0 ){
162164
zErrMsg =
163
- @ <p><font color="red">
165
+ @ <p><span class="loginError">
164166
@ The two copies of your new passwords do not match.
165167
@ Your password is unchanged.
166
- @ </font></p>
168
+ @ </span></p>
167169
;
168170
}else{
169171
char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
170172
db_multi_exec(
171173
"UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
@@ -204,13 +206,13 @@
204206
zUsername, zPasswd, zSha1Pw
205207
);
206208
if( uid<=0 ){
207209
sleep(1);
208210
zErrMsg =
209
- @ <p><font color="red">
211
+ @ <p><span class="loginError">
210212
@ You entered an unknown user or an incorrect password.
211
- @ </font></p>
213
+ @ </span></p>
212214
;
213215
}else{
214216
char *zCookie;
215217
const char *zCookieName = login_cookie_name();
216218
const char *zExpire = db_get("cookie-expire","8766");
@@ -227,38 +229,38 @@
227229
redirect_to_g();
228230
}
229231
}
230232
style_header("Login/Logout");
231233
@ %s(zErrMsg)
232
- @ <form action="login" method="POST">
234
+ @ <form action="login" method="post">
233235
if( P("g") ){
234
- @ <input type="hidden" name="g" value="%h(P("g"))">
236
+ @ <input type="hidden" name="g" value="%h(P("g"))" />
235237
}
236
- @ <table align="left" hspace="10">
238
+ @ <table class="login_out">
237239
@ <tr>
238
- @ <td align="right">User ID:</td>
240
+ @ <td class="login_out_label">User ID:</td>
239241
if( anonFlag ){
240
- @ <td><input type="text" id="u" name="u" value="anonymous" size=30></td>
242
+ @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
241243
}else{
242
- @ <td><input type="text" id="u" name="u" value="" size=30></td>
244
+ @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
243245
}
244246
@ </tr>
245247
@ <tr>
246
- @ <td align="right">Password:</td>
247
- @ <td><input type="password" id="p" name="p" value="" size=30></td>
248
+ @ <td class="login_out_label">Password:</td>
249
+ @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
248250
@ </tr>
249251
if( g.zLogin==0 ){
250252
zAnonPw = db_text(0, "SELECT pw FROM user"
251253
" WHERE login='anonymous'"
252254
" AND cap!=''");
253255
}
254256
@ <tr>
255257
@ <td></td>
256
- @ <td><input type="submit" name="in" value="Login"></td>
258
+ @ <td><input type="submit" name="in" value="Login" /></td>
257259
@ </tr>
258260
@ </table>
259
- @ <script>document.getElementById('u').focus()</script>
261
+ @ <script type="text/JavaScript">document.getElementById('u').focus()</script>
260262
if( g.zLogin==0 ){
261263
@ <p>Enter
262264
}else{
263265
@ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
264266
@ <p>To change your login to a different user, enter
@@ -271,46 +273,46 @@
271273
unsigned int uSeed = captcha_seed();
272274
char const *zDecoded = captcha_decode(uSeed);
273275
int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
274276
char *zCaptcha = captcha_render(zDecoded);
275277
276
- @ <input type="hidden" name="cs" value="%u(uSeed)"/>
277
- @ <p>Visitors may enter <b>anonymous</b> as the user-ID with
278
+ @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
279
+ @ Visitors may enter <b>anonymous</b> as the user-ID with
278280
@ the 8-character hexadecimal password shown below:</p>
279
- @ <center><table border="1" cellpadding="10"><tr><td><pre>
281
+ @ <div class="captcha"><table class="captcha"><tr><td><pre>
280282
@ %s(zCaptcha)
281283
@ </pre></td></tr></table>
282284
if( bAutoCaptcha ) {
283285
@ <input type="button" value="Fill out captcha"
284286
@ onclick="document.getElementById('u').value='anonymous';
285
- @ document.getElementById('p').value='%s(zDecoded)';"/>
287
+ @ document.getElementById('p').value='%s(zDecoded)';" />
286288
}
287
- @ </center>
289
+ @ </div>
288290
free(zCaptcha);
289291
}
290292
if( g.zLogin ){
291
- @ <br clear="both"><hr>
293
+ @ <hr />
292294
@ <p>To log off the system (and delete your login cookie)
293
- @ press the following button:<br>
294
- @ <input type="submit" name="out" value="Logout"></p>
295
+ @ press the following button:<br />
296
+ @ <input type="submit" name="out" value="Logout" /></p>
295297
}
296298
@ </form>
297299
if( g.okPassword ){
298
- @ <br clear="both"><hr>
300
+ @ <hr />
299301
@ <p>To change your password, enter your old password and your
300302
@ new password twice below then press the "Change Password"
301303
@ button.</p>
302
- @ <form action="login" method="POST">
304
+ @ <form action="login" method="post">
303305
@ <table>
304
- @ <tr><td align="right">Old Password:</td>
305
- @ <td><input type="password" name="p" size=30></td></tr>
306
- @ <tr><td align="right">New Password:</td>
307
- @ <td><input type="password" name="n1" size=30></td></tr>
308
- @ <tr><td align="right">Repeat New Password:</td>
309
- @ <td><input type="password" name="n2" size=30></td></tr>
306
+ @ <tr><td class="login_out_label">Old Password:</td>
307
+ @ <td><input type="password" name="p" size="30" /></td></tr>
308
+ @ <tr><td class="login_out_label">New Password:</td>
309
+ @ <td><input type="password" name="n1" size="30" /></td></tr>
310
+ @ <tr><td class="login_out_label">Repeat New Password:</td>
311
+ @ <td><input type="password" name="n2" size="30" /></td></tr>
310312
@ <tr><td></td>
311
- @ <td><input type="submit" value="Change Password"></td></tr>
313
+ @ <td><input type="submit" value="Change Password" /></td></tr>
312314
@ </table>
313315
@ </form>
314316
}
315317
style_footer();
316318
}
@@ -594,22 +596,22 @@
594596
if( !g.okHistory &&
595597
db_exists("SELECT 1 FROM user"
596598
" WHERE login='anonymous'"
597599
" AND cap LIKE '%%h%%'") ){
598600
const char *zUrl = PD("REQUEST_URI", "index");
599
- @ <p>Many <font color="red">hyperlinks are disabled.</font><br />
600
- @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
601
+ @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
602
+ @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
601603
@ to enable hyperlinks.</p>
602604
}
603605
}
604606
605607
/*
606608
** While rendering a form, call this routine to add the Anti-CSRF token
607609
** as a hidden element of the form.
608610
*/
609611
void login_insert_csrf_secret(void){
610
- @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
612
+ @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" />
611613
}
612614
613615
/*
614616
** Before using the results of a form, first call this routine to verify
615617
** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
616618
--- src/login.c
+++ src/login.c
@@ -37,13 +37,15 @@
37 ** logs and downloading diffs of very version of the archive that
38 ** has ever existed, and things like that.
39 */
40 #include "config.h"
41 #include "login.h"
42 #ifdef __MINGW32__
43 # include <windows.h> /* for Sleep */
44 # define sleep Sleep /* windows does not have sleep, but Sleep */
 
 
45 #endif
46 #include <time.h>
47
48 /*
49 ** Return the name of the login cookie
@@ -151,21 +153,21 @@
151 if( db_int(1, "SELECT 0 FROM user"
152 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
153 g.userUid, zPasswd, zSha1Pw) ){
154 sleep(1);
155 zErrMsg =
156 @ <p><font color="red">
157 @ You entered an incorrect old password while attempting to change
158 @ your password. Your password is unchanged.
159 @ </font></p>
160 ;
161 }else if( strcmp(zNew1,zNew2)!=0 ){
162 zErrMsg =
163 @ <p><font color="red">
164 @ The two copies of your new passwords do not match.
165 @ Your password is unchanged.
166 @ </font></p>
167 ;
168 }else{
169 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
170 db_multi_exec(
171 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
@@ -204,13 +206,13 @@
204 zUsername, zPasswd, zSha1Pw
205 );
206 if( uid<=0 ){
207 sleep(1);
208 zErrMsg =
209 @ <p><font color="red">
210 @ You entered an unknown user or an incorrect password.
211 @ </font></p>
212 ;
213 }else{
214 char *zCookie;
215 const char *zCookieName = login_cookie_name();
216 const char *zExpire = db_get("cookie-expire","8766");
@@ -227,38 +229,38 @@
227 redirect_to_g();
228 }
229 }
230 style_header("Login/Logout");
231 @ %s(zErrMsg)
232 @ <form action="login" method="POST">
233 if( P("g") ){
234 @ <input type="hidden" name="g" value="%h(P("g"))">
235 }
236 @ <table align="left" hspace="10">
237 @ <tr>
238 @ <td align="right">User ID:</td>
239 if( anonFlag ){
240 @ <td><input type="text" id="u" name="u" value="anonymous" size=30></td>
241 }else{
242 @ <td><input type="text" id="u" name="u" value="" size=30></td>
243 }
244 @ </tr>
245 @ <tr>
246 @ <td align="right">Password:</td>
247 @ <td><input type="password" id="p" name="p" value="" size=30></td>
248 @ </tr>
249 if( g.zLogin==0 ){
250 zAnonPw = db_text(0, "SELECT pw FROM user"
251 " WHERE login='anonymous'"
252 " AND cap!=''");
253 }
254 @ <tr>
255 @ <td></td>
256 @ <td><input type="submit" name="in" value="Login"></td>
257 @ </tr>
258 @ </table>
259 @ <script>document.getElementById('u').focus()</script>
260 if( g.zLogin==0 ){
261 @ <p>Enter
262 }else{
263 @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
264 @ <p>To change your login to a different user, enter
@@ -271,46 +273,46 @@
271 unsigned int uSeed = captcha_seed();
272 char const *zDecoded = captcha_decode(uSeed);
273 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
274 char *zCaptcha = captcha_render(zDecoded);
275
276 @ <input type="hidden" name="cs" value="%u(uSeed)"/>
277 @ <p>Visitors may enter <b>anonymous</b> as the user-ID with
278 @ the 8-character hexadecimal password shown below:</p>
279 @ <center><table border="1" cellpadding="10"><tr><td><pre>
280 @ %s(zCaptcha)
281 @ </pre></td></tr></table>
282 if( bAutoCaptcha ) {
283 @ <input type="button" value="Fill out captcha"
284 @ onclick="document.getElementById('u').value='anonymous';
285 @ document.getElementById('p').value='%s(zDecoded)';"/>
286 }
287 @ </center>
288 free(zCaptcha);
289 }
290 if( g.zLogin ){
291 @ <br clear="both"><hr>
292 @ <p>To log off the system (and delete your login cookie)
293 @ press the following button:<br>
294 @ <input type="submit" name="out" value="Logout"></p>
295 }
296 @ </form>
297 if( g.okPassword ){
298 @ <br clear="both"><hr>
299 @ <p>To change your password, enter your old password and your
300 @ new password twice below then press the "Change Password"
301 @ button.</p>
302 @ <form action="login" method="POST">
303 @ <table>
304 @ <tr><td align="right">Old Password:</td>
305 @ <td><input type="password" name="p" size=30></td></tr>
306 @ <tr><td align="right">New Password:</td>
307 @ <td><input type="password" name="n1" size=30></td></tr>
308 @ <tr><td align="right">Repeat New Password:</td>
309 @ <td><input type="password" name="n2" size=30></td></tr>
310 @ <tr><td></td>
311 @ <td><input type="submit" value="Change Password"></td></tr>
312 @ </table>
313 @ </form>
314 }
315 style_footer();
316 }
@@ -594,22 +596,22 @@
594 if( !g.okHistory &&
595 db_exists("SELECT 1 FROM user"
596 " WHERE login='anonymous'"
597 " AND cap LIKE '%%h%%'") ){
598 const char *zUrl = PD("REQUEST_URI", "index");
599 @ <p>Many <font color="red">hyperlinks are disabled.</font><br />
600 @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
601 @ to enable hyperlinks.</p>
602 }
603 }
604
605 /*
606 ** While rendering a form, call this routine to add the Anti-CSRF token
607 ** as a hidden element of the form.
608 */
609 void login_insert_csrf_secret(void){
610 @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
611 }
612
613 /*
614 ** Before using the results of a form, first call this routine to verify
615 ** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
616
--- src/login.c
+++ src/login.c
@@ -37,13 +37,15 @@
37 ** logs and downloading diffs of very version of the archive that
38 ** has ever existed, and things like that.
39 */
40 #include "config.h"
41 #include "login.h"
42 #if defined(_WIN32)
43 # include <windows.h> /* for Sleep */
44 # if defined(__MINGW32__) || defined(_MSC_VER)
45 # define sleep Sleep /* windows does not have sleep, but Sleep */
46 # endif
47 #endif
48 #include <time.h>
49
50 /*
51 ** Return the name of the login cookie
@@ -151,21 +153,21 @@
153 if( db_int(1, "SELECT 0 FROM user"
154 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
155 g.userUid, zPasswd, zSha1Pw) ){
156 sleep(1);
157 zErrMsg =
158 @ <p><span class="loginError">
159 @ You entered an incorrect old password while attempting to change
160 @ your password. Your password is unchanged.
161 @ </span></p>
162 ;
163 }else if( strcmp(zNew1,zNew2)!=0 ){
164 zErrMsg =
165 @ <p><span class="loginError">
166 @ The two copies of your new passwords do not match.
167 @ Your password is unchanged.
168 @ </span></p>
169 ;
170 }else{
171 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
172 db_multi_exec(
173 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
@@ -204,13 +206,13 @@
206 zUsername, zPasswd, zSha1Pw
207 );
208 if( uid<=0 ){
209 sleep(1);
210 zErrMsg =
211 @ <p><span class="loginError">
212 @ You entered an unknown user or an incorrect password.
213 @ </span></p>
214 ;
215 }else{
216 char *zCookie;
217 const char *zCookieName = login_cookie_name();
218 const char *zExpire = db_get("cookie-expire","8766");
@@ -227,38 +229,38 @@
229 redirect_to_g();
230 }
231 }
232 style_header("Login/Logout");
233 @ %s(zErrMsg)
234 @ <form action="login" method="post">
235 if( P("g") ){
236 @ <input type="hidden" name="g" value="%h(P("g"))" />
237 }
238 @ <table class="login_out">
239 @ <tr>
240 @ <td class="login_out_label">User ID:</td>
241 if( anonFlag ){
242 @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
243 }else{
244 @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
245 }
246 @ </tr>
247 @ <tr>
248 @ <td class="login_out_label">Password:</td>
249 @ <td><input type="password" id="p" name="p" value="" size="30" /></td>
250 @ </tr>
251 if( g.zLogin==0 ){
252 zAnonPw = db_text(0, "SELECT pw FROM user"
253 " WHERE login='anonymous'"
254 " AND cap!=''");
255 }
256 @ <tr>
257 @ <td></td>
258 @ <td><input type="submit" name="in" value="Login" /></td>
259 @ </tr>
260 @ </table>
261 @ <script type="text/JavaScript">document.getElementById('u').focus()</script>
262 if( g.zLogin==0 ){
263 @ <p>Enter
264 }else{
265 @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
266 @ <p>To change your login to a different user, enter
@@ -271,46 +273,46 @@
273 unsigned int uSeed = captcha_seed();
274 char const *zDecoded = captcha_decode(uSeed);
275 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
276 char *zCaptcha = captcha_render(zDecoded);
277
278 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
279 @ Visitors may enter <b>anonymous</b> as the user-ID with
280 @ the 8-character hexadecimal password shown below:</p>
281 @ <div class="captcha"><table class="captcha"><tr><td><pre>
282 @ %s(zCaptcha)
283 @ </pre></td></tr></table>
284 if( bAutoCaptcha ) {
285 @ <input type="button" value="Fill out captcha"
286 @ onclick="document.getElementById('u').value='anonymous';
287 @ document.getElementById('p').value='%s(zDecoded)';" />
288 }
289 @ </div>
290 free(zCaptcha);
291 }
292 if( g.zLogin ){
293 @ <hr />
294 @ <p>To log off the system (and delete your login cookie)
295 @ press the following button:<br />
296 @ <input type="submit" name="out" value="Logout" /></p>
297 }
298 @ </form>
299 if( g.okPassword ){
300 @ <hr />
301 @ <p>To change your password, enter your old password and your
302 @ new password twice below then press the "Change Password"
303 @ button.</p>
304 @ <form action="login" method="post">
305 @ <table>
306 @ <tr><td class="login_out_label">Old Password:</td>
307 @ <td><input type="password" name="p" size="30" /></td></tr>
308 @ <tr><td class="login_out_label">New Password:</td>
309 @ <td><input type="password" name="n1" size="30" /></td></tr>
310 @ <tr><td class="login_out_label">Repeat New Password:</td>
311 @ <td><input type="password" name="n2" size="30" /></td></tr>
312 @ <tr><td></td>
313 @ <td><input type="submit" value="Change Password" /></td></tr>
314 @ </table>
315 @ </form>
316 }
317 style_footer();
318 }
@@ -594,22 +596,22 @@
596 if( !g.okHistory &&
597 db_exists("SELECT 1 FROM user"
598 " WHERE login='anonymous'"
599 " AND cap LIKE '%%h%%'") ){
600 const char *zUrl = PD("REQUEST_URI", "index");
601 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
602 @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
603 @ to enable hyperlinks.</p>
604 }
605 }
606
607 /*
608 ** While rendering a form, call this routine to add the Anti-CSRF token
609 ** as a hidden element of the form.
610 */
611 void login_insert_csrf_secret(void){
612 @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" />
613 }
614
615 /*
616 ** Before using the results of a form, first call this routine to verify
617 ** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
618
+24 -11
--- src/main.c
+++ src/main.c
@@ -86,10 +86,11 @@
8686
int *aCommitFile; /* Array of files to be committed */
8787
int markPrivate; /* All new artifacts are private if true */
8888
8989
int urlIsFile; /* True if a "file:" url */
9090
int urlIsHttps; /* True if a "https:" url */
91
+ int urlIsSsh; /* True if an "ssh:" url */
9192
char *urlName; /* Hostname for http: or filename for file: */
9293
char *urlHostname; /* The HOST: parameter on http headers */
9394
char *urlProtocol; /* "http" or "https" */
9495
int urlPort; /* TCP port number for http: or https: */
9596
int urlDfltPort; /* The default port for the given protocol */
@@ -96,10 +97,11 @@
9697
char *urlPath; /* Pathname for http: */
9798
char *urlUser; /* User id for http: */
9899
char *urlPasswd; /* Password for http: */
99100
char *urlCanonical; /* Canonical representation of the URL */
100101
char *urlProxyAuth; /* Proxy-Authorizer: string */
102
+ char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
101103
int dontKeepUrl; /* Do not persist the URL */
102104
103105
const char *zLogin; /* Login name. "" if not logged in. */
104106
int noPswd; /* Logged in without password (on 127.0.0.1) */
105107
int userUid; /* Integer user id */
@@ -630,11 +632,11 @@
630632
**
631633
** Assume the user-id and group-id of the repository, or if zRepo
632634
** is a directory, of that directory.
633635
*/
634636
static char *enter_chroot_jail(char *zRepo){
635
-#if !defined(__MINGW32__)
637
+#if !defined(_WIN32)
636638
if( getuid()==0 ){
637639
int i;
638640
struct stat sStat;
639641
Blob dir;
640642
char *zDir;
@@ -808,11 +810,11 @@
808810
}else{
809811
zFile = g.argv[1];
810812
}
811813
g.httpOut = stdout;
812814
g.httpIn = stdin;
813
-#ifdef __MINGW32__
815
+#if defined(_WIN32)
814816
/* Set binary mode on windows to avoid undesired translations
815817
** between \n and \r\n. */
816818
setmode(_fileno(g.httpOut), _O_BINARY);
817819
setmode(_fileno(g.httpIn), _O_BINARY);
818820
#endif
@@ -914,14 +916,14 @@
914916
*/
915917
void cmd_http(void){
916918
const char *zIpAddr;
917919
const char *zNotFound;
918920
zNotFound = find_option("notfound", 0, 1);
921
+ g.cgiOutput = 1;
919922
if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
920
- cgi_panic("no repository specified");
923
+ fossil_fatal("no repository specified");
921924
}
922
- g.cgiOutput = 1;
923925
g.fullHttpReply = 1;
924926
if( g.argc==6 ){
925927
g.httpIn = fopen(g.argv[3], "rb");
926928
g.httpOut = fopen(g.argv[4], "wb");
927929
zIpAddr = g.argv[5];
@@ -935,19 +937,27 @@
935937
cgi_handle_http_request(zIpAddr);
936938
process_one_web_page(zNotFound);
937939
}
938940
939941
/*
942
+** Note that the following command is used by ssh:// processing.
943
+**
940944
** COMMAND: test-http
941945
** Works like the http command but gives setup permission to all users.
942946
*/
943947
void cmd_test_http(void){
944948
login_set_capabilities("s");
945
- cmd_http();
949
+ g.httpIn = stdin;
950
+ g.httpOut = stdout;
951
+ find_server_repository(0);
952
+ g.cgiOutput = 1;
953
+ g.fullHttpReply = 1;
954
+ cgi_handle_http_request(0);
955
+ process_one_web_page(0);
946956
}
947957
948
-#ifndef __MINGW32__
958
+#if !defined(_WIN32)
949959
#if !defined(__DARWIN__) && !defined(__APPLE__)
950960
/*
951961
** Search for an executable on the PATH environment variable.
952962
** Return true (1) if found and false (0) if not found.
953963
*/
@@ -982,11 +992,12 @@
982992
** --port option. The optional argument is the name of the repository.
983993
** The repository argument may be omitted if the working directory is
984994
** within an open checkout.
985995
**
986996
** The "ui" command automatically starts a web browser after initializing
987
-** the web server.
997
+** the web server. The "ui" command also binds to 127.0.0.1 and so will
998
+** only process HTTP traffic from the local machine.
988999
**
9891000
** In the "server" command, the REPOSITORY can be a directory (aka folder)
9901001
** that contains one or more respositories with names ending in ".fossil".
9911002
** In that case, the first element of the URL is used to select among the
9921003
** various repositories.
@@ -996,12 +1007,13 @@
9961007
const char *zPort; /* Value of the --port option */
9971008
char *zBrowser; /* Name of web browser program */
9981009
char *zBrowserCmd = 0; /* Command to launch the web browser */
9991010
int isUiCmd; /* True if command is "ui", not "server' */
10001011
const char *zNotFound; /* The --notfound option or NULL */
1012
+ int flags = 0; /* Server flags */
10011013
1002
-#ifdef __MINGW32__
1014
+#if defined(_WIN32)
10031015
const char *zStopperFile; /* Name of file used to terminate server */
10041016
zStopperFile = find_option("stopper", 0, 1);
10051017
#endif
10061018
10071019
g.thTrace = find_option("th-trace", 0, 0)!=0;
@@ -1010,18 +1022,19 @@
10101022
}
10111023
zPort = find_option("port", "P", 1);
10121024
zNotFound = find_option("notfound", 0, 1);
10131025
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
10141026
isUiCmd = g.argv[1][0]=='u';
1027
+ if( isUiCmd ) flags |= HTTP_SERVER_LOCALHOST;
10151028
find_server_repository(isUiCmd);
10161029
if( zPort ){
10171030
iPort = mxPort = atoi(zPort);
10181031
}else{
10191032
iPort = db_get_int("http-port", 8080);
10201033
mxPort = iPort+100;
10211034
}
1022
-#ifndef __MINGW32__
1035
+#if !defined(_WIN32)
10231036
/* Unix implementation */
10241037
if( isUiCmd ){
10251038
#if !defined(__DARWIN__) && !defined(__APPLE__)
10261039
zBrowser = db_get("web-browser", 0);
10271040
if( zBrowser==0 ){
@@ -1039,11 +1052,11 @@
10391052
zBrowser = db_get("web-browser", "open");
10401053
#endif
10411054
zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
10421055
}
10431056
db_close();
1044
- if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
1057
+ if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
10451058
fossil_fatal("unable to listen on TCP socket %d", iPort);
10461059
}
10471060
g.httpIn = stdin;
10481061
g.httpOut = stdout;
10491062
if( g.fHttpTrace || g.fSqlTrace ){
@@ -1059,8 +1072,8 @@
10591072
if( isUiCmd ){
10601073
zBrowser = db_get("web-browser", "start");
10611074
zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
10621075
}
10631076
db_close();
1064
- win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound);
1077
+ win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags);
10651078
#endif
10661079
}
10671080
--- src/main.c
+++ src/main.c
@@ -86,10 +86,11 @@
86 int *aCommitFile; /* Array of files to be committed */
87 int markPrivate; /* All new artifacts are private if true */
88
89 int urlIsFile; /* True if a "file:" url */
90 int urlIsHttps; /* True if a "https:" url */
 
91 char *urlName; /* Hostname for http: or filename for file: */
92 char *urlHostname; /* The HOST: parameter on http headers */
93 char *urlProtocol; /* "http" or "https" */
94 int urlPort; /* TCP port number for http: or https: */
95 int urlDfltPort; /* The default port for the given protocol */
@@ -96,10 +97,11 @@
96 char *urlPath; /* Pathname for http: */
97 char *urlUser; /* User id for http: */
98 char *urlPasswd; /* Password for http: */
99 char *urlCanonical; /* Canonical representation of the URL */
100 char *urlProxyAuth; /* Proxy-Authorizer: string */
 
101 int dontKeepUrl; /* Do not persist the URL */
102
103 const char *zLogin; /* Login name. "" if not logged in. */
104 int noPswd; /* Logged in without password (on 127.0.0.1) */
105 int userUid; /* Integer user id */
@@ -630,11 +632,11 @@
630 **
631 ** Assume the user-id and group-id of the repository, or if zRepo
632 ** is a directory, of that directory.
633 */
634 static char *enter_chroot_jail(char *zRepo){
635 #if !defined(__MINGW32__)
636 if( getuid()==0 ){
637 int i;
638 struct stat sStat;
639 Blob dir;
640 char *zDir;
@@ -808,11 +810,11 @@
808 }else{
809 zFile = g.argv[1];
810 }
811 g.httpOut = stdout;
812 g.httpIn = stdin;
813 #ifdef __MINGW32__
814 /* Set binary mode on windows to avoid undesired translations
815 ** between \n and \r\n. */
816 setmode(_fileno(g.httpOut), _O_BINARY);
817 setmode(_fileno(g.httpIn), _O_BINARY);
818 #endif
@@ -914,14 +916,14 @@
914 */
915 void cmd_http(void){
916 const char *zIpAddr;
917 const char *zNotFound;
918 zNotFound = find_option("notfound", 0, 1);
 
919 if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
920 cgi_panic("no repository specified");
921 }
922 g.cgiOutput = 1;
923 g.fullHttpReply = 1;
924 if( g.argc==6 ){
925 g.httpIn = fopen(g.argv[3], "rb");
926 g.httpOut = fopen(g.argv[4], "wb");
927 zIpAddr = g.argv[5];
@@ -935,19 +937,27 @@
935 cgi_handle_http_request(zIpAddr);
936 process_one_web_page(zNotFound);
937 }
938
939 /*
 
 
940 ** COMMAND: test-http
941 ** Works like the http command but gives setup permission to all users.
942 */
943 void cmd_test_http(void){
944 login_set_capabilities("s");
945 cmd_http();
 
 
 
 
 
 
946 }
947
948 #ifndef __MINGW32__
949 #if !defined(__DARWIN__) && !defined(__APPLE__)
950 /*
951 ** Search for an executable on the PATH environment variable.
952 ** Return true (1) if found and false (0) if not found.
953 */
@@ -982,11 +992,12 @@
982 ** --port option. The optional argument is the name of the repository.
983 ** The repository argument may be omitted if the working directory is
984 ** within an open checkout.
985 **
986 ** The "ui" command automatically starts a web browser after initializing
987 ** the web server.
 
988 **
989 ** In the "server" command, the REPOSITORY can be a directory (aka folder)
990 ** that contains one or more respositories with names ending in ".fossil".
991 ** In that case, the first element of the URL is used to select among the
992 ** various repositories.
@@ -996,12 +1007,13 @@
996 const char *zPort; /* Value of the --port option */
997 char *zBrowser; /* Name of web browser program */
998 char *zBrowserCmd = 0; /* Command to launch the web browser */
999 int isUiCmd; /* True if command is "ui", not "server' */
1000 const char *zNotFound; /* The --notfound option or NULL */
 
1001
1002 #ifdef __MINGW32__
1003 const char *zStopperFile; /* Name of file used to terminate server */
1004 zStopperFile = find_option("stopper", 0, 1);
1005 #endif
1006
1007 g.thTrace = find_option("th-trace", 0, 0)!=0;
@@ -1010,18 +1022,19 @@
1010 }
1011 zPort = find_option("port", "P", 1);
1012 zNotFound = find_option("notfound", 0, 1);
1013 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
1014 isUiCmd = g.argv[1][0]=='u';
 
1015 find_server_repository(isUiCmd);
1016 if( zPort ){
1017 iPort = mxPort = atoi(zPort);
1018 }else{
1019 iPort = db_get_int("http-port", 8080);
1020 mxPort = iPort+100;
1021 }
1022 #ifndef __MINGW32__
1023 /* Unix implementation */
1024 if( isUiCmd ){
1025 #if !defined(__DARWIN__) && !defined(__APPLE__)
1026 zBrowser = db_get("web-browser", 0);
1027 if( zBrowser==0 ){
@@ -1039,11 +1052,11 @@
1039 zBrowser = db_get("web-browser", "open");
1040 #endif
1041 zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
1042 }
1043 db_close();
1044 if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
1045 fossil_fatal("unable to listen on TCP socket %d", iPort);
1046 }
1047 g.httpIn = stdin;
1048 g.httpOut = stdout;
1049 if( g.fHttpTrace || g.fSqlTrace ){
@@ -1059,8 +1072,8 @@
1059 if( isUiCmd ){
1060 zBrowser = db_get("web-browser", "start");
1061 zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
1062 }
1063 db_close();
1064 win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound);
1065 #endif
1066 }
1067
--- src/main.c
+++ src/main.c
@@ -86,10 +86,11 @@
86 int *aCommitFile; /* Array of files to be committed */
87 int markPrivate; /* All new artifacts are private if true */
88
89 int urlIsFile; /* True if a "file:" url */
90 int urlIsHttps; /* True if a "https:" url */
91 int urlIsSsh; /* True if an "ssh:" url */
92 char *urlName; /* Hostname for http: or filename for file: */
93 char *urlHostname; /* The HOST: parameter on http headers */
94 char *urlProtocol; /* "http" or "https" */
95 int urlPort; /* TCP port number for http: or https: */
96 int urlDfltPort; /* The default port for the given protocol */
@@ -96,10 +97,11 @@
97 char *urlPath; /* Pathname for http: */
98 char *urlUser; /* User id for http: */
99 char *urlPasswd; /* Password for http: */
100 char *urlCanonical; /* Canonical representation of the URL */
101 char *urlProxyAuth; /* Proxy-Authorizer: string */
102 char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
103 int dontKeepUrl; /* Do not persist the URL */
104
105 const char *zLogin; /* Login name. "" if not logged in. */
106 int noPswd; /* Logged in without password (on 127.0.0.1) */
107 int userUid; /* Integer user id */
@@ -630,11 +632,11 @@
632 **
633 ** Assume the user-id and group-id of the repository, or if zRepo
634 ** is a directory, of that directory.
635 */
636 static char *enter_chroot_jail(char *zRepo){
637 #if !defined(_WIN32)
638 if( getuid()==0 ){
639 int i;
640 struct stat sStat;
641 Blob dir;
642 char *zDir;
@@ -808,11 +810,11 @@
810 }else{
811 zFile = g.argv[1];
812 }
813 g.httpOut = stdout;
814 g.httpIn = stdin;
815 #if defined(_WIN32)
816 /* Set binary mode on windows to avoid undesired translations
817 ** between \n and \r\n. */
818 setmode(_fileno(g.httpOut), _O_BINARY);
819 setmode(_fileno(g.httpIn), _O_BINARY);
820 #endif
@@ -914,14 +916,14 @@
916 */
917 void cmd_http(void){
918 const char *zIpAddr;
919 const char *zNotFound;
920 zNotFound = find_option("notfound", 0, 1);
921 g.cgiOutput = 1;
922 if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){
923 fossil_fatal("no repository specified");
924 }
 
925 g.fullHttpReply = 1;
926 if( g.argc==6 ){
927 g.httpIn = fopen(g.argv[3], "rb");
928 g.httpOut = fopen(g.argv[4], "wb");
929 zIpAddr = g.argv[5];
@@ -935,19 +937,27 @@
937 cgi_handle_http_request(zIpAddr);
938 process_one_web_page(zNotFound);
939 }
940
941 /*
942 ** Note that the following command is used by ssh:// processing.
943 **
944 ** COMMAND: test-http
945 ** Works like the http command but gives setup permission to all users.
946 */
947 void cmd_test_http(void){
948 login_set_capabilities("s");
949 g.httpIn = stdin;
950 g.httpOut = stdout;
951 find_server_repository(0);
952 g.cgiOutput = 1;
953 g.fullHttpReply = 1;
954 cgi_handle_http_request(0);
955 process_one_web_page(0);
956 }
957
958 #if !defined(_WIN32)
959 #if !defined(__DARWIN__) && !defined(__APPLE__)
960 /*
961 ** Search for an executable on the PATH environment variable.
962 ** Return true (1) if found and false (0) if not found.
963 */
@@ -982,11 +992,12 @@
992 ** --port option. The optional argument is the name of the repository.
993 ** The repository argument may be omitted if the working directory is
994 ** within an open checkout.
995 **
996 ** The "ui" command automatically starts a web browser after initializing
997 ** the web server. The "ui" command also binds to 127.0.0.1 and so will
998 ** only process HTTP traffic from the local machine.
999 **
1000 ** In the "server" command, the REPOSITORY can be a directory (aka folder)
1001 ** that contains one or more respositories with names ending in ".fossil".
1002 ** In that case, the first element of the URL is used to select among the
1003 ** various repositories.
@@ -996,12 +1007,13 @@
1007 const char *zPort; /* Value of the --port option */
1008 char *zBrowser; /* Name of web browser program */
1009 char *zBrowserCmd = 0; /* Command to launch the web browser */
1010 int isUiCmd; /* True if command is "ui", not "server' */
1011 const char *zNotFound; /* The --notfound option or NULL */
1012 int flags = 0; /* Server flags */
1013
1014 #if defined(_WIN32)
1015 const char *zStopperFile; /* Name of file used to terminate server */
1016 zStopperFile = find_option("stopper", 0, 1);
1017 #endif
1018
1019 g.thTrace = find_option("th-trace", 0, 0)!=0;
@@ -1010,18 +1022,19 @@
1022 }
1023 zPort = find_option("port", "P", 1);
1024 zNotFound = find_option("notfound", 0, 1);
1025 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
1026 isUiCmd = g.argv[1][0]=='u';
1027 if( isUiCmd ) flags |= HTTP_SERVER_LOCALHOST;
1028 find_server_repository(isUiCmd);
1029 if( zPort ){
1030 iPort = mxPort = atoi(zPort);
1031 }else{
1032 iPort = db_get_int("http-port", 8080);
1033 mxPort = iPort+100;
1034 }
1035 #if !defined(_WIN32)
1036 /* Unix implementation */
1037 if( isUiCmd ){
1038 #if !defined(__DARWIN__) && !defined(__APPLE__)
1039 zBrowser = db_get("web-browser", 0);
1040 if( zBrowser==0 ){
@@ -1039,11 +1052,11 @@
1052 zBrowser = db_get("web-browser", "open");
1053 #endif
1054 zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
1055 }
1056 db_close();
1057 if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
1058 fossil_fatal("unable to listen on TCP socket %d", iPort);
1059 }
1060 g.httpIn = stdin;
1061 g.httpOut = stdout;
1062 if( g.fHttpTrace || g.fSqlTrace ){
@@ -1059,8 +1072,8 @@
1072 if( isUiCmd ){
1073 zBrowser = db_get("web-browser", "start");
1074 zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
1075 }
1076 db_close();
1077 win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags);
1078 #endif
1079 }
1080
+12 -2
--- src/main.mk
+++ src/main.mk
@@ -51,10 +51,11 @@
5151
$(SRCDIR)/md5.c \
5252
$(SRCDIR)/merge.c \
5353
$(SRCDIR)/merge3.c \
5454
$(SRCDIR)/name.c \
5555
$(SRCDIR)/pivot.c \
56
+ $(SRCDIR)/popen.c \
5657
$(SRCDIR)/pqueue.c \
5758
$(SRCDIR)/printf.c \
5859
$(SRCDIR)/rebuild.c \
5960
$(SRCDIR)/report.c \
6061
$(SRCDIR)/rss.c \
@@ -123,10 +124,11 @@
123124
md5_.c \
124125
merge_.c \
125126
merge3_.c \
126127
name_.c \
127128
pivot_.c \
129
+ popen_.c \
128130
pqueue_.c \
129131
printf_.c \
130132
rebuild_.c \
131133
report_.c \
132134
rss_.c \
@@ -195,10 +197,11 @@
195197
$(OBJDIR)/md5.o \
196198
$(OBJDIR)/merge.o \
197199
$(OBJDIR)/merge3.o \
198200
$(OBJDIR)/name.o \
199201
$(OBJDIR)/pivot.o \
202
+ $(OBJDIR)/popen.o \
200203
$(OBJDIR)/pqueue.o \
201204
$(OBJDIR)/printf.o \
202205
$(OBJDIR)/rebuild.o \
203206
$(OBJDIR)/report.o \
204207
$(OBJDIR)/rss.o \
@@ -270,16 +273,16 @@
270273
# noop
271274
272275
clean:
273276
rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
274277
rm -f translate makeheaders mkindex page_index.h headers
275
- rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
278
+ rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
276279
277280
page_index.h: $(TRANS_SRC) mkindex
278281
./mkindex $(TRANS_SRC) >$@
279282
headers: page_index.h makeheaders VERSION.h
280
- ./makeheaders 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 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
283
+ ./makeheaders 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
281284
touch headers
282285
headers: Makefile
283286
Makefile:
284287
add_.c: $(SRCDIR)/add.c translate
285288
./translate $(SRCDIR)/add.c >add_.c
@@ -559,10 +562,17 @@
559562
560563
$(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h
561564
$(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c
562565
563566
pivot.h: headers
567
+popen_.c: $(SRCDIR)/popen.c translate
568
+ ./translate $(SRCDIR)/popen.c >popen_.c
569
+
570
+$(OBJDIR)/popen.o: popen_.c popen.h $(SRCDIR)/config.h
571
+ $(XTCC) -o $(OBJDIR)/popen.o -c popen_.c
572
+
573
+popen.h: headers
564574
pqueue_.c: $(SRCDIR)/pqueue.c translate
565575
./translate $(SRCDIR)/pqueue.c >pqueue_.c
566576
567577
$(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h
568578
$(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c
569579
--- src/main.mk
+++ src/main.mk
@@ -51,10 +51,11 @@
51 $(SRCDIR)/md5.c \
52 $(SRCDIR)/merge.c \
53 $(SRCDIR)/merge3.c \
54 $(SRCDIR)/name.c \
55 $(SRCDIR)/pivot.c \
 
56 $(SRCDIR)/pqueue.c \
57 $(SRCDIR)/printf.c \
58 $(SRCDIR)/rebuild.c \
59 $(SRCDIR)/report.c \
60 $(SRCDIR)/rss.c \
@@ -123,10 +124,11 @@
123 md5_.c \
124 merge_.c \
125 merge3_.c \
126 name_.c \
127 pivot_.c \
 
128 pqueue_.c \
129 printf_.c \
130 rebuild_.c \
131 report_.c \
132 rss_.c \
@@ -195,10 +197,11 @@
195 $(OBJDIR)/md5.o \
196 $(OBJDIR)/merge.o \
197 $(OBJDIR)/merge3.o \
198 $(OBJDIR)/name.o \
199 $(OBJDIR)/pivot.o \
 
200 $(OBJDIR)/pqueue.o \
201 $(OBJDIR)/printf.o \
202 $(OBJDIR)/rebuild.o \
203 $(OBJDIR)/report.o \
204 $(OBJDIR)/rss.o \
@@ -270,16 +273,16 @@
270 # noop
271
272 clean:
273 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
274 rm -f translate makeheaders mkindex page_index.h headers
275 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
276
277 page_index.h: $(TRANS_SRC) mkindex
278 ./mkindex $(TRANS_SRC) >$@
279 headers: page_index.h makeheaders VERSION.h
280 ./makeheaders 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 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
281 touch headers
282 headers: Makefile
283 Makefile:
284 add_.c: $(SRCDIR)/add.c translate
285 ./translate $(SRCDIR)/add.c >add_.c
@@ -559,10 +562,17 @@
559
560 $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h
561 $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c
562
563 pivot.h: headers
 
 
 
 
 
 
 
564 pqueue_.c: $(SRCDIR)/pqueue.c translate
565 ./translate $(SRCDIR)/pqueue.c >pqueue_.c
566
567 $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h
568 $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c
569
--- src/main.mk
+++ src/main.mk
@@ -51,10 +51,11 @@
51 $(SRCDIR)/md5.c \
52 $(SRCDIR)/merge.c \
53 $(SRCDIR)/merge3.c \
54 $(SRCDIR)/name.c \
55 $(SRCDIR)/pivot.c \
56 $(SRCDIR)/popen.c \
57 $(SRCDIR)/pqueue.c \
58 $(SRCDIR)/printf.c \
59 $(SRCDIR)/rebuild.c \
60 $(SRCDIR)/report.c \
61 $(SRCDIR)/rss.c \
@@ -123,10 +124,11 @@
124 md5_.c \
125 merge_.c \
126 merge3_.c \
127 name_.c \
128 pivot_.c \
129 popen_.c \
130 pqueue_.c \
131 printf_.c \
132 rebuild_.c \
133 report_.c \
134 rss_.c \
@@ -195,10 +197,11 @@
197 $(OBJDIR)/md5.o \
198 $(OBJDIR)/merge.o \
199 $(OBJDIR)/merge3.o \
200 $(OBJDIR)/name.o \
201 $(OBJDIR)/pivot.o \
202 $(OBJDIR)/popen.o \
203 $(OBJDIR)/pqueue.o \
204 $(OBJDIR)/printf.o \
205 $(OBJDIR)/rebuild.o \
206 $(OBJDIR)/report.o \
207 $(OBJDIR)/rss.o \
@@ -270,16 +273,16 @@
273 # noop
274
275 clean:
276 rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
277 rm -f translate makeheaders mkindex page_index.h headers
278 rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
279
280 page_index.h: $(TRANS_SRC) mkindex
281 ./mkindex $(TRANS_SRC) >$@
282 headers: page_index.h makeheaders VERSION.h
283 ./makeheaders 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
284 touch headers
285 headers: Makefile
286 Makefile:
287 add_.c: $(SRCDIR)/add.c translate
288 ./translate $(SRCDIR)/add.c >add_.c
@@ -559,10 +562,17 @@
562
563 $(OBJDIR)/pivot.o: pivot_.c pivot.h $(SRCDIR)/config.h
564 $(XTCC) -o $(OBJDIR)/pivot.o -c pivot_.c
565
566 pivot.h: headers
567 popen_.c: $(SRCDIR)/popen.c translate
568 ./translate $(SRCDIR)/popen.c >popen_.c
569
570 $(OBJDIR)/popen.o: popen_.c popen.h $(SRCDIR)/config.h
571 $(XTCC) -o $(OBJDIR)/popen.o -c popen_.c
572
573 popen.h: headers
574 pqueue_.c: $(SRCDIR)/pqueue.c translate
575 ./translate $(SRCDIR)/pqueue.c >pqueue_.c
576
577 $(OBJDIR)/pqueue.o: pqueue_.c pqueue.h $(SRCDIR)/config.h
578 $(XTCC) -o $(OBJDIR)/pqueue.o -c pqueue_.c
579
+14 -11
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -13,14 +13,17 @@
1313
#include <stdlib.h>
1414
#include <ctype.h>
1515
#include <memory.h>
1616
#include <sys/stat.h>
1717
#include <assert.h>
18
-#ifndef WIN32
19
-# include <unistd.h>
20
-#else
18
+#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER)
19
+# ifndef WIN32
20
+# define WIN32
21
+# endif
2122
# include <string.h>
23
+#else
24
+# include <unistd.h>
2225
#endif
2326
2427
/*
2528
** Macros for debugging.
2629
*/
@@ -2141,11 +2144,11 @@
21412144
zArg = &zCmd[2];
21422145
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
21432146
zArg++;
21442147
}
21452148
if( *zArg==0 || *zArg=='\n' ){ return 0; }
2146
- nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2149
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
21472150
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
21482151
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
21492152
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
21502153
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
21512154
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
@@ -2160,11 +2163,11 @@
21602163
zArg = &zCmd[5];
21612164
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
21622165
zArg++;
21632166
}
21642167
if( *zArg==0 || *zArg=='\n' ){ return 0; }
2165
- nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2168
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
21662169
PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
21672170
}else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
21682171
/*
21692172
** Push an #ifndef.
21702173
*/
@@ -2171,11 +2174,11 @@
21712174
zArg = &zCmd[6];
21722175
while( *zArg && isspace(*zArg) && *zArg!='\n' ){
21732176
zArg++;
21742177
}
21752178
if( *zArg==0 || *zArg=='\n' ){ return 0; }
2176
- nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2179
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
21772180
PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
21782181
}else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
21792182
/*
21802183
** Invert the #if on the top of the stack
21812184
*/
@@ -2797,11 +2800,11 @@
27972800
}else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
27982801
if( report ) fprintf(report,"error!\n");
27992802
fprintf(stderr,
28002803
"%s: Can't overwrite this file because it wasn't previously\n"
28012804
"%*s generated by 'makeheaders'.\n",
2802
- pFile->zHdr, strlen(pFile->zHdr), "");
2805
+ pFile->zHdr, (int)strlen(pFile->zHdr), "");
28032806
nErr++;
28042807
}else if( strcmp(zOldVersion,zNewVersion)!=0 ){
28052808
if( report ) fprintf(report,"updated\n");
28062809
if( WriteFile(pFile->zHdr,zNewVersion) ){
28072810
fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
@@ -2952,18 +2955,18 @@
29522955
if( nLabel==0 ) continue;
29532956
zLabel[nLabel] = 0;
29542957
InsertExtraDecl(pDecl);
29552958
zDecl = pDecl->zDecl;
29562959
if( zDecl==0 ) zDecl = pDecl->zFwd;
2957
- printf("%s %s %s %d %d %d %d %d %d\n",
2960
+ printf("%s %s %s %p %d %d %d %d %d\n",
29582961
pDecl->zName,
29592962
zLabel,
29602963
pDecl->zFile,
2961
- pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0,
2964
+ pDecl->pComment,
29622965
pDecl->pComment ? pDecl->pComment->nText+1 : 0,
2963
- pDecl->zIf ? strlen(pDecl->zIf)+1 : 0,
2964
- zDecl ? strlen(zDecl) : 0,
2966
+ pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
2967
+ zDecl ? (int)strlen(zDecl) : 0,
29652968
pDecl->pComment ? pDecl->pComment->nLine : 0,
29662969
pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
29672970
);
29682971
if( pDecl->pComment ){
29692972
printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
29702973
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -13,14 +13,17 @@
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <memory.h>
16 #include <sys/stat.h>
17 #include <assert.h>
18 #ifndef WIN32
19 # include <unistd.h>
20 #else
 
21 # include <string.h>
 
 
22 #endif
23
24 /*
25 ** Macros for debugging.
26 */
@@ -2141,11 +2144,11 @@
2141 zArg = &zCmd[2];
2142 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2143 zArg++;
2144 }
2145 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2146 nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2147 if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
2148 PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
2149 }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
2150 PushIfMacro(0,0,0,pToken->nLine,PS_Export);
2151 }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
@@ -2160,11 +2163,11 @@
2160 zArg = &zCmd[5];
2161 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2162 zArg++;
2163 }
2164 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2165 nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2166 PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
2167 }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
2168 /*
2169 ** Push an #ifndef.
2170 */
@@ -2171,11 +2174,11 @@
2171 zArg = &zCmd[6];
2172 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2173 zArg++;
2174 }
2175 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2176 nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2177 PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
2178 }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
2179 /*
2180 ** Invert the #if on the top of the stack
2181 */
@@ -2797,11 +2800,11 @@
2797 }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
2798 if( report ) fprintf(report,"error!\n");
2799 fprintf(stderr,
2800 "%s: Can't overwrite this file because it wasn't previously\n"
2801 "%*s generated by 'makeheaders'.\n",
2802 pFile->zHdr, strlen(pFile->zHdr), "");
2803 nErr++;
2804 }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
2805 if( report ) fprintf(report,"updated\n");
2806 if( WriteFile(pFile->zHdr,zNewVersion) ){
2807 fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
@@ -2952,18 +2955,18 @@
2952 if( nLabel==0 ) continue;
2953 zLabel[nLabel] = 0;
2954 InsertExtraDecl(pDecl);
2955 zDecl = pDecl->zDecl;
2956 if( zDecl==0 ) zDecl = pDecl->zFwd;
2957 printf("%s %s %s %d %d %d %d %d %d\n",
2958 pDecl->zName,
2959 zLabel,
2960 pDecl->zFile,
2961 pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0,
2962 pDecl->pComment ? pDecl->pComment->nText+1 : 0,
2963 pDecl->zIf ? strlen(pDecl->zIf)+1 : 0,
2964 zDecl ? strlen(zDecl) : 0,
2965 pDecl->pComment ? pDecl->pComment->nLine : 0,
2966 pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
2967 );
2968 if( pDecl->pComment ){
2969 printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
2970
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -13,14 +13,17 @@
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 # include <unistd.h>
25 #endif
26
27 /*
28 ** Macros for debugging.
29 */
@@ -2141,11 +2144,11 @@
2144 zArg = &zCmd[2];
2145 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2146 zArg++;
2147 }
2148 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2149 nArg = pToken->nText + (int)(pToken->zText - zArg);
2150 if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
2151 PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
2152 }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
2153 PushIfMacro(0,0,0,pToken->nLine,PS_Export);
2154 }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
@@ -2160,11 +2163,11 @@
2163 zArg = &zCmd[5];
2164 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2165 zArg++;
2166 }
2167 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2168 nArg = pToken->nText + (int)(pToken->zText - zArg);
2169 PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
2170 }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
2171 /*
2172 ** Push an #ifndef.
2173 */
@@ -2171,11 +2174,11 @@
2174 zArg = &zCmd[6];
2175 while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2176 zArg++;
2177 }
2178 if( *zArg==0 || *zArg=='\n' ){ return 0; }
2179 nArg = pToken->nText + (int)(pToken->zText - zArg);
2180 PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
2181 }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
2182 /*
2183 ** Invert the #if on the top of the stack
2184 */
@@ -2797,11 +2800,11 @@
2800 }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
2801 if( report ) fprintf(report,"error!\n");
2802 fprintf(stderr,
2803 "%s: Can't overwrite this file because it wasn't previously\n"
2804 "%*s generated by 'makeheaders'.\n",
2805 pFile->zHdr, (int)strlen(pFile->zHdr), "");
2806 nErr++;
2807 }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
2808 if( report ) fprintf(report,"updated\n");
2809 if( WriteFile(pFile->zHdr,zNewVersion) ){
2810 fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
@@ -2952,18 +2955,18 @@
2955 if( nLabel==0 ) continue;
2956 zLabel[nLabel] = 0;
2957 InsertExtraDecl(pDecl);
2958 zDecl = pDecl->zDecl;
2959 if( zDecl==0 ) zDecl = pDecl->zFwd;
2960 printf("%s %s %s %p %d %d %d %d %d\n",
2961 pDecl->zName,
2962 zLabel,
2963 pDecl->zFile,
2964 pDecl->pComment,
2965 pDecl->pComment ? pDecl->pComment->nText+1 : 0,
2966 pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
2967 zDecl ? (int)strlen(zDecl) : 0,
2968 pDecl->pComment ? pDecl->pComment->nLine : 0,
2969 pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
2970 );
2971 if( pDecl->pComment ){
2972 printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
2973
+243 -1
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -44,10 +44,11 @@
4444
md5
4545
merge
4646
merge3
4747
name
4848
pivot
49
+ popen
4950
pqueue
5051
printf
5152
rebuild
5253
report
5354
rss
@@ -80,11 +81,11 @@
8081
}
8182
8283
# Name of the final application
8384
#
8485
set name fossil
85
-
86
+if { 0 == $argc } {
8687
puts {# DO NOT EDIT
8788
#
8889
# This file is automatically generated. Instead of editing this
8990
# file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
9091
# to regenerate this file.
@@ -204,5 +205,246 @@
204205
puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
205206
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
206207
207208
puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
208209
puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
210
+exit
211
+}
212
+if { "dmc" == [lindex $argv 0] } {
213
+
214
+puts {# DO NOT EDIT
215
+#
216
+# This file is automatically generated. Instead of editing this
217
+# file, edit "makemake.tcl" then run
218
+# "tclsh src/makemake.tcl dmc > win/Makefile.dmc"
219
+# to regenerate this file.
220
+B = ..
221
+SRCDIR = $B\src
222
+OBJDIR = .
223
+O = .obj
224
+E = .exe
225
+
226
+
227
+# Maybe DMDIR, SSL or INCL needs adjustment
228
+DMDIR = c:\DM
229
+INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include
230
+
231
+#SSL = -DFOSSIL_ENABLE_SSL=1
232
+SSL =
233
+
234
+DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
235
+I18N = -DFOSSIL_I18N=0
236
+
237
+CFLAGS = -o
238
+BCC = $(DMDIR)\bin\dmc $(CFLAGS)
239
+TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL)
240
+LIBS = $(DMDIR)\extra\lib\ zlib wsock32
241
+}
242
+puts -nonewline "SRC = "
243
+foreach s [lsort $src] {
244
+ puts -nonewline "${s}_.c "
245
+}
246
+puts "\n"
247
+puts -nonewline "OBJ = "
248
+foreach s [lsort $src] {
249
+ puts -nonewline "\$(OBJDIR)\\$s\$O "
250
+}
251
+puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
252
+puts {
253
+
254
+APPNAME = $(OBJDIR)\fossil$(E)
255
+
256
+all: $(APPNAME)
257
+
258
+$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link
259
+ cd $(OBJDIR)
260
+ $(DMDIR)\bin\link @link
261
+
262
+$(OBJDIR)\link: $B\win\Makefile.dmc}
263
+puts -nonewline "\t+echo "
264
+foreach s [lsort $src] {
265
+ puts -nonewline "$s "
266
+}
267
+puts "sqlite3 th th_lang > \$@"
268
+puts "\t+echo fossil >> \$@"
269
+puts "\t+echo fossil >> \$@"
270
+puts "\t+echo \$(LIBS) >> \$@\n\n"
271
+
272
+puts {
273
+translate$E: $(SRCDIR)\translate.c
274
+ $(BCC) -o$@ $**
275
+
276
+makeheaders$E: $(SRCDIR)\makeheaders.c
277
+ $(BCC) -o$@ $**
278
+
279
+mkindex$E: $(SRCDIR)\mkindex.c
280
+ $(BCC) -o$@ $**
281
+
282
+version$E: $B\win\version.c
283
+ $(BCC) -o$@ $**
284
+
285
+$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
286
+ $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
287
+
288
+$(OBJDIR)\th$O : $(SRCDIR)\th.c
289
+ $(TCC) -o$@ -c $**
290
+
291
+$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
292
+ $(TCC) -o$@ -c $**
293
+
294
+VERSION.h : version$E $B\manifest.uuid $B\manifest
295
+ +$** > $@
296
+
297
+page_index.h: mkindex$E $(SRC)
298
+ +$** > $@
299
+
300
+clean:
301
+ -del $(OBJDIR)\*.obj
302
+ -del *.obj *_.c *.h *.map
303
+
304
+realclean:
305
+ -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
306
+
307
+}
308
+foreach s [lsort $src] {
309
+ puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
310
+ puts "\t\$(TCC) -o\$@ -c ${s}_.c\n"
311
+ puts "${s}_.c : \$(SRCDIR)\\$s.c"
312
+ puts "\t+translate\$E \$** > \$@\n"
313
+}
314
+
315
+puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
316
+foreach s [lsort $src] {
317
+ puts -nonewline "${s}_.c:$s.h "
318
+}
319
+puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
320
+puts "\t@copy /Y nul: headers"
321
+exit
322
+}
323
+
324
+if { "msc" == [lindex $argv 0] } {
325
+
326
+puts {# DO NOT EDIT
327
+#
328
+# This file is automatically generated. Instead of editing this
329
+# file, edit "makemake.tcl" then run
330
+# "tclsh src/makemake.tcl msc > win/Makefile.msc"
331
+# to regenerate this file.
332
+B = ..
333
+SRCDIR = $B\src
334
+OBJDIR = .
335
+O = .obj
336
+E = .exe
337
+
338
+# Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment
339
+MSCDIR = c:\msc
340
+
341
+# Uncomment below for SSL support
342
+SSL =
343
+SSLLIB =
344
+#SSL = -DFOSSIL_ENABLE_SSL=1
345
+#SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
346
+
347
+# zlib options
348
+# When using precompiled from http://zlib.net/zlib125-dll.zip
349
+#ZINCDIR = C:\zlib125-dll\include
350
+#ZLIBDIR = C:\zlib125-dll\lib
351
+#ZLIB = zdll.lib
352
+ZINCDIR = $(MSCDIR)\extra\include
353
+ZLIBDIR = $(MSCDIR)\extra\lib
354
+ZLIB = zlib.lib
355
+
356
+INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
357
+
358
+MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
359
+I18N = -DFOSSIL_I18N=0
360
+
361
+CFLAGS = -nologo -MT -O2
362
+BCC = $(CC) $(CFLAGS)
363
+TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL)
364
+LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
365
+LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
366
+}
367
+puts -nonewline "SRC = "
368
+foreach s [lsort $src] {
369
+ puts -nonewline "${s}_.c "
370
+}
371
+puts "\n"
372
+puts -nonewline "OBJ = "
373
+foreach s [lsort $src] {
374
+ puts -nonewline "\$(OBJDIR)\\$s\$O "
375
+}
376
+puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
377
+puts {
378
+
379
+APPNAME = $(OBJDIR)\fossil$(E)
380
+
381
+all: $(OBJDIR) $(APPNAME)
382
+
383
+$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts
384
+ cd $(OBJDIR)
385
+ link -LINK -OUT:$@ $(LIBDIR) @linkopts
386
+
387
+$(OBJDIR)\linkopts: $B\win\Makefile.msc}
388
+puts -nonewline "\techo "
389
+foreach s [lsort $src] {
390
+ puts -nonewline "$s "
391
+}
392
+puts "sqlite3 th th_lang > \$@"
393
+puts "\techo \$(LIBS) >> \$@\n\n"
394
+
395
+puts {
396
+
397
+$(OBJDIR):
398
+ @-mkdir $@
399
+
400
+translate$E: $(SRCDIR)\translate.c
401
+ $(BCC) $**
402
+
403
+makeheaders$E: $(SRCDIR)\makeheaders.c
404
+ $(BCC) $**
405
+
406
+mkindex$E: $(SRCDIR)\mkindex.c
407
+ $(BCC) $**
408
+
409
+version$E: $B\win\version.c
410
+ $(BCC) $**
411
+
412
+$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
413
+ $(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
414
+
415
+$(OBJDIR)\th$O : $(SRCDIR)\th.c
416
+ $(TCC) /Fo$@ -c $**
417
+
418
+$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
419
+ $(TCC) /Fo$@ -c $**
420
+
421
+VERSION.h : version$E $B\manifest.uuid $B\manifest
422
+ $** > $@
423
+
424
+page_index.h: mkindex$E $(SRC)
425
+ $** > $@
426
+
427
+clean:
428
+ -del $(OBJDIR)\*.obj
429
+ -del *.obj *_.c *.h *.map
430
+ -del headers linkopts
431
+
432
+realclean:
433
+ -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
434
+
435
+}
436
+foreach s [lsort $src] {
437
+ puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
438
+ puts "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
439
+ puts "${s}_.c : \$(SRCDIR)\\$s.c"
440
+ puts "\ttranslate\$E \$** > \$@\n"
441
+}
442
+
443
+puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
444
+foreach s [lsort $src] {
445
+ puts -nonewline "${s}_.c:$s.h "
446
+}
447
+puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
448
+puts "\t@copy /Y nul: headers"
449
+
450
+}
209451
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -44,10 +44,11 @@
44 md5
45 merge
46 merge3
47 name
48 pivot
 
49 pqueue
50 printf
51 rebuild
52 report
53 rss
@@ -80,11 +81,11 @@
80 }
81
82 # Name of the final application
83 #
84 set name fossil
85
86 puts {# DO NOT EDIT
87 #
88 # This file is automatically generated. Instead of editing this
89 # file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
90 # to regenerate this file.
@@ -204,5 +205,246 @@
204 puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
205 puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
206
207 puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
208 puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -44,10 +44,11 @@
44 md5
45 merge
46 merge3
47 name
48 pivot
49 popen
50 pqueue
51 printf
52 rebuild
53 report
54 rss
@@ -80,11 +81,11 @@
81 }
82
83 # Name of the final application
84 #
85 set name fossil
86 if { 0 == $argc } {
87 puts {# DO NOT EDIT
88 #
89 # This file is automatically generated. Instead of editing this
90 # file, edit "makemake.tcl" then run "tclsh makemake.tcl >main.mk"
91 # to regenerate this file.
@@ -204,5 +205,246 @@
205 puts "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
206 puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
207
208 puts "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
209 puts "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
210 exit
211 }
212 if { "dmc" == [lindex $argv 0] } {
213
214 puts {# DO NOT EDIT
215 #
216 # This file is automatically generated. Instead of editing this
217 # file, edit "makemake.tcl" then run
218 # "tclsh src/makemake.tcl dmc > win/Makefile.dmc"
219 # to regenerate this file.
220 B = ..
221 SRCDIR = $B\src
222 OBJDIR = .
223 O = .obj
224 E = .exe
225
226
227 # Maybe DMDIR, SSL or INCL needs adjustment
228 DMDIR = c:\DM
229 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(DMDIR)\extra\include
230
231 #SSL = -DFOSSIL_ENABLE_SSL=1
232 SSL =
233
234 DMCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
235 I18N = -DFOSSIL_I18N=0
236
237 CFLAGS = -o
238 BCC = $(DMDIR)\bin\dmc $(CFLAGS)
239 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL)
240 LIBS = $(DMDIR)\extra\lib\ zlib wsock32
241 }
242 puts -nonewline "SRC = "
243 foreach s [lsort $src] {
244 puts -nonewline "${s}_.c "
245 }
246 puts "\n"
247 puts -nonewline "OBJ = "
248 foreach s [lsort $src] {
249 puts -nonewline "\$(OBJDIR)\\$s\$O "
250 }
251 puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
252 puts {
253
254 APPNAME = $(OBJDIR)\fossil$(E)
255
256 all: $(APPNAME)
257
258 $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\link
259 cd $(OBJDIR)
260 $(DMDIR)\bin\link @link
261
262 $(OBJDIR)\link: $B\win\Makefile.dmc}
263 puts -nonewline "\t+echo "
264 foreach s [lsort $src] {
265 puts -nonewline "$s "
266 }
267 puts "sqlite3 th th_lang > \$@"
268 puts "\t+echo fossil >> \$@"
269 puts "\t+echo fossil >> \$@"
270 puts "\t+echo \$(LIBS) >> \$@\n\n"
271
272 puts {
273 translate$E: $(SRCDIR)\translate.c
274 $(BCC) -o$@ $**
275
276 makeheaders$E: $(SRCDIR)\makeheaders.c
277 $(BCC) -o$@ $**
278
279 mkindex$E: $(SRCDIR)\mkindex.c
280 $(BCC) -o$@ $**
281
282 version$E: $B\win\version.c
283 $(BCC) -o$@ $**
284
285 $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
286 $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
287
288 $(OBJDIR)\th$O : $(SRCDIR)\th.c
289 $(TCC) -o$@ -c $**
290
291 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
292 $(TCC) -o$@ -c $**
293
294 VERSION.h : version$E $B\manifest.uuid $B\manifest
295 +$** > $@
296
297 page_index.h: mkindex$E $(SRC)
298 +$** > $@
299
300 clean:
301 -del $(OBJDIR)\*.obj
302 -del *.obj *_.c *.h *.map
303
304 realclean:
305 -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
306
307 }
308 foreach s [lsort $src] {
309 puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
310 puts "\t\$(TCC) -o\$@ -c ${s}_.c\n"
311 puts "${s}_.c : \$(SRCDIR)\\$s.c"
312 puts "\t+translate\$E \$** > \$@\n"
313 }
314
315 puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
316 foreach s [lsort $src] {
317 puts -nonewline "${s}_.c:$s.h "
318 }
319 puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
320 puts "\t@copy /Y nul: headers"
321 exit
322 }
323
324 if { "msc" == [lindex $argv 0] } {
325
326 puts {# DO NOT EDIT
327 #
328 # This file is automatically generated. Instead of editing this
329 # file, edit "makemake.tcl" then run
330 # "tclsh src/makemake.tcl msc > win/Makefile.msc"
331 # to regenerate this file.
332 B = ..
333 SRCDIR = $B\src
334 OBJDIR = .
335 O = .obj
336 E = .exe
337
338 # Maybe MSCDIR, SSL, ZLIB, or INCL needs adjustment
339 MSCDIR = c:\msc
340
341 # Uncomment below for SSL support
342 SSL =
343 SSLLIB =
344 #SSL = -DFOSSIL_ENABLE_SSL=1
345 #SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib advapi32.lib
346
347 # zlib options
348 # When using precompiled from http://zlib.net/zlib125-dll.zip
349 #ZINCDIR = C:\zlib125-dll\include
350 #ZLIBDIR = C:\zlib125-dll\lib
351 #ZLIB = zdll.lib
352 ZINCDIR = $(MSCDIR)\extra\include
353 ZLIBDIR = $(MSCDIR)\extra\lib
354 ZLIB = zlib.lib
355
356 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
357
358 MSCDEF = -Dstrncasecmp=memicmp -Dstrcasecmp=stricmp
359 I18N = -DFOSSIL_I18N=0
360
361 CFLAGS = -nologo -MT -O2
362 BCC = $(CC) $(CFLAGS)
363 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(I18N) $(SSL) $(INCL)
364 LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
365 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
366 }
367 puts -nonewline "SRC = "
368 foreach s [lsort $src] {
369 puts -nonewline "${s}_.c "
370 }
371 puts "\n"
372 puts -nonewline "OBJ = "
373 foreach s [lsort $src] {
374 puts -nonewline "\$(OBJDIR)\\$s\$O "
375 }
376 puts "\$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O "
377 puts {
378
379 APPNAME = $(OBJDIR)\fossil$(E)
380
381 all: $(OBJDIR) $(APPNAME)
382
383 $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OBJDIR)\linkopts
384 cd $(OBJDIR)
385 link -LINK -OUT:$@ $(LIBDIR) @linkopts
386
387 $(OBJDIR)\linkopts: $B\win\Makefile.msc}
388 puts -nonewline "\techo "
389 foreach s [lsort $src] {
390 puts -nonewline "$s "
391 }
392 puts "sqlite3 th th_lang > \$@"
393 puts "\techo \$(LIBS) >> \$@\n\n"
394
395 puts {
396
397 $(OBJDIR):
398 @-mkdir $@
399
400 translate$E: $(SRCDIR)\translate.c
401 $(BCC) $**
402
403 makeheaders$E: $(SRCDIR)\makeheaders.c
404 $(BCC) $**
405
406 mkindex$E: $(SRCDIR)\mkindex.c
407 $(BCC) $**
408
409 version$E: $B\win\version.c
410 $(BCC) $**
411
412 $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
413 $(TCC) /Fo$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $**
414
415 $(OBJDIR)\th$O : $(SRCDIR)\th.c
416 $(TCC) /Fo$@ -c $**
417
418 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
419 $(TCC) /Fo$@ -c $**
420
421 VERSION.h : version$E $B\manifest.uuid $B\manifest
422 $** > $@
423
424 page_index.h: mkindex$E $(SRC)
425 $** > $@
426
427 clean:
428 -del $(OBJDIR)\*.obj
429 -del *.obj *_.c *.h *.map
430 -del headers linkopts
431
432 realclean:
433 -del $(APPNAME) translate$E mkindex$E makeheaders$E version$E
434
435 }
436 foreach s [lsort $src] {
437 puts "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
438 puts "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
439 puts "${s}_.c : \$(SRCDIR)\\$s.c"
440 puts "\ttranslate\$E \$** > \$@\n"
441 }
442
443 puts -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
444 foreach s [lsort $src] {
445 puts -nonewline "${s}_.c:$s.h "
446 }
447 puts "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
448 puts "\t@copy /Y nul: headers"
449
450 }
451
+2 -2
--- src/merge3.c
+++ src/merge3.c
@@ -165,12 +165,12 @@
165165
** is the number of lines of text to copy directly from the pivot,
166166
** the second integer is the number of lines of text to omit from the
167167
** pivot, and the third integer is the number of lines of text that are
168168
** inserted. The edit array ends with a triple of 0,0,0.
169169
*/
170
- aC1 = text_diff(pPivot, pV1, 0, 0);
171
- aC2 = text_diff(pPivot, pV2, 0, 0);
170
+ aC1 = text_diff(pPivot, pV1, 0, 0, 1);
171
+ aC2 = text_diff(pPivot, pV2, 0, 0, 1);
172172
if( aC1==0 || aC2==0 ){
173173
free(aC1);
174174
free(aC2);
175175
return -1;
176176
}
177177
178178
ADDED src/popen.c
--- src/merge3.c
+++ src/merge3.c
@@ -165,12 +165,12 @@
165 ** is the number of lines of text to copy directly from the pivot,
166 ** the second integer is the number of lines of text to omit from the
167 ** pivot, and the third integer is the number of lines of text that are
168 ** inserted. The edit array ends with a triple of 0,0,0.
169 */
170 aC1 = text_diff(pPivot, pV1, 0, 0);
171 aC2 = text_diff(pPivot, pV2, 0, 0);
172 if( aC1==0 || aC2==0 ){
173 free(aC1);
174 free(aC2);
175 return -1;
176 }
177
178 DDED src/popen.c
--- src/merge3.c
+++ src/merge3.c
@@ -165,12 +165,12 @@
165 ** is the number of lines of text to copy directly from the pivot,
166 ** the second integer is the number of lines of text to omit from the
167 ** pivot, and the third integer is the number of lines of text that are
168 ** inserted. The edit array ends with a triple of 0,0,0.
169 */
170 aC1 = text_diff(pPivot, pV1, 0, 0, 1);
171 aC2 = text_diff(pPivot, pV2, 0, 0, 1);
172 if( aC1==0 || aC2==0 ){
173 free(aC1);
174 free(aC2);
175 return -1;
176 }
177
178 DDED src/popen.c
+11
--- a/src/popen.c
+++ b/src/popen.c
@@ -0,0 +1,11 @@
1
+TCHAR *zCmd, mb(c)zCmd),
2
+wchar_t charTCHAR *zCmd, mb(c)zCmd),
3
+wchar_t TCHAR *zCmd, TCHAR *zCmd, mb©zCmd),
4
+wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
5
+mWunicodemd),
6
+wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
7
+mWunicodeCmd),
8
+wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
9
+mWunicodeTCHAR *zCmd, #elsembcs#endif mb(c)zCmd),
10
+dupclose(1);
11
+ (intptr_t)hStdoutRd(intptr_t)hStdinWrlonglong
--- a/src/popen.c
+++ b/src/popen.c
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
--- a/src/popen.c
+++ b/src/popen.c
@@ -0,0 +1,11 @@
1 TCHAR *zCmd, mb(c)zCmd),
2 wchar_t charTCHAR *zCmd, mb(c)zCmd),
3 wchar_t TCHAR *zCmd, TCHAR *zCmd, mb©zCmd),
4 wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
5 mWunicodemd),
6 wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
7 mWunicodeCmd),
8 wchar_t *zCmd,TCHAR *zCmd, mbcs(zCmd),
9 mWunicodeTCHAR *zCmd, #elsembcs#endif mb(c)zCmd),
10 dupclose(1);
11 (intptr_t)hStdoutRd(intptr_t)hStdinWrlonglong
--- src/rebuild.c
+++ src/rebuild.c
@@ -446,10 +446,12 @@
446446
blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
447447
if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
448448
fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
449449
}
450450
content_put(&aContent, 0, 0);
451
+ blob_reset(&path);
452
+ blob_reset(&aContent);
451453
}
452454
}
453455
else {
454456
fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
455457
}
456458
--- src/rebuild.c
+++ src/rebuild.c
@@ -446,10 +446,12 @@
446 blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
447 if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
448 fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
449 }
450 content_put(&aContent, 0, 0);
 
 
451 }
452 }
453 else {
454 fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
455 }
456
--- src/rebuild.c
+++ src/rebuild.c
@@ -446,10 +446,12 @@
446 blob_appendf(&path, "%s/%s", g.argv[3], pEntry->d_name);
447 if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){
448 fossil_panic("Some unknown error occurred while reading \"%s\"", blob_str(&path));
449 }
450 content_put(&aContent, 0, 0);
451 blob_reset(&path);
452 blob_reset(&aContent);
453 }
454 }
455 else {
456 fossil_panic("Encountered error %d while trying to open \"%s\".", errno, g.argv[3]);
457 }
458
+12 -12
--- src/report.c
+++ src/report.c
@@ -277,13 +277,13 @@
277277
zSQL = db_column_text(&q, 1);
278278
zOwner = db_column_text(&q, 2);
279279
zClrKey = db_column_text(&q, 3);
280280
@ <table cellpadding=0 cellspacing=0 border=0>
281281
@ <tr><td valign="top" align="right">Title:</td><td width=15></td>
282
- @ <td colspan=3>%h(zTitle)</td></tr>
282
+ @ <td colspan="3">%h(zTitle)</td></tr>
283283
@ <tr><td valign="top" align="right">Owner:</td><td></td>
284
- @ <td colspan=3>%h(zOwner)</td></tr>
284
+ @ <td colspan="3">%h(zOwner)</td></tr>
285285
@ <tr><td valign="top" align="right">SQL:</td><td></td>
286286
@ <td valign="top"><pre>
287287
@ %h(zSQL)
288288
@ </pre></td>
289289
@ <td width=15></td><td valign="top">
@@ -393,21 +393,21 @@
393393
}
394394
}
395395
if( zOwner==0 ) zOwner = g.zLogin;
396396
style_submenu_element("Cancel", "Cancel", "reportlist");
397397
if( rn>0 ){
398
- style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
398
+ style_submenu_element("Delete", "Delete", "rptedit?rn=%d&amp;del1=1", rn);
399399
}
400400
style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
401401
if( zErr ){
402402
@ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
403403
}
404404
@ <form action="rptedit" method="POST">
405405
@ <input type="hidden" name="rn" value="%d(rn)">
406
- @ <p>Report Title:<br>
406
+ @ <p>Report Title:<br />
407407
@ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
408
- @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
408
+ @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
409409
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
410410
@ </p>
411411
login_insert_csrf_secret();
412412
if( g.okAdmin ){
413413
@ <p>Report owner:
@@ -417,11 +417,11 @@
417417
@ <input type="hidden" name="w" value="%h(zOwner)">
418418
}
419419
@ <p>Enter an optional color key in the following box. (If blank, no
420420
@ color key is displayed.) Each line contains the text for a single
421421
@ entry in the key. The first token of each line is the background
422
- @ color for that line.<br>
422
+ @ color for that line.<br />
423423
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
424424
@ </p>
425425
if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
426426
@ <p>This report format is owned by %h(zOwner). You are not allowed
427427
@ to change it.</p>
@@ -448,11 +448,11 @@
448448
zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
449449
if( zSchema==0 ){
450450
zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
451451
" WHERE name='ticket'");
452452
}
453
- @ <hr><h3>TICKET Schema</h3>
453
+ @ <hr /><h3>TICKET Schema</h3>
454454
@ <blockquote><pre>
455455
@ %h(zSchema)
456456
@ </pre></blockquote>
457457
@ <h3>Notes</h3>
458458
@ <ul>
@@ -829,14 +829,14 @@
829829
while( isspace(*zSafeKey) ) zSafeKey++;
830830
for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
831831
for(j=i; isspace(zSafeKey[j]); j++){}
832832
for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
833833
if( !horiz ){
834
- cgi_printf("<tr bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
834
+ cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n",
835835
i, zSafeKey, k-j, &zSafeKey[j]);
836836
}else{
837
- cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
837
+ cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n",
838838
i, zSafeKey, k-j, &zSafeKey[j]);
839839
}
840840
zSafeKey += k;
841841
}
842842
free(zToFree);
@@ -907,11 +907,11 @@
907907
if( !tabs ){
908908
struct GenerateHTML sState;
909909
910910
db_multi_exec("PRAGMA empty_result_callbacks=ON");
911911
style_submenu_element("Raw", "Raw",
912
- "rptview?tablist=1&%s", PD("QUERY_STRING",""));
912
+ "rptview?tablist=1&amp;%s", PD("QUERY_STRING",""));
913913
if( g.okAdmin
914914
|| (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
915915
style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
916916
}
917917
if( g.okTktFmt ){
@@ -921,12 +921,12 @@
921921
style_submenu_element("New Ticket", "Create a new ticket",
922922
"%s/tktnew", g.zTop);
923923
}
924924
style_header(zTitle);
925925
output_color_key(zClrKey, 1,
926
- "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
927
- @ <table border=1 cellpadding=2 cellspacing=0 class="report">
926
+ "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
927
+ @ <table border="1" cellpadding="2" cellspacing="0" class="report">
928928
sState.rn = rn;
929929
sState.nCount = 0;
930930
sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
931931
sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
932932
sqlite3_set_authorizer(g.db, 0, 0);
933933
--- src/report.c
+++ src/report.c
@@ -277,13 +277,13 @@
277 zSQL = db_column_text(&q, 1);
278 zOwner = db_column_text(&q, 2);
279 zClrKey = db_column_text(&q, 3);
280 @ <table cellpadding=0 cellspacing=0 border=0>
281 @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
282 @ <td colspan=3>%h(zTitle)</td></tr>
283 @ <tr><td valign="top" align="right">Owner:</td><td></td>
284 @ <td colspan=3>%h(zOwner)</td></tr>
285 @ <tr><td valign="top" align="right">SQL:</td><td></td>
286 @ <td valign="top"><pre>
287 @ %h(zSQL)
288 @ </pre></td>
289 @ <td width=15></td><td valign="top">
@@ -393,21 +393,21 @@
393 }
394 }
395 if( zOwner==0 ) zOwner = g.zLogin;
396 style_submenu_element("Cancel", "Cancel", "reportlist");
397 if( rn>0 ){
398 style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
399 }
400 style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
401 if( zErr ){
402 @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
403 }
404 @ <form action="rptedit" method="POST">
405 @ <input type="hidden" name="rn" value="%d(rn)">
406 @ <p>Report Title:<br>
407 @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
408 @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
409 @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
410 @ </p>
411 login_insert_csrf_secret();
412 if( g.okAdmin ){
413 @ <p>Report owner:
@@ -417,11 +417,11 @@
417 @ <input type="hidden" name="w" value="%h(zOwner)">
418 }
419 @ <p>Enter an optional color key in the following box. (If blank, no
420 @ color key is displayed.) Each line contains the text for a single
421 @ entry in the key. The first token of each line is the background
422 @ color for that line.<br>
423 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
424 @ </p>
425 if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
426 @ <p>This report format is owned by %h(zOwner). You are not allowed
427 @ to change it.</p>
@@ -448,11 +448,11 @@
448 zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
449 if( zSchema==0 ){
450 zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
451 " WHERE name='ticket'");
452 }
453 @ <hr><h3>TICKET Schema</h3>
454 @ <blockquote><pre>
455 @ %h(zSchema)
456 @ </pre></blockquote>
457 @ <h3>Notes</h3>
458 @ <ul>
@@ -829,14 +829,14 @@
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 bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
835 i, zSafeKey, k-j, &zSafeKey[j]);
836 }else{
837 cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
838 i, zSafeKey, k-j, &zSafeKey[j]);
839 }
840 zSafeKey += k;
841 }
842 free(zToFree);
@@ -907,11 +907,11 @@
907 if( !tabs ){
908 struct GenerateHTML sState;
909
910 db_multi_exec("PRAGMA empty_result_callbacks=ON");
911 style_submenu_element("Raw", "Raw",
912 "rptview?tablist=1&%s", PD("QUERY_STRING",""));
913 if( g.okAdmin
914 || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
915 style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
916 }
917 if( g.okTktFmt ){
@@ -921,12 +921,12 @@
921 style_submenu_element("New Ticket", "Create a new ticket",
922 "%s/tktnew", g.zTop);
923 }
924 style_header(zTitle);
925 output_color_key(zClrKey, 1,
926 "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
927 @ <table border=1 cellpadding=2 cellspacing=0 class="report">
928 sState.rn = rn;
929 sState.nCount = 0;
930 sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
931 sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
932 sqlite3_set_authorizer(g.db, 0, 0);
933
--- src/report.c
+++ src/report.c
@@ -277,13 +277,13 @@
277 zSQL = db_column_text(&q, 1);
278 zOwner = db_column_text(&q, 2);
279 zClrKey = db_column_text(&q, 3);
280 @ <table cellpadding=0 cellspacing=0 border=0>
281 @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
282 @ <td colspan="3">%h(zTitle)</td></tr>
283 @ <tr><td valign="top" align="right">Owner:</td><td></td>
284 @ <td colspan="3">%h(zOwner)</td></tr>
285 @ <tr><td valign="top" align="right">SQL:</td><td></td>
286 @ <td valign="top"><pre>
287 @ %h(zSQL)
288 @ </pre></td>
289 @ <td width=15></td><td valign="top">
@@ -393,21 +393,21 @@
393 }
394 }
395 if( zOwner==0 ) zOwner = g.zLogin;
396 style_submenu_element("Cancel", "Cancel", "reportlist");
397 if( rn>0 ){
398 style_submenu_element("Delete", "Delete", "rptedit?rn=%d&amp;del1=1", rn);
399 }
400 style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
401 if( zErr ){
402 @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
403 }
404 @ <form action="rptedit" method="POST">
405 @ <input type="hidden" name="rn" value="%d(rn)">
406 @ <p>Report Title:<br />
407 @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
408 @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
409 @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
410 @ </p>
411 login_insert_csrf_secret();
412 if( g.okAdmin ){
413 @ <p>Report owner:
@@ -417,11 +417,11 @@
417 @ <input type="hidden" name="w" value="%h(zOwner)">
418 }
419 @ <p>Enter an optional color key in the following box. (If blank, no
420 @ color key is displayed.) Each line contains the text for a single
421 @ entry in the key. The first token of each line is the background
422 @ color for that line.<br />
423 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
424 @ </p>
425 if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
426 @ <p>This report format is owned by %h(zOwner). You are not allowed
427 @ to change it.</p>
@@ -448,11 +448,11 @@
448 zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
449 if( zSchema==0 ){
450 zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
451 " WHERE name='ticket'");
452 }
453 @ <hr /><h3>TICKET Schema</h3>
454 @ <blockquote><pre>
455 @ %h(zSchema)
456 @ </pre></blockquote>
457 @ <h3>Notes</h3>
458 @ <ul>
@@ -829,14 +829,14 @@
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{
837 cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n",
838 i, zSafeKey, k-j, &zSafeKey[j]);
839 }
840 zSafeKey += k;
841 }
842 free(zToFree);
@@ -907,11 +907,11 @@
907 if( !tabs ){
908 struct GenerateHTML sState;
909
910 db_multi_exec("PRAGMA empty_result_callbacks=ON");
911 style_submenu_element("Raw", "Raw",
912 "rptview?tablist=1&amp;%s", PD("QUERY_STRING",""));
913 if( g.okAdmin
914 || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
915 style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
916 }
917 if( g.okTktFmt ){
@@ -921,12 +921,12 @@
921 style_submenu_element("New Ticket", "Create a new ticket",
922 "%s/tktnew", g.zTop);
923 }
924 style_header(zTitle);
925 output_color_key(zClrKey, 1,
926 "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
927 @ <table border="1" cellpadding="2" cellspacing="0" class="report">
928 sState.rn = rn;
929 sState.nCount = 0;
930 sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
931 sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
932 sqlite3_set_authorizer(g.db, 0, 0);
933
+1 -1
--- src/rss.c
+++ src/rss.c
@@ -16,13 +16,13 @@
1616
*******************************************************************************
1717
**
1818
** This file contains code used to create a RSS feed for the CGI interface.
1919
*/
2020
#include "config.h"
21
+#include <time.h>
2122
#include "rss.h"
2223
#include <assert.h>
23
-#include <time.h>
2424
2525
/*
2626
** WEBPAGE: timeline.rss
2727
*/
2828
void page_timeline_rss(void){
2929
--- src/rss.c
+++ src/rss.c
@@ -16,13 +16,13 @@
16 *******************************************************************************
17 **
18 ** This file contains code used to create a RSS feed for the CGI interface.
19 */
20 #include "config.h"
 
21 #include "rss.h"
22 #include <assert.h>
23 #include <time.h>
24
25 /*
26 ** WEBPAGE: timeline.rss
27 */
28 void page_timeline_rss(void){
29
--- src/rss.c
+++ src/rss.c
@@ -16,13 +16,13 @@
16 *******************************************************************************
17 **
18 ** This file contains code used to create a RSS feed for the CGI interface.
19 */
20 #include "config.h"
21 #include <time.h>
22 #include "rss.h"
23 #include <assert.h>
 
24
25 /*
26 ** WEBPAGE: timeline.rss
27 */
28 void page_timeline_rss(void){
29
+309 -275
--- src/setup.c
+++ src/setup.c
@@ -99,42 +99,40 @@
9999
return;
100100
}
101101
102102
style_submenu_element("Add", "Add User", "setup_uedit");
103103
style_header("User List");
104
- @ <table border="0" cellpadding="0" cellspacing="25">
105
- @ <tr><td valign="top">
106
- @ <b>Users:</b>
107
- @ <table border="1" cellpadding="10"><tr><td>
108
- @ <table cellspacing=0 cellpadding=0 border=0>
104
+ @ <table class="usetupLayoutTable">
105
+ @ <tr><td class="usetupColumnLayout">
106
+ @ <span class="note">Users:</span>
107
+ @ <table class="usetupUserList">
109108
@ <tr>
110
- @ <th align="right">User&nbsp;ID</th><td width="20">&nbsp;</td>
111
- @ <th>Capabilities</th><td width="15">&nbsp;</td>
112
- @ <th>Contact&nbsp;Info</th>
109
+ @ <th class="usetupListUser" style="text-align: right;padding-right: 20px;">User&nbsp;ID</th>
110
+ @ <th class="usetupListCap" style="text-align: center;padding-right: 15px;">Capabilities</th>
111
+ @ <th class="usetupListCon" style="text-align: left;">Contact&nbsp;Info</th>
113112
@ </tr>
114113
db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
115114
while( db_step(&s)==SQLITE_ROW ){
116115
const char *zCap = db_column_text(&s, 2);
117116
if( strstr(zCap, "s") ) zCap = "s";
118117
@ <tr>
119
- @ <td align="right">
118
+ @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
120119
if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
121120
@ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
122121
}
123
- @ <nobr>%h(db_column_text(&s,1))</nobr>
122
+ @ %h(db_column_text(&s,1))
124123
if( g.okAdmin ){
125124
@ </a>
126125
}
127
- @ </td><td>&nbsp;&nbsp;&nbsp;</td>
128
- @ <td align="center">%s(zCap)</td>
129
- @ <td>&nbsp;&nbsp;&nbsp;</td>
130
- @ <td align="left">%s(db_column_text(&s,3))</td>
126
+ @ </td>
127
+ @ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td>
128
+ @ <td class="usetupListCon" style="text-align: left;">%s(db_column_text(&s,3))</td>
131129
@ </tr>
132130
}
133
- @ </table></td></tr></table>
134
- @ <td valign="top">
135
- @ <b>Notes:</b>
131
+ @ </table>
132
+ @ </td><td class="usetupColumnLayout">
133
+ @ <span class="note">Notes:</span>
136134
@ <ol>
137135
@ <li><p>The permission flags are as follows:</p>
138136
@ <table>
139137
@ <tr><td valign="top"><b>a</b></td>
140138
@ <td><i>Admin:</i> Create and delete users</td></tr>
@@ -181,31 +179,35 @@
181179
@ user <tt>developer</tt></td></tr>
182180
@ <tr><td valign="top"><b>w</b></td>
183181
@ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
184182
@ <tr><td valign="top"><b>z</b></td>
185183
@ <td><i>Zip download:</i> Download a baseline via the
186
- @ <tt>/zip</tt> URL even without check<b>o</b>ut
187
- @ and <b>h</b>istory permissions</td></tr>
184
+ @ <tt>/zip</tt> URL even without
185
+ @ check<span class="capability">o</span>ut
186
+ @ and <span class="capability">h</span>istory permissions</td></tr>
188187
@ </table>
189188
@ </li>
190189
@
191190
@ <li><p>
192
- @ Every user, logged in or not, inherits the privileges of <b>nobody</b>.
191
+ @ Every user, logged in or not, inherits the privileges of
192
+ @ <span class="usertype">nobody</span>.
193193
@ </p></li>
194194
@
195195
@ <li><p>
196
- @ Any human can login as <b>anonymous</b> since the password is
197
- @ clearly displayed on the login page for them to type. The purpose
198
- @ of requiring anonymous to log in is to prevent access by spiders.
196
+ @ Any human can login as <span class="usertype">anonymous</span> since the
197
+ @ password is clearly displayed on the login page for them to type. The
198
+ @ purpose of requiring anonymous to log in is to prevent access by spiders.
199199
@ Every logged-in user inherits the combined privileges of
200
- @ <b>anonymous</b> and
201
- @ <b>nobody</b>.
200
+ @ <span class="usertype">anonymous</span> and
201
+ @ <span class="usertype">nobody</span>.
202202
@ </p></li>
203203
@
204204
@ <li><p>
205
- @ Users with privilege <b>v</b> inherit the combined privileges of
206
- @ <b>developer</b>, <b>anonymous</b>, and <b>nobody</b>.
205
+ @ Users with privilege <span class="capability">v</span> inherit the combined
206
+ @ privileges of <span class="usertype">developer</span>,
207
+ @ <span class="usertype">anonymous</span>, and
208
+ @ <span class="usertype">nobody</span>.
207209
@ </p></li>
208210
@
209211
@ </ol>
210212
@ </td></tr></table>
211213
style_footer();
@@ -323,12 +325,12 @@
323325
}
324326
if( uid>0 &&
325327
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
326328
){
327329
style_header("User Creation Error");
328
- @ <font color="red">Login "%h(zLogin)" is already used by a different
329
- @ user.</font>
330
+ @ <span class="loginError">Login "%h(zLogin)" is already used by
331
+ @ a different user.</span>
330332
@
331333
@ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
332334
style_footer();
333335
return;
334336
}
@@ -353,65 +355,69 @@
353355
if( uid ){
354356
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
355357
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
356358
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
357359
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
358
- if( strchr(zCap, 'a') ) oaa = " checked";
359
- if( strchr(zCap, 'b') ) oab = " checked";
360
- if( strchr(zCap, 'c') ) oac = " checked";
361
- if( strchr(zCap, 'd') ) oad = " checked";
362
- if( strchr(zCap, 'e') ) oae = " checked";
363
- if( strchr(zCap, 'f') ) oaf = " checked";
364
- if( strchr(zCap, 'g') ) oag = " checked";
365
- if( strchr(zCap, 'h') ) oah = " checked";
366
- if( strchr(zCap, 'i') ) oai = " checked";
367
- if( strchr(zCap, 'j') ) oaj = " checked";
368
- if( strchr(zCap, 'k') ) oak = " checked";
369
- if( strchr(zCap, 'm') ) oam = " checked";
370
- if( strchr(zCap, 'n') ) oan = " checked";
371
- if( strchr(zCap, 'o') ) oao = " checked";
372
- if( strchr(zCap, 'p') ) oap = " checked";
373
- if( strchr(zCap, 'r') ) oar = " checked";
374
- if( strchr(zCap, 's') ) oas = " checked";
375
- if( strchr(zCap, 't') ) oat = " checked";
376
- if( strchr(zCap, 'u') ) oau = " checked";
377
- if( strchr(zCap, 'v') ) oav = " checked";
378
- if( strchr(zCap, 'w') ) oaw = " checked";
379
- if( strchr(zCap, 'z') ) oaz = " checked";
360
+ if( strchr(zCap, 'a') ) oaa = " checked=\"checked\"";
361
+ if( strchr(zCap, 'b') ) oab = " checked=\"checked\"";
362
+ if( strchr(zCap, 'c') ) oac = " checked=\"checked\"";
363
+ if( strchr(zCap, 'd') ) oad = " checked=\"checked\"";
364
+ if( strchr(zCap, 'e') ) oae = " checked=\"checked\"";
365
+ if( strchr(zCap, 'f') ) oaf = " checked=\"checked\"";
366
+ if( strchr(zCap, 'g') ) oag = " checked=\"checked\"";
367
+ if( strchr(zCap, 'h') ) oah = " checked=\"checked\"";
368
+ if( strchr(zCap, 'i') ) oai = " checked=\"checked\"";
369
+ if( strchr(zCap, 'j') ) oaj = " checked=\"checked\"";
370
+ if( strchr(zCap, 'k') ) oak = " checked=\"checked\"";
371
+ if( strchr(zCap, 'm') ) oam = " checked=\"checked\"";
372
+ if( strchr(zCap, 'n') ) oan = " checked=\"checked\"";
373
+ if( strchr(zCap, 'o') ) oao = " checked=\"checked\"";
374
+ if( strchr(zCap, 'p') ) oap = " checked=\"checked\"";
375
+ if( strchr(zCap, 'r') ) oar = " checked=\"checked\"";
376
+ if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
377
+ if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
378
+ if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
379
+ if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
380
+ if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
381
+ if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
380382
}
381383
382384
/* figure out inherited permissions */
383385
memset(inherit, 0, sizeof(inherit));
384386
if( strcmp(zLogin, "developer") ){
385387
char *z1, *z2;
386388
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
387389
while( z1 && *z1 ){
388
- inherit[0x7f & *(z1++)] = "<font color=\"red\">&bull;</font>";
390
+ inherit[0x7f & *(z1++)] =
391
+ "<span class=\"ueditInheritDeveloper\">&bull;</span>";
389392
}
390393
free(z2);
391394
}
392395
if( strcmp(zLogin, "reader") ){
393396
char *z1, *z2;
394397
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
395398
while( z1 && *z1 ){
396
- inherit[0x7f & *(z1++)] = "<font color=\"black\">&bull;</font>";
399
+ inherit[0x7f & *(z1++)] =
400
+ "<span class=\"ueditInheritReader\">&bull;</span>";
397401
}
398402
free(z2);
399403
}
400404
if( strcmp(zLogin, "anonymous") ){
401405
char *z1, *z2;
402406
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
403407
while( z1 && *z1 ){
404
- inherit[0x7f & *(z1++)] = "<font color=\"blue\">&bull;</font>";
408
+ inherit[0x7f & *(z1++)] =
409
+ "<span class=\"ueditInheritAnonymous\">&bull;</span>";
405410
}
406411
free(z2);
407412
}
408413
if( strcmp(zLogin, "nobody") ){
409414
char *z1, *z2;
410415
z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
411416
while( z1 && *z1 ){
412
- inherit[0x7f & *(z1++)] = "<font color=\"green\">&bull;</font>";
417
+ inherit[0x7f & *(z1++)] =
418
+ "<span class=\"ueditInheritNobody\">&bull;</span>";
413419
}
414420
free(z2);
415421
}
416422
417423
/* Begin generating the page
@@ -420,77 +426,79 @@
420426
if( uid ){
421427
style_header(mprintf("Edit User %h", zLogin));
422428
}else{
423429
style_header("Add A New User");
424430
}
425
- @ <table align="left" hspace="20" vspace="10"><tr><td>
426
- @ <form action="%s(g.zPath)" method="POST">
431
+ @ <div class="ueditCapBox">
432
+ @ <form action="%s(g.zPath)" method="post"><div>
427433
login_insert_csrf_secret();
428434
@ <table>
429435
@ <tr>
430
- @ <td align="right"><nobr>User ID:</nobr></td>
431
- if( uid ){
432
- @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
433
- }else{
434
- @ <td>(new user)<input type="hidden" name="id" value=0></td>
435
- }
436
- @ </tr>
437
- @ <tr>
438
- @ <td align="right"><nobr>Login:</nobr></td>
439
- @ <td><input type="text" name="login" value="%h(zLogin)"></td>
440
- @ </tr>
441
- @ <tr>
442
- @ <td align="right"><nobr>Contact&nbsp;Info:</nobr></td>
443
- @ <td><input type="text" name="info" size=40 value="%h(zInfo)"></td>
444
- @ </tr>
445
- @ <tr>
446
- @ <td align="right" valign="top">Capabilities:</td>
436
+ @ <td class="usetupEditLabel">User ID:</td>
437
+ if( uid ){
438
+ @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
439
+ }else{
440
+ @ <td>(new user)<input type="hidden" name="id" value="0" /></td>
441
+ }
442
+ @ </tr>
443
+ @ <tr>
444
+ @ <td class="usetupEditLabel">Login:</td>
445
+ @ <td><input type="text" name="login" value="%h(zLogin)" /></td>
446
+ @ </tr>
447
+ @ <tr>
448
+ @ <td class="usetupEditLabel">Contact&nbsp;Info:</td>
449
+ @ <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
450
+ @ </tr>
451
+ @ <tr>
452
+ @ <td class="usetupEditLabel">Capabilities:</td>
447453
@ <td>
448454
#define B(x) inherit[x]
449455
if( g.okSetup ){
450
- @ <input type="checkbox" name="as"%s(oas)/>%s(B('s'))Setup<br>
451
- }
452
- @ <input type="checkbox" name="aa"%s(oaa)/>%s(B('a'))Admin<br>
453
- @ <input type="checkbox" name="ad"%s(oad)/>%s(B('d'))Delete<br>
454
- @ <input type="checkbox" name="ae"%s(oae)/>%s(B('e'))Email<br>
455
- @ <input type="checkbox" name="ap"%s(oap)/>%s(B('p'))Password<br>
456
- @ <input type="checkbox" name="ai"%s(oai)/>%s(B('i'))Check-In<br>
457
- @ <input type="checkbox" name="ao"%s(oao)/>%s(B('o'))Check-Out<br>
458
- @ <input type="checkbox" name="ah"%s(oah)/>%s(B('h'))History<br>
459
- @ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
460
- @ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
461
- @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
462
- @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
463
- @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
464
- @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
465
- @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
466
- @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
467
- @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
468
- @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
469
- @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
470
- @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
471
- @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
472
- @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
456
+ @ <input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup<br />
457
+ }
458
+ @ <input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin<br />
459
+ @ <input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete<br />
460
+ @ <input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email<br />
461
+ @ <input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password<br />
462
+ @ <input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In<br />
463
+ @ <input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out<br />
464
+ @ <input type="checkbox" name="ah"%s(oah) />%s(B('h'))History<br />
465
+ @ <input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader<br />
466
+ @ <input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer<br />
467
+ @ <input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone<br />
468
+ @ <input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki<br />
469
+ @ <input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki<br />
470
+ @ <input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki<br />
471
+ @ <input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki<br />
472
+ @ <input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments<br />
473
+ @ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
474
+ @ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
475
+ @ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
476
+ @ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
477
+ @ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
478
+ @ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
473479
@ </td>
474480
@ </tr>
475481
@ <tr>
476482
@ <td align="right">Password:</td>
477483
if( zPw[0] ){
478484
/* Obscure the password for all users */
479
- @ <td><input type="password" name="pw" value="**********"></td>
485
+ @ <td><input type="password" name="pw" value="**********" /></td>
480486
}else{
481487
/* Show an empty password as an empty input field */
482
- @ <td><input type="password" name="pw" value=""></td>
488
+ @ <td><input type="password" name="pw" value="" /></td>
483489
}
484490
@ </tr>
485491
if( !higherUser ){
486492
@ <tr>
487
- @ <td>&nbsp</td>
488
- @ <td><input type="submit" name="submit" value="Apply Changes">
493
+ @ <td>&nbsp;</td>
494
+ @ <td><input type="submit" name="submit" value="Apply Changes" /></td>
489495
@ </tr>
490496
}
491
- @ </table></td></tr></table>
497
+ @ </table>
498
+ @ </div></form>
499
+ @ </div>
492500
@ <h2>Privileges And Capabilities:</h2>
493501
@ <ul>
494502
if( higherUser ){
495503
@ <li><p><font color="blue"><b>
496504
@ User %h(zLogin) has Setup privileges and you only have Admin privileges
@@ -497,95 +505,114 @@
497505
@ so you are not permitted to make changes to %h(zLogin).
498506
@ </b></font></p></li>
499507
@
500508
}
501509
@ <li><p>
502
- @ The <b>Setup</b> user can make arbitrary configuration changes.
503
- @ An <b>Admin</b> user can add other users and change user privileges
510
+ @ The <span class="capability">Setup</span> user can make arbitrary
511
+ @ configuration changes. An <span class="usertype">Admin</span> user
512
+ @ can add other users and change user privileges
504513
@ and reset user passwords. Both automatically get all other privileges
505514
@ listed below. Use these two settings with discretion.
506515
@ </p></li>
507516
@
508517
@ <li><p>
509
- @ The "<font color="green"><big>&bull;</big></font>" mark indicates
510
- @ the privileges of "nobody" that are available to all users
511
- @ regardless of whether or not they are logged in.
512
- @ </p></li>
513
- @
514
- @ <li><p>
515
- @ The "<font color="blue"><big>&bull;</big></font>" mark indicates
516
- @ the privileges of "anonymous" that are inherited by all logged-in users.
517
- @ </p></li>
518
- @
519
- @ <li><p>
520
- @ The "<font color="red"><big>&bull;</big></font>" mark indicates
521
- @ the privileges of "developer" that are inherited by all users with
522
- @ the <b>Developer</b> privilege.
523
- @ </p></li>
524
- @
525
- @ <li><p>
526
- @ The "<font color="black"><big>&bull;</big></font>" mark indicates
527
- @ the privileges of "reader" that are inherited by all users with
528
- @ the <b>Reader</b> privilege.
529
- @ </p></li>
530
- @
531
- @ <li><p>
532
- @ The <b>Delete</b> privilege give the user the ability to erase
533
- @ wiki, tickets, and attachments that have been added by anonymous
534
- @ users. This capability is intended for deletion of spam. The
535
- @ delete capability is only in effect for 24 hours after the item
536
- @ is first posted. The Setup user can delete anything at any time.
537
- @ </p></li>
538
- @
539
- @ <li><p>
540
- @ The <b>History</b> privilege allows a user to see most hyperlinks.
541
- @ This is recommended ON for most logged-in users but OFF for
542
- @ user "nobody" to avoid problems with spiders trying to walk every
543
- @ historical version of every baseline and file.
544
- @ </p></li>
545
- @
546
- @ <li><p>
547
- @ The <b>Zip</b> privilege allows a user to see the "download as ZIP"
548
- @ hyperlink and permits access to the <tt>/zip</tt> page. This allows
549
- @ users to download ZIP archives without granting other rights like
550
- @ <b>Read</b> or <b>History</b>. This privilege is recommended for
551
- @ user <b>nobody</b> so that automatic package downloaders can obtain
552
- @ the sources without going through the login procedure.
553
- @ </p></li>
554
- @
555
- @ <li><p>
556
- @ The <b>Check-in</b> privilege allows remote users to "push".
557
- @ The <b>Check-out</b> privilege allows remote users to "pull".
558
- @ The <b>Clone</b> privilege allows remote users to "clone".
559
- @ </li><p>
560
- @
561
- @ <li><p>
562
- @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
563
- @ <b>Write Wiki</b> privileges control access to wiki pages. The
564
- @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
565
- @ <b>Write Ticket</b> privileges control access to trouble tickets.
566
- @ The <b>Ticket Report</b> privilege allows the user to create or edit
567
- @ ticket report formats.
568
- @ </p></li>
569
- @
570
- @ <li><p>
571
- @ Users with the <b>Password</b> privilege are allowed to change their
572
- @ own password. Recommended ON for most users but OFF for special
573
- @ users "developer", "anonymous", and "nobody".
574
- @ </p></li>
575
- @
576
- @ <li><p>
577
- @ The <b>EMail</b> privilege allows the display of sensitive information
578
- @ such as the email address of users and contact information on tickets.
579
- @ Recommended OFF for "anonymous" and for "nobody" but ON for
580
- @ "developer".
581
- @ </p></li>
582
- @
583
- @ <li><p>
584
- @ The <b>Attachment</b> privilege is needed in order to add attachments
585
- @ to tickets or wiki. Write privilege on the ticket or wiki is also
586
- @ required.</p></li>
518
+ @ The "<span class="ueditInheritNobody"><big>&bull;</big></span>" mark
519
+ @ indicates the privileges of <span class="usertype">nobody</span> that
520
+ @ are available to all users regardless of whether or not they are logged in.
521
+ @ </p></li>
522
+ @
523
+ @ <li><p>
524
+ @ The "<span class="ueditInheritAnonymous"><big>&bull;</big></span>" mark
525
+ @ indicates the privileges of <span class="usertype">anonymous</span> that
526
+ @ are inherited by all logged-in users.
527
+ @ </p></li>
528
+ @
529
+ @ <li><p>
530
+ @ The "<span class="ueditInheritDeveloper"><big>&bull;</big></span>" mark
531
+ @ indicates the privileges of <span class="usertype">developer</span> that
532
+ @ are inherited by all users with the
533
+ @ <span class="capability">Developer</span> privilege.
534
+ @ </p></li>
535
+ @
536
+ @ <li><p>
537
+ @ The "<span class="ueditInheritReader"><big>&bull;</big></span>" mark
538
+ @ indicates the privileges of <span class="usertype">reader</span> that
539
+ @ are inherited by all users with the <span class="capability">Reader</span>
540
+ @ privilege.
541
+ @ </p></li>
542
+ @
543
+ @ <li><p>
544
+ @ The <span class="capability">Delete</span> privilege give the user the
545
+ @ ability to erase wiki, tickets, and attachments that have been added
546
+ @ by anonymous users. This capability is intended for deletion of spam.
547
+ @ The delete capability is only in effect for 24 hours after the item
548
+ @ is first posted. The <span class="usertype">Setup</span> user can
549
+ @ delete anything at any time.
550
+ @ </p></li>
551
+ @
552
+ @ <li><p>
553
+ @ The <span class="capability">History</span> privilege allows a user
554
+ @ to see most hyperlinks. This is recommended ON for most logged-in users
555
+ @ but OFF for user "nobody" to avoid problems with spiders trying to walk
556
+ @ every historical version of every baseline and file.
557
+ @ </p></li>
558
+ @
559
+ @ <li><p>
560
+ @ The <span class="capability">Zip</span> privilege allows a user to
561
+ @ see the "download as ZIP"
562
+ @ hyperlink and permits access to the <tt>/zip</tt> page. This allows
563
+ @ users to download ZIP archives without granting other rights like
564
+ @ <span class="capability">Read</span> or
565
+ @ <span class="capability">History</span>. This privilege is recommended for
566
+ @ user <span class="usertype">nobody</span> so that automatic package
567
+ @ downloaders can obtain the sources without going through the login
568
+ @ procedure.
569
+ @ </p></li>
570
+ @
571
+ @ <li><p>
572
+ @ The <span class="capability">Check-in</span> privilege allows remote
573
+ @ users to "push". The <span class="capability">Check-out</span> privilege
574
+ @ allows remote users to "pull". The <span class="capability">Clone</span>
575
+ @ privilege allows remote users to "clone".
576
+ @ </p></li>
577
+ @
578
+ @ <li><p>
579
+ @ The <span class="capability">Read Wiki</span>,
580
+ @ <span class="capability">New Wiki</span>,
581
+ @ <span class="capability">Append Wiki</span>, and
582
+ @ <b>Write Wiki</b> privileges control access to wiki pages. The
583
+ @ <span class="capability">Read Ticket</span>,
584
+ @ <span class="capability">New Ticket</span>,
585
+ @ <span class="capability">Append Ticket</span>, and
586
+ @ <span class="capability">Write Ticket</span> privileges control access
587
+ @ to trouble tickets.
588
+ @ The <span class="capability">Ticket Report</span> privilege allows
589
+ @ the user to create or edit ticket report formats.
590
+ @ </p></li>
591
+ @
592
+ @ <li><p>
593
+ @ Users with the <span class="capability">Password</span> privilege
594
+ @ are allowed to change their own password. Recommended ON for most
595
+ @ users but OFF for special users <span class="usertype">developer</span>,
596
+ @ <span class="usertype">anonymous</span>,
597
+ @ and <span class="usertype">nobody</span>.
598
+ @ </p></li>
599
+ @
600
+ @ <li><p>
601
+ @ The <span class="capability">EMail</span> privilege allows the display of
602
+ @ sensitive information such as the email address of users and contact
603
+ @ information on tickets. Recommended OFF for
604
+ @ <span class="usertype">anonymousy</span> and for
605
+ @ <span class="usertype">nobody</span> but ON for
606
+ @ <span class="usertype">developer</span>.
607
+ @ </p></li>
608
+ @
609
+ @ <li><p>
610
+ @ The <span class="capability">Attachment</span> privilege is needed in
611
+ @ order to add attachments to tickets or wiki. Write privilege on the
612
+ @ ticket or wiki is also required.
613
+ @ </p></li>
587614
@
588615
@ <li><p>
589616
@ Login is prohibited if the password is an empty string.
590617
@ </p></li>
591618
@ </ul>
@@ -592,42 +619,46 @@
592619
@
593620
@ <h2>Special Logins</h2>
594621
@
595622
@ <ul>
596623
@ <li><p>
597
- @ No login is required for user "<b>nobody</b>". The capabilities
598
- @ of the <b>nobody</b> user are inherited by all users, regardless of
599
- @ whether or not they are logged in. To disable universal access
600
- @ to the repository, make sure no user named "<b>nobody</b>" exists or
601
- @ that the <b>nobody</b> user has no capabilities enabled.
602
- @ The password for <b>nobody</b> is ignore. To avoid problems with
603
- @ spiders overloading the server, it is recommended
604
- @ that the 'h' (History) capability be turned off for the <b>nobody</b>
605
- @ user.
606
- @ </p></li>
607
- @
608
- @ <li><p>
609
- @ Login is required for user "<b>anonymous</b>" but the password
610
- @ is displayed on the login screen beside the password entry box
611
- @ so anybody who can read should be able to login as anonymous.
612
- @ On the other hand, spiders and web-crawlers will typically not
613
- @ be able to login. Set the capabilities of the anonymous user
614
- @ to things that you want any human to be able to do, but not any
615
- @ spider. Every other logged-in user inherits the privileges of
616
- @ <b>anonymous</b>.
617
- @ </p></li>
618
- @
619
- @ <li><p>
620
- @ The "<b>developer</b>" user is intended as a template for trusted users
621
- @ with check-in privileges. When adding new trusted users, simply
622
- @ select the <b>Developer</b> privilege to cause the new user to inherit
623
- @ all privileges of the "developer" user. Similarly, the "<b>reader</b>"
624
- @ user is a template for users who are allowed more access than anonymous,
625
- @ but less than a developer.
626
- @ </p></li>
627
- @ </ul>
628
- @ </form>
624
+ @ No login is required for user <span class="usertype">nobody</span>. The
625
+ @ capabilities of the <span class="usertype">nobody</span> user are
626
+ @ inherited by all users, regardless of whether or not they are logged in.
627
+ @ To disable universal access to the repository, make sure no user named
628
+ @ <span class="usertype">nobody</span> exists or that the
629
+ @ <span class="usertype">nobody</span> user has no capabilities
630
+ @ enabled. The password for <span class="usertype">nobody</span> is ignore.
631
+ @ To avoid problems with spiders overloading the server, it is recommended
632
+ @ that the <span class="capability">h</span> (History) capability be turned
633
+ @ off for the <span class="usertype">nobody</span> user.
634
+ @ </p></li>
635
+ @
636
+ @ <li><p>
637
+ @ Login is required for user <span class="usertype">anonymous</span> but the
638
+ @ password is displayed on the login screen beside the password entry box
639
+ @ so anybody who can read should be able to login as anonymous.
640
+ @ On the other hand, spiders and web-crawlers will typically not
641
+ @ be able to login. Set the capabilities of the
642
+ @ <span class="usertype">anonymous</span>
643
+ @ user to things that you want any human to be able to do, but not any
644
+ @ spider. Every other logged-in user inherits the privileges of
645
+ @ <span class="usertype">anonymous</span>.
646
+ @ </p></li>
647
+ @
648
+ @ <li><p>
649
+ @ The <span class="usertype">developer</span> user is intended as a template
650
+ @ for trusted users with check-in privileges. When adding new trusted users,
651
+ @ simply select the <span class="capability">developer</span> privilege to
652
+ @ cause the new user to inherit all privileges of the
653
+ @ <span class="usertype">developer</span>
654
+ @ user. Similarly, the <span class="usertype">reader</span> user is a
655
+ @ template for users who are allowed more access than
656
+ @ <span class="usertype">anonymous</span>,
657
+ @ but less than a <span class="usertype">developer</span>.
658
+ @ </p></li>
659
+ @ </ul>
629660
style_footer();
630661
}
631662
632663
633664
/*
@@ -651,13 +682,14 @@
651682
db_set(zVar, iQ ? "1" : "0", 0);
652683
iVal = iQ;
653684
}
654685
}
655686
if( iVal ){
656
- @ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
687
+ @ <input type="checkbox" name="%s(zQParm)" checked="checked" />
688
+ @ <b>%s(zLabel)</b>
657689
}else{
658
- @ <input type="checkbox" name="%s(zQParm)"><b>%s(zLabel)</b></input>
690
+ @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b>
659691
}
660692
}
661693
662694
/*
663695
** Generate an entry box for an attribute.
@@ -674,11 +706,11 @@
674706
if( zQ && strcmp(zQ,zVal)!=0 ){
675707
login_verify_csrf_secret();
676708
db_set(zVar, zQ, 0);
677709
zVal = zQ;
678710
}
679
- @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
711
+ @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" />
680712
@ <b>%s(zLabel)</b>
681713
}
682714
683715
/*
684716
** Generate a text box for an attribute.
@@ -698,11 +730,12 @@
698730
db_set(zVar, zQ, 0);
699731
z = zQ;
700732
}
701733
if( rows>0 && cols>0 ){
702734
@ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
703
- @ <b>%s(zLabel)</b>
735
+ if (zLabel && *zLabel)
736
+ @ <span class="textareaLabel">%s(zLabel)</span>
704737
}
705738
}
706739
707740
708741
/*
@@ -714,57 +747,57 @@
714747
login_needed();
715748
}
716749
717750
style_header("Access Control Settings");
718751
db_begin_transaction();
719
- @ <form action="%s(g.zBaseURL)/setup_access" method="POST">
752
+ @ <form action="%s(g.zBaseURL)/setup_access" method="post"><div>
720753
login_insert_csrf_secret();
721
- @ <hr>
754
+ @ <hr />
722755
onoff_attribute("Require password for local access",
723756
"localauth", "localauth", 0);
724757
@ <p>When enabled, the password sign-in is required for
725758
@ web access coming from 127.0.0.1. When disabled, web access
726759
@ from 127.0.0.1 is allows without any login - the user id is selected
727760
@ from the ~/.fossil database. Password login is always required
728761
@ for incoming web connections on internet addresses other than
729
- @ 127.0.0.1.</p></li>
762
+ @ 127.0.0.1.</p>
730763
731
- @ <hr>
764
+ @ <hr />
732765
onoff_attribute("Allow REMOTE_USER authentication",
733766
"remote_user_ok", "remote_user_ok", 0);
734767
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
735768
@ login name of a valid user and no other login credentials are available,
736769
@ then the REMOTE_USER is accepted as an authenticated user.
737
- @ </p></li>
770
+ @ </p>
738771
739
- @ <hr>
772
+ @ <hr />
740773
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
741774
@ <p>The number of hours for which a login is valid. This must be a
742775
@ positive number. The default is 8760 hours which is approximately equal
743776
@ to a year.</p>
744777
745
- @ <hr>
778
+ @ <hr />
746779
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
747780
"5000000");
748781
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
749782
@ to this many bytes, uncompressed. If the client requires more data
750783
@ than this, then the client will issue multiple HTTP requests.
751784
@ Values below 1 million are not recommended. 5 million is a
752785
@ reasonable number.</p>
753786
754
- @ <hr>
787
+ @ <hr />
755788
onoff_attribute("Show javascript button to fill in CAPTCHA",
756789
"auto-captcha", "autocaptcha", 0);
757790
@ <p>When enabled, a button appears on the login screen for user
758791
@ "anonymous" that will automatically fill in the CAPTCHA password.
759792
@ This is less secure that forcing the user to do it manually, but is
760793
@ probably secure enough and it is certainly more convenient for
761794
@ anonymous users.</p>
762795
763
- @ <hr>
764
- @ <p><input type="submit" name="submit" value="Apply Changes"></p>
765
- @ </form>
796
+ @ <hr />
797
+ @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
798
+ @ </div></form>
766799
db_end_transaction(0);
767800
style_footer();
768801
}
769802
770803
/*
@@ -776,42 +809,42 @@
776809
login_needed();
777810
}
778811
779812
style_header("Timeline Display Preferences");
780813
db_begin_transaction();
781
- @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
814
+ @ <form action="%s(g.zBaseURL)/setup_timeline" method="post"><div>
782815
login_insert_csrf_secret();
783816
784
- @ <hr>
817
+ @ <hr />
785818
onoff_attribute("Allow block-markup in timeline",
786819
"timeline-block-markup", "tbm", 0);
787820
@ <p>In timeline displays, check-in comments can be displayed with or
788821
@ without block markup (paragraphs, tables, etc.)</p>
789822
790
- @ <hr>
823
+ @ <hr />
791824
onoff_attribute("Use Universal Coordinated Time (UTC)",
792825
"timeline-utc", "utc", 1);
793826
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
794827
@ Zulu) instead of in local time.</p>
795828
796
- @ <hr>
829
+ @ <hr />
797830
onoff_attribute("Show version differences by default",
798831
"show-version-diffs", "vdiff", 0);
799832
@ <p>On the version-information pages linked from the timeline can either
800833
@ show complete diffs of all file changes, or can just list the names of
801834
@ the files that have changed. Users can get to either page by
802835
@ clicking. This setting selects the default.</p>
803836
804
- @ <hr>
837
+ @ <hr />
805838
entry_attribute("Max timeline comment length", 6,
806839
"timeline-max-comment", "tmc", "0");
807840
@ <p>The maximum length of a comment to be displayed in a timeline.
808841
@ "0" there is no length limit.</p>
809842
810
- @ <hr>
811
- @ <p><input type="submit" name="submit" value="Apply Changes"></p>
812
- @ </form>
843
+ @ <hr />
844
+ @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
845
+ @ </div></form>
813846
db_end_transaction(0);
814847
style_footer();
815848
}
816849
817850
/*
@@ -823,11 +856,11 @@
823856
login_needed();
824857
}
825858
826859
style_header("WWW Configuration");
827860
db_begin_transaction();
828
- @ <form action="%s(g.zBaseURL)/setup_config" method="POST">
861
+ @ <form action="%s(g.zBaseURL)/setup_config" method="post"><div>
829862
login_insert_csrf_secret();
830863
@ <hr />
831864
entry_attribute("Project Name", 60, "project-name", "pn", "");
832865
@ <p>Give your project a name so visitors know what this site is about.
833866
@ The project name will also be used as the RSS feed title.</p>
@@ -840,36 +873,37 @@
840873
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
841874
@ <p>Enter the pathname of the page to display when the "Home" menu
842875
@ option is selected and when no pathname is
843876
@ specified in the URL. For example, if you visit the url:</p>
844877
@
845
- @ <blockquote>%h(g.zBaseURL)</blockquote>
878
+ @ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
846879
@
847880
@ <p>And you have specified an index page of "/home" the above will
848881
@ automatically redirect to:</p>
849882
@
850
- @ <blockquote>%h(g.zBaseURL)/home</blockquote>
883
+ @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
851884
@
852885
@ <p>The default "/home" page displays a Wiki page with the same name
853886
@ as the Project Name specified above. Some sites prefer to redirect
854887
@ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
855888
@ <hr />
856889
onoff_attribute("Use HTML as wiki markup language",
857890
"wiki-use-html", "wiki-use-html", 0);
858
- @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed but
859
- @ all other wiki formatting will be ignored. This option is helpful if you have
860
- @ chosen to use a rich HTML editor for wiki markup such as TinyMCE.</p>
891
+ @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
892
+ @ but all other wiki formatting will be ignored. This option is helpful
893
+ @ if you have chosen to use a rich HTML editor for wiki markup such as
894
+ @ TinyMCE.</p>
861895
@ <p><strong>CAUTION:</strong> when
862896
@ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
863897
@ No sanitization is done. This means that it is very possible for malicious
864898
@ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
865899
@ <p>This should <strong>only</strong> be enabled when wiki editing is limited
866900
@ to trusted users. It should <strong>not</strong> be used on a publically
867901
@ editable wiki.</p>
868902
@ <hr />
869
- @ <p><input type="submit" name="submit" value="Apply Changes"></p>
870
- @ </form>
903
+ @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
904
+ @ </div></form>
871905
db_end_transaction(0);
872906
style_footer();
873907
}
874908
875909
/*
@@ -892,27 +926,27 @@
892926
if( P("submit")!=0 ){
893927
db_end_transaction(0);
894928
cgi_redirect("setup_editcss");
895929
}
896930
style_header("Edit CSS");
897
- @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
931
+ @ <form action="%s(g.zBaseURL)/setup_editcss" method="post"><div>
898932
login_insert_csrf_secret();
899933
@ Edit the CSS below:<br />
900934
textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
901935
@ <br />
902
- @ <input type="submit" name="submit" value="Apply Changes">
903
- @ <input type="submit" name="clear" value="Revert To Default">
904
- @ </form>
905
- @ <p><b>Note:</b> Press your browser Reload button after modifying the
906
- @ CSS in order to pull in the modified CSS file.</p>
907
- @ <hr>
936
+ @ <input type="submit" name="submit" value="Apply Changes" />
937
+ @ <input type="submit" name="clear" value="Revert To Default" />
938
+ @ </div></form>
939
+ @ <p><span class="note">Note:</span> Press your browser Reload button after
940
+ @ modifying the CSS in order to pull in the modified CSS file.</p>
941
+ @ <hr />
908942
@ The default CSS is shown below for reference. Other examples
909943
@ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
910944
@ See also the <a href="setup_header">header</a> and
911945
@ <a href="setup_footer">footer</a> editing screens.
912946
@ <blockquote><pre>
913
- @ %h(zDefaultCSS)
947
+ cgi_append_default_css();
914948
@ </pre></blockquote>
915949
style_footer();
916950
db_end_transaction(0);
917951
}
918952
@@ -930,21 +964,21 @@
930964
cgi_replace_parameter("header", zDefaultHeader);
931965
}else{
932966
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
933967
}
934968
style_header("Edit Page Header");
935
- @ <form action="%s(g.zBaseURL)/setup_header" method="POST">
969
+ @ <form action="%s(g.zBaseURL)/setup_header" method="post"><div>
936970
login_insert_csrf_secret();
937971
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
938972
@ generate the beginning of every page through start of the main
939973
@ menu.</p>
940974
textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
941975
@ <br />
942
- @ <input type="submit" name="submit" value="Apply Changes">
943
- @ <input type="submit" name="clear" value="Revert To Default">
944
- @ </form>
945
- @ <hr>
976
+ @ <input type="submit" name="submit" value="Apply Changes" />
977
+ @ <input type="submit" name="clear" value="Revert To Default" />
978
+ @ </div></form>
979
+ @ <hr />
946980
@ The default header is shown below for reference. Other examples
947981
@ of headers can be seen on the <a href="setup_skin">skins page</a>.
948982
@ See also the <a href="setup_editcss">CSS</a> and
949983
@ <a href="setup_footer">footer</a> editing screeens.
950984
@ <blockquote><pre>
@@ -968,20 +1002,20 @@
9681002
cgi_replace_parameter("footer", zDefaultFooter);
9691003
}else{
9701004
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
9711005
}
9721006
style_header("Edit Page Footer");
973
- @ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
1007
+ @ <form action="%s(g.zBaseURL)/setup_footer" method="post"><div>
9741008
login_insert_csrf_secret();
9751009
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
9761010
@ generate the end of every page.</p>
9771011
textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
9781012
@ <br />
979
- @ <input type="submit" name="submit" value="Apply Changes">
980
- @ <input type="submit" name="clear" value="Revert To Default">
981
- @ </form>
982
- @ <hr>
1013
+ @ <input type="submit" name="submit" value="Apply Changes" />
1014
+ @ <input type="submit" name="clear" value="Revert To Default" />
1015
+ @ </div></form>
1016
+ @ <hr />
9831017
@ The default footer is shown below for reference. Other examples
9841018
@ of footers can be seen on the <a href="setup_skin">skins page</a>.
9851019
@ See also the <a href="setup_editcss">CSS</a> and
9861020
@ <a href="setup_header">header</a> editing screens.
9871021
@ <blockquote><pre>
@@ -1031,31 +1065,31 @@
10311065
cgi_redirect("setup_logo");
10321066
}
10331067
style_header("Edit Project Logo");
10341068
@ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
10351069
@ like this:</p>
1036
- @ <blockquote><img src="%s(g.zTop)/logo" alt="logo"></blockquote>
1070
+ @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" /></p></blockquote>
10371071
@
10381072
@ <p>The logo is accessible to all users at this URL:
10391073
@ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
10401074
@ The logo may or may not appear on each
10411075
@ page depending on the <a href="setup_editcss">CSS</a> and
10421076
@ <a href="setup_header">header setup</a>.</p>
10431077
@
1044
- @ <form action="%s(g.zBaseURL)/setup_logo" method="POST"
1045
- @ enctype="multipart/form-data">
1078
+ @ <form action="%s(g.zBaseURL)/setup_logo" method="post"
1079
+ @ enctype="multipart/form-data"><div>
10461080
@ <p>To set a new logo image, select a file to use as the logo using
10471081
@ the entry box below and then press the "Change Logo" button.</p>
10481082
login_insert_csrf_secret();
10491083
@ Logo Image file:
1050
- @ <input type="file" name="im" size="60" accepts="image/*"><br>
1051
- @ <input type="submit" name="set" value="Change Logo">
1052
- @ <input type="submit" name="clr" value="Revert To Default">
1053
- @ </form>
1054
- @
1055
- @ <p><b>Note:</b> Your browser has probably cached the logo image, so
1056
- @ you will probably need to press the Reload button on your browser after
1057
- @ changing the logo to provoke your browser to reload the new logo image.
1058
- @ </p>
1084
+ @ <input type="file" name="im" size="60" accept="image/*" /><br />
1085
+ @ <input type="submit" name="set" value="Change Logo" />
1086
+ @ <input type="submit" name="clr" value="Revert To Default" />
1087
+ @ </div></form>
1088
+ @
1089
+ @ <p><span class="note">Note:</span> Your browser has probably cached the
1090
+ @ logo image, so you will probably need to press the Reload button on your
1091
+ @ browser after changing the logo to provoke your browser to reload the new
1092
+ @ logo image. </p>
10591093
style_footer();
10601094
db_end_transaction(0);
10611095
}
10621096
--- src/setup.c
+++ src/setup.c
@@ -99,42 +99,40 @@
99 return;
100 }
101
102 style_submenu_element("Add", "Add User", "setup_uedit");
103 style_header("User List");
104 @ <table border="0" cellpadding="0" cellspacing="25">
105 @ <tr><td valign="top">
106 @ <b>Users:</b>
107 @ <table border="1" cellpadding="10"><tr><td>
108 @ <table cellspacing=0 cellpadding=0 border=0>
109 @ <tr>
110 @ <th align="right">User&nbsp;ID</th><td width="20">&nbsp;</td>
111 @ <th>Capabilities</th><td width="15">&nbsp;</td>
112 @ <th>Contact&nbsp;Info</th>
113 @ </tr>
114 db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
115 while( db_step(&s)==SQLITE_ROW ){
116 const char *zCap = db_column_text(&s, 2);
117 if( strstr(zCap, "s") ) zCap = "s";
118 @ <tr>
119 @ <td align="right">
120 if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
121 @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
122 }
123 @ <nobr>%h(db_column_text(&s,1))</nobr>
124 if( g.okAdmin ){
125 @ </a>
126 }
127 @ </td><td>&nbsp;&nbsp;&nbsp;</td>
128 @ <td align="center">%s(zCap)</td>
129 @ <td>&nbsp;&nbsp;&nbsp;</td>
130 @ <td align="left">%s(db_column_text(&s,3))</td>
131 @ </tr>
132 }
133 @ </table></td></tr></table>
134 @ <td valign="top">
135 @ <b>Notes:</b>
136 @ <ol>
137 @ <li><p>The permission flags are as follows:</p>
138 @ <table>
139 @ <tr><td valign="top"><b>a</b></td>
140 @ <td><i>Admin:</i> Create and delete users</td></tr>
@@ -181,31 +179,35 @@
181 @ user <tt>developer</tt></td></tr>
182 @ <tr><td valign="top"><b>w</b></td>
183 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
184 @ <tr><td valign="top"><b>z</b></td>
185 @ <td><i>Zip download:</i> Download a baseline via the
186 @ <tt>/zip</tt> URL even without check<b>o</b>ut
187 @ and <b>h</b>istory permissions</td></tr>
 
188 @ </table>
189 @ </li>
190 @
191 @ <li><p>
192 @ Every user, logged in or not, inherits the privileges of <b>nobody</b>.
 
193 @ </p></li>
194 @
195 @ <li><p>
196 @ Any human can login as <b>anonymous</b> since the password is
197 @ clearly displayed on the login page for them to type. The purpose
198 @ of requiring anonymous to log in is to prevent access by spiders.
199 @ Every logged-in user inherits the combined privileges of
200 @ <b>anonymous</b> and
201 @ <b>nobody</b>.
202 @ </p></li>
203 @
204 @ <li><p>
205 @ Users with privilege <b>v</b> inherit the combined privileges of
206 @ <b>developer</b>, <b>anonymous</b>, and <b>nobody</b>.
 
 
207 @ </p></li>
208 @
209 @ </ol>
210 @ </td></tr></table>
211 style_footer();
@@ -323,12 +325,12 @@
323 }
324 if( uid>0 &&
325 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
326 ){
327 style_header("User Creation Error");
328 @ <font color="red">Login "%h(zLogin)" is already used by a different
329 @ user.</font>
330 @
331 @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
332 style_footer();
333 return;
334 }
@@ -353,65 +355,69 @@
353 if( uid ){
354 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
355 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
356 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
357 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
358 if( strchr(zCap, 'a') ) oaa = " checked";
359 if( strchr(zCap, 'b') ) oab = " checked";
360 if( strchr(zCap, 'c') ) oac = " checked";
361 if( strchr(zCap, 'd') ) oad = " checked";
362 if( strchr(zCap, 'e') ) oae = " checked";
363 if( strchr(zCap, 'f') ) oaf = " checked";
364 if( strchr(zCap, 'g') ) oag = " checked";
365 if( strchr(zCap, 'h') ) oah = " checked";
366 if( strchr(zCap, 'i') ) oai = " checked";
367 if( strchr(zCap, 'j') ) oaj = " checked";
368 if( strchr(zCap, 'k') ) oak = " checked";
369 if( strchr(zCap, 'm') ) oam = " checked";
370 if( strchr(zCap, 'n') ) oan = " checked";
371 if( strchr(zCap, 'o') ) oao = " checked";
372 if( strchr(zCap, 'p') ) oap = " checked";
373 if( strchr(zCap, 'r') ) oar = " checked";
374 if( strchr(zCap, 's') ) oas = " checked";
375 if( strchr(zCap, 't') ) oat = " checked";
376 if( strchr(zCap, 'u') ) oau = " checked";
377 if( strchr(zCap, 'v') ) oav = " checked";
378 if( strchr(zCap, 'w') ) oaw = " checked";
379 if( strchr(zCap, 'z') ) oaz = " checked";
380 }
381
382 /* figure out inherited permissions */
383 memset(inherit, 0, sizeof(inherit));
384 if( strcmp(zLogin, "developer") ){
385 char *z1, *z2;
386 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
387 while( z1 && *z1 ){
388 inherit[0x7f & *(z1++)] = "<font color=\"red\">&bull;</font>";
 
389 }
390 free(z2);
391 }
392 if( strcmp(zLogin, "reader") ){
393 char *z1, *z2;
394 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
395 while( z1 && *z1 ){
396 inherit[0x7f & *(z1++)] = "<font color=\"black\">&bull;</font>";
 
397 }
398 free(z2);
399 }
400 if( strcmp(zLogin, "anonymous") ){
401 char *z1, *z2;
402 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
403 while( z1 && *z1 ){
404 inherit[0x7f & *(z1++)] = "<font color=\"blue\">&bull;</font>";
 
405 }
406 free(z2);
407 }
408 if( strcmp(zLogin, "nobody") ){
409 char *z1, *z2;
410 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
411 while( z1 && *z1 ){
412 inherit[0x7f & *(z1++)] = "<font color=\"green\">&bull;</font>";
 
413 }
414 free(z2);
415 }
416
417 /* Begin generating the page
@@ -420,77 +426,79 @@
420 if( uid ){
421 style_header(mprintf("Edit User %h", zLogin));
422 }else{
423 style_header("Add A New User");
424 }
425 @ <table align="left" hspace="20" vspace="10"><tr><td>
426 @ <form action="%s(g.zPath)" method="POST">
427 login_insert_csrf_secret();
428 @ <table>
429 @ <tr>
430 @ <td align="right"><nobr>User ID:</nobr></td>
431 if( uid ){
432 @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
433 }else{
434 @ <td>(new user)<input type="hidden" name="id" value=0></td>
435 }
436 @ </tr>
437 @ <tr>
438 @ <td align="right"><nobr>Login:</nobr></td>
439 @ <td><input type="text" name="login" value="%h(zLogin)"></td>
440 @ </tr>
441 @ <tr>
442 @ <td align="right"><nobr>Contact&nbsp;Info:</nobr></td>
443 @ <td><input type="text" name="info" size=40 value="%h(zInfo)"></td>
444 @ </tr>
445 @ <tr>
446 @ <td align="right" valign="top">Capabilities:</td>
447 @ <td>
448 #define B(x) inherit[x]
449 if( g.okSetup ){
450 @ <input type="checkbox" name="as"%s(oas)/>%s(B('s'))Setup<br>
451 }
452 @ <input type="checkbox" name="aa"%s(oaa)/>%s(B('a'))Admin<br>
453 @ <input type="checkbox" name="ad"%s(oad)/>%s(B('d'))Delete<br>
454 @ <input type="checkbox" name="ae"%s(oae)/>%s(B('e'))Email<br>
455 @ <input type="checkbox" name="ap"%s(oap)/>%s(B('p'))Password<br>
456 @ <input type="checkbox" name="ai"%s(oai)/>%s(B('i'))Check-In<br>
457 @ <input type="checkbox" name="ao"%s(oao)/>%s(B('o'))Check-Out<br>
458 @ <input type="checkbox" name="ah"%s(oah)/>%s(B('h'))History<br>
459 @ <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
460 @ <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
461 @ <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
462 @ <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
463 @ <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
464 @ <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
465 @ <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
466 @ <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
467 @ <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
468 @ <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
469 @ <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
470 @ <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
471 @ <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
472 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
473 @ </td>
474 @ </tr>
475 @ <tr>
476 @ <td align="right">Password:</td>
477 if( zPw[0] ){
478 /* Obscure the password for all users */
479 @ <td><input type="password" name="pw" value="**********"></td>
480 }else{
481 /* Show an empty password as an empty input field */
482 @ <td><input type="password" name="pw" value=""></td>
483 }
484 @ </tr>
485 if( !higherUser ){
486 @ <tr>
487 @ <td>&nbsp</td>
488 @ <td><input type="submit" name="submit" value="Apply Changes">
489 @ </tr>
490 }
491 @ </table></td></tr></table>
 
 
492 @ <h2>Privileges And Capabilities:</h2>
493 @ <ul>
494 if( higherUser ){
495 @ <li><p><font color="blue"><b>
496 @ User %h(zLogin) has Setup privileges and you only have Admin privileges
@@ -497,95 +505,114 @@
497 @ so you are not permitted to make changes to %h(zLogin).
498 @ </b></font></p></li>
499 @
500 }
501 @ <li><p>
502 @ The <b>Setup</b> user can make arbitrary configuration changes.
503 @ An <b>Admin</b> user can add other users and change user privileges
 
504 @ and reset user passwords. Both automatically get all other privileges
505 @ listed below. Use these two settings with discretion.
506 @ </p></li>
507 @
508 @ <li><p>
509 @ The "<font color="green"><big>&bull;</big></font>" mark indicates
510 @ the privileges of "nobody" that are available to all users
511 @ regardless of whether or not they are logged in.
512 @ </p></li>
513 @
514 @ <li><p>
515 @ The "<font color="blue"><big>&bull;</big></font>" mark indicates
516 @ the privileges of "anonymous" that are inherited by all logged-in users.
517 @ </p></li>
518 @
519 @ <li><p>
520 @ The "<font color="red"><big>&bull;</big></font>" mark indicates
521 @ the privileges of "developer" that are inherited by all users with
522 @ the <b>Developer</b> privilege.
523 @ </p></li>
524 @
525 @ <li><p>
526 @ The "<font color="black"><big>&bull;</big></font>" mark indicates
527 @ the privileges of "reader" that are inherited by all users with
528 @ the <b>Reader</b> privilege.
529 @ </p></li>
530 @
531 @ <li><p>
532 @ The <b>Delete</b> privilege give the user the ability to erase
533 @ wiki, tickets, and attachments that have been added by anonymous
534 @ users. This capability is intended for deletion of spam. The
535 @ delete capability is only in effect for 24 hours after the item
536 @ is first posted. The Setup user can delete anything at any time.
537 @ </p></li>
538 @
539 @ <li><p>
540 @ The <b>History</b> privilege allows a user to see most hyperlinks.
541 @ This is recommended ON for most logged-in users but OFF for
542 @ user "nobody" to avoid problems with spiders trying to walk every
543 @ historical version of every baseline and file.
544 @ </p></li>
545 @
546 @ <li><p>
547 @ The <b>Zip</b> privilege allows a user to see the "download as ZIP"
548 @ hyperlink and permits access to the <tt>/zip</tt> page. This allows
549 @ users to download ZIP archives without granting other rights like
550 @ <b>Read</b> or <b>History</b>. This privilege is recommended for
551 @ user <b>nobody</b> so that automatic package downloaders can obtain
552 @ the sources without going through the login procedure.
553 @ </p></li>
554 @
555 @ <li><p>
556 @ The <b>Check-in</b> privilege allows remote users to "push".
557 @ The <b>Check-out</b> privilege allows remote users to "pull".
558 @ The <b>Clone</b> privilege allows remote users to "clone".
559 @ </li><p>
560 @
561 @ <li><p>
562 @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
563 @ <b>Write Wiki</b> privileges control access to wiki pages. The
564 @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
565 @ <b>Write Ticket</b> privileges control access to trouble tickets.
566 @ The <b>Ticket Report</b> privilege allows the user to create or edit
567 @ ticket report formats.
568 @ </p></li>
569 @
570 @ <li><p>
571 @ Users with the <b>Password</b> privilege are allowed to change their
572 @ own password. Recommended ON for most users but OFF for special
573 @ users "developer", "anonymous", and "nobody".
574 @ </p></li>
575 @
576 @ <li><p>
577 @ The <b>EMail</b> privilege allows the display of sensitive information
578 @ such as the email address of users and contact information on tickets.
579 @ Recommended OFF for "anonymous" and for "nobody" but ON for
580 @ "developer".
581 @ </p></li>
582 @
583 @ <li><p>
584 @ The <b>Attachment</b> privilege is needed in order to add attachments
585 @ to tickets or wiki. Write privilege on the ticket or wiki is also
586 @ required.</p></li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587 @
588 @ <li><p>
589 @ Login is prohibited if the password is an empty string.
590 @ </p></li>
591 @ </ul>
@@ -592,42 +619,46 @@
592 @
593 @ <h2>Special Logins</h2>
594 @
595 @ <ul>
596 @ <li><p>
597 @ No login is required for user "<b>nobody</b>". The capabilities
598 @ of the <b>nobody</b> user are inherited by all users, regardless of
599 @ whether or not they are logged in. To disable universal access
600 @ to the repository, make sure no user named "<b>nobody</b>" exists or
601 @ that the <b>nobody</b> user has no capabilities enabled.
602 @ The password for <b>nobody</b> is ignore. To avoid problems with
603 @ spiders overloading the server, it is recommended
604 @ that the 'h' (History) capability be turned off for the <b>nobody</b>
605 @ user.
606 @ </p></li>
607 @
608 @ <li><p>
609 @ Login is required for user "<b>anonymous</b>" but the password
610 @ is displayed on the login screen beside the password entry box
611 @ so anybody who can read should be able to login as anonymous.
612 @ On the other hand, spiders and web-crawlers will typically not
613 @ be able to login. Set the capabilities of the anonymous user
614 @ to things that you want any human to be able to do, but not any
615 @ spider. Every other logged-in user inherits the privileges of
616 @ <b>anonymous</b>.
617 @ </p></li>
618 @
619 @ <li><p>
620 @ The "<b>developer</b>" user is intended as a template for trusted users
621 @ with check-in privileges. When adding new trusted users, simply
622 @ select the <b>Developer</b> privilege to cause the new user to inherit
623 @ all privileges of the "developer" user. Similarly, the "<b>reader</b>"
624 @ user is a template for users who are allowed more access than anonymous,
625 @ but less than a developer.
626 @ </p></li>
627 @ </ul>
628 @ </form>
 
 
 
 
629 style_footer();
630 }
631
632
633 /*
@@ -651,13 +682,14 @@
651 db_set(zVar, iQ ? "1" : "0", 0);
652 iVal = iQ;
653 }
654 }
655 if( iVal ){
656 @ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
 
657 }else{
658 @ <input type="checkbox" name="%s(zQParm)"><b>%s(zLabel)</b></input>
659 }
660 }
661
662 /*
663 ** Generate an entry box for an attribute.
@@ -674,11 +706,11 @@
674 if( zQ && strcmp(zQ,zVal)!=0 ){
675 login_verify_csrf_secret();
676 db_set(zVar, zQ, 0);
677 zVal = zQ;
678 }
679 @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
680 @ <b>%s(zLabel)</b>
681 }
682
683 /*
684 ** Generate a text box for an attribute.
@@ -698,11 +730,12 @@
698 db_set(zVar, zQ, 0);
699 z = zQ;
700 }
701 if( rows>0 && cols>0 ){
702 @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
703 @ <b>%s(zLabel)</b>
 
704 }
705 }
706
707
708 /*
@@ -714,57 +747,57 @@
714 login_needed();
715 }
716
717 style_header("Access Control Settings");
718 db_begin_transaction();
719 @ <form action="%s(g.zBaseURL)/setup_access" method="POST">
720 login_insert_csrf_secret();
721 @ <hr>
722 onoff_attribute("Require password for local access",
723 "localauth", "localauth", 0);
724 @ <p>When enabled, the password sign-in is required for
725 @ web access coming from 127.0.0.1. When disabled, web access
726 @ from 127.0.0.1 is allows without any login - the user id is selected
727 @ from the ~/.fossil database. Password login is always required
728 @ for incoming web connections on internet addresses other than
729 @ 127.0.0.1.</p></li>
730
731 @ <hr>
732 onoff_attribute("Allow REMOTE_USER authentication",
733 "remote_user_ok", "remote_user_ok", 0);
734 @ <p>When enabled, if the REMOTE_USER environment variable is set to the
735 @ login name of a valid user and no other login credentials are available,
736 @ then the REMOTE_USER is accepted as an authenticated user.
737 @ </p></li>
738
739 @ <hr>
740 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
741 @ <p>The number of hours for which a login is valid. This must be a
742 @ positive number. The default is 8760 hours which is approximately equal
743 @ to a year.</p>
744
745 @ <hr>
746 entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
747 "5000000");
748 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
749 @ to this many bytes, uncompressed. If the client requires more data
750 @ than this, then the client will issue multiple HTTP requests.
751 @ Values below 1 million are not recommended. 5 million is a
752 @ reasonable number.</p>
753
754 @ <hr>
755 onoff_attribute("Show javascript button to fill in CAPTCHA",
756 "auto-captcha", "autocaptcha", 0);
757 @ <p>When enabled, a button appears on the login screen for user
758 @ "anonymous" that will automatically fill in the CAPTCHA password.
759 @ This is less secure that forcing the user to do it manually, but is
760 @ probably secure enough and it is certainly more convenient for
761 @ anonymous users.</p>
762
763 @ <hr>
764 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
765 @ </form>
766 db_end_transaction(0);
767 style_footer();
768 }
769
770 /*
@@ -776,42 +809,42 @@
776 login_needed();
777 }
778
779 style_header("Timeline Display Preferences");
780 db_begin_transaction();
781 @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
782 login_insert_csrf_secret();
783
784 @ <hr>
785 onoff_attribute("Allow block-markup in timeline",
786 "timeline-block-markup", "tbm", 0);
787 @ <p>In timeline displays, check-in comments can be displayed with or
788 @ without block markup (paragraphs, tables, etc.)</p>
789
790 @ <hr>
791 onoff_attribute("Use Universal Coordinated Time (UTC)",
792 "timeline-utc", "utc", 1);
793 @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
794 @ Zulu) instead of in local time.</p>
795
796 @ <hr>
797 onoff_attribute("Show version differences by default",
798 "show-version-diffs", "vdiff", 0);
799 @ <p>On the version-information pages linked from the timeline can either
800 @ show complete diffs of all file changes, or can just list the names of
801 @ the files that have changed. Users can get to either page by
802 @ clicking. This setting selects the default.</p>
803
804 @ <hr>
805 entry_attribute("Max timeline comment length", 6,
806 "timeline-max-comment", "tmc", "0");
807 @ <p>The maximum length of a comment to be displayed in a timeline.
808 @ "0" there is no length limit.</p>
809
810 @ <hr>
811 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
812 @ </form>
813 db_end_transaction(0);
814 style_footer();
815 }
816
817 /*
@@ -823,11 +856,11 @@
823 login_needed();
824 }
825
826 style_header("WWW Configuration");
827 db_begin_transaction();
828 @ <form action="%s(g.zBaseURL)/setup_config" method="POST">
829 login_insert_csrf_secret();
830 @ <hr />
831 entry_attribute("Project Name", 60, "project-name", "pn", "");
832 @ <p>Give your project a name so visitors know what this site is about.
833 @ The project name will also be used as the RSS feed title.</p>
@@ -840,36 +873,37 @@
840 entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
841 @ <p>Enter the pathname of the page to display when the "Home" menu
842 @ option is selected and when no pathname is
843 @ specified in the URL. For example, if you visit the url:</p>
844 @
845 @ <blockquote>%h(g.zBaseURL)</blockquote>
846 @
847 @ <p>And you have specified an index page of "/home" the above will
848 @ automatically redirect to:</p>
849 @
850 @ <blockquote>%h(g.zBaseURL)/home</blockquote>
851 @
852 @ <p>The default "/home" page displays a Wiki page with the same name
853 @ as the Project Name specified above. Some sites prefer to redirect
854 @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
855 @ <hr />
856 onoff_attribute("Use HTML as wiki markup language",
857 "wiki-use-html", "wiki-use-html", 0);
858 @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed but
859 @ all other wiki formatting will be ignored. This option is helpful if you have
860 @ chosen to use a rich HTML editor for wiki markup such as TinyMCE.</p>
 
861 @ <p><strong>CAUTION:</strong> when
862 @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
863 @ No sanitization is done. This means that it is very possible for malicious
864 @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
865 @ <p>This should <strong>only</strong> be enabled when wiki editing is limited
866 @ to trusted users. It should <strong>not</strong> be used on a publically
867 @ editable wiki.</p>
868 @ <hr />
869 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
870 @ </form>
871 db_end_transaction(0);
872 style_footer();
873 }
874
875 /*
@@ -892,27 +926,27 @@
892 if( P("submit")!=0 ){
893 db_end_transaction(0);
894 cgi_redirect("setup_editcss");
895 }
896 style_header("Edit CSS");
897 @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
898 login_insert_csrf_secret();
899 @ Edit the CSS below:<br />
900 textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
901 @ <br />
902 @ <input type="submit" name="submit" value="Apply Changes">
903 @ <input type="submit" name="clear" value="Revert To Default">
904 @ </form>
905 @ <p><b>Note:</b> Press your browser Reload button after modifying the
906 @ CSS in order to pull in the modified CSS file.</p>
907 @ <hr>
908 @ The default CSS is shown below for reference. Other examples
909 @ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
910 @ See also the <a href="setup_header">header</a> and
911 @ <a href="setup_footer">footer</a> editing screens.
912 @ <blockquote><pre>
913 @ %h(zDefaultCSS)
914 @ </pre></blockquote>
915 style_footer();
916 db_end_transaction(0);
917 }
918
@@ -930,21 +964,21 @@
930 cgi_replace_parameter("header", zDefaultHeader);
931 }else{
932 textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
933 }
934 style_header("Edit Page Header");
935 @ <form action="%s(g.zBaseURL)/setup_header" method="POST">
936 login_insert_csrf_secret();
937 @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
938 @ generate the beginning of every page through start of the main
939 @ menu.</p>
940 textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
941 @ <br />
942 @ <input type="submit" name="submit" value="Apply Changes">
943 @ <input type="submit" name="clear" value="Revert To Default">
944 @ </form>
945 @ <hr>
946 @ The default header is shown below for reference. Other examples
947 @ of headers can be seen on the <a href="setup_skin">skins page</a>.
948 @ See also the <a href="setup_editcss">CSS</a> and
949 @ <a href="setup_footer">footer</a> editing screeens.
950 @ <blockquote><pre>
@@ -968,20 +1002,20 @@
968 cgi_replace_parameter("footer", zDefaultFooter);
969 }else{
970 textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
971 }
972 style_header("Edit Page Footer");
973 @ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
974 login_insert_csrf_secret();
975 @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
976 @ generate the end of every page.</p>
977 textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
978 @ <br />
979 @ <input type="submit" name="submit" value="Apply Changes">
980 @ <input type="submit" name="clear" value="Revert To Default">
981 @ </form>
982 @ <hr>
983 @ The default footer is shown below for reference. Other examples
984 @ of footers can be seen on the <a href="setup_skin">skins page</a>.
985 @ See also the <a href="setup_editcss">CSS</a> and
986 @ <a href="setup_header">header</a> editing screens.
987 @ <blockquote><pre>
@@ -1031,31 +1065,31 @@
1031 cgi_redirect("setup_logo");
1032 }
1033 style_header("Edit Project Logo");
1034 @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
1035 @ like this:</p>
1036 @ <blockquote><img src="%s(g.zTop)/logo" alt="logo"></blockquote>
1037 @
1038 @ <p>The logo is accessible to all users at this URL:
1039 @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
1040 @ The logo may or may not appear on each
1041 @ page depending on the <a href="setup_editcss">CSS</a> and
1042 @ <a href="setup_header">header setup</a>.</p>
1043 @
1044 @ <form action="%s(g.zBaseURL)/setup_logo" method="POST"
1045 @ enctype="multipart/form-data">
1046 @ <p>To set a new logo image, select a file to use as the logo using
1047 @ the entry box below and then press the "Change Logo" button.</p>
1048 login_insert_csrf_secret();
1049 @ Logo Image file:
1050 @ <input type="file" name="im" size="60" accepts="image/*"><br>
1051 @ <input type="submit" name="set" value="Change Logo">
1052 @ <input type="submit" name="clr" value="Revert To Default">
1053 @ </form>
1054 @
1055 @ <p><b>Note:</b> Your browser has probably cached the logo image, so
1056 @ you will probably need to press the Reload button on your browser after
1057 @ changing the logo to provoke your browser to reload the new logo image.
1058 @ </p>
1059 style_footer();
1060 db_end_transaction(0);
1061 }
1062
--- src/setup.c
+++ src/setup.c
@@ -99,42 +99,40 @@
99 return;
100 }
101
102 style_submenu_element("Add", "Add User", "setup_uedit");
103 style_header("User List");
104 @ <table class="usetupLayoutTable">
105 @ <tr><td class="usetupColumnLayout">
106 @ <span class="note">Users:</span>
107 @ <table class="usetupUserList">
 
108 @ <tr>
109 @ <th class="usetupListUser" style="text-align: right;padding-right: 20px;">User&nbsp;ID</th>
110 @ <th class="usetupListCap" style="text-align: center;padding-right: 15px;">Capabilities</th>
111 @ <th class="usetupListCon" style="text-align: left;">Contact&nbsp;Info</th>
112 @ </tr>
113 db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
114 while( db_step(&s)==SQLITE_ROW ){
115 const char *zCap = db_column_text(&s, 2);
116 if( strstr(zCap, "s") ) zCap = "s";
117 @ <tr>
118 @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
119 if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
120 @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
121 }
122 @ %h(db_column_text(&s,1))
123 if( g.okAdmin ){
124 @ </a>
125 }
126 @ </td>
127 @ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td>
128 @ <td class="usetupListCon" style="text-align: left;">%s(db_column_text(&s,3))</td>
 
129 @ </tr>
130 }
131 @ </table>
132 @ </td><td class="usetupColumnLayout">
133 @ <span class="note">Notes:</span>
134 @ <ol>
135 @ <li><p>The permission flags are as follows:</p>
136 @ <table>
137 @ <tr><td valign="top"><b>a</b></td>
138 @ <td><i>Admin:</i> Create and delete users</td></tr>
@@ -181,31 +179,35 @@
179 @ user <tt>developer</tt></td></tr>
180 @ <tr><td valign="top"><b>w</b></td>
181 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
182 @ <tr><td valign="top"><b>z</b></td>
183 @ <td><i>Zip download:</i> Download a baseline via the
184 @ <tt>/zip</tt> URL even without
185 @ check<span class="capability">o</span>ut
186 @ and <span class="capability">h</span>istory permissions</td></tr>
187 @ </table>
188 @ </li>
189 @
190 @ <li><p>
191 @ Every user, logged in or not, inherits the privileges of
192 @ <span class="usertype">nobody</span>.
193 @ </p></li>
194 @
195 @ <li><p>
196 @ Any human can login as <span class="usertype">anonymous</span> since the
197 @ password is clearly displayed on the login page for them to type. The
198 @ purpose of requiring anonymous to log in is to prevent access by spiders.
199 @ Every logged-in user inherits the combined privileges of
200 @ <span class="usertype">anonymous</span> and
201 @ <span class="usertype">nobody</span>.
202 @ </p></li>
203 @
204 @ <li><p>
205 @ Users with privilege <span class="capability">v</span> inherit the combined
206 @ privileges of <span class="usertype">developer</span>,
207 @ <span class="usertype">anonymous</span>, and
208 @ <span class="usertype">nobody</span>.
209 @ </p></li>
210 @
211 @ </ol>
212 @ </td></tr></table>
213 style_footer();
@@ -323,12 +325,12 @@
325 }
326 if( uid>0 &&
327 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
328 ){
329 style_header("User Creation Error");
330 @ <span class="loginError">Login "%h(zLogin)" is already used by
331 @ a different user.</span>
332 @
333 @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
334 style_footer();
335 return;
336 }
@@ -353,65 +355,69 @@
355 if( uid ){
356 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
357 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
358 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
359 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
360 if( strchr(zCap, 'a') ) oaa = " checked=\"checked\"";
361 if( strchr(zCap, 'b') ) oab = " checked=\"checked\"";
362 if( strchr(zCap, 'c') ) oac = " checked=\"checked\"";
363 if( strchr(zCap, 'd') ) oad = " checked=\"checked\"";
364 if( strchr(zCap, 'e') ) oae = " checked=\"checked\"";
365 if( strchr(zCap, 'f') ) oaf = " checked=\"checked\"";
366 if( strchr(zCap, 'g') ) oag = " checked=\"checked\"";
367 if( strchr(zCap, 'h') ) oah = " checked=\"checked\"";
368 if( strchr(zCap, 'i') ) oai = " checked=\"checked\"";
369 if( strchr(zCap, 'j') ) oaj = " checked=\"checked\"";
370 if( strchr(zCap, 'k') ) oak = " checked=\"checked\"";
371 if( strchr(zCap, 'm') ) oam = " checked=\"checked\"";
372 if( strchr(zCap, 'n') ) oan = " checked=\"checked\"";
373 if( strchr(zCap, 'o') ) oao = " checked=\"checked\"";
374 if( strchr(zCap, 'p') ) oap = " checked=\"checked\"";
375 if( strchr(zCap, 'r') ) oar = " checked=\"checked\"";
376 if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
377 if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
378 if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
379 if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
380 if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
381 if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
382 }
383
384 /* figure out inherited permissions */
385 memset(inherit, 0, sizeof(inherit));
386 if( strcmp(zLogin, "developer") ){
387 char *z1, *z2;
388 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
389 while( z1 && *z1 ){
390 inherit[0x7f & *(z1++)] =
391 "<span class=\"ueditInheritDeveloper\">&bull;</span>";
392 }
393 free(z2);
394 }
395 if( strcmp(zLogin, "reader") ){
396 char *z1, *z2;
397 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
398 while( z1 && *z1 ){
399 inherit[0x7f & *(z1++)] =
400 "<span class=\"ueditInheritReader\">&bull;</span>";
401 }
402 free(z2);
403 }
404 if( strcmp(zLogin, "anonymous") ){
405 char *z1, *z2;
406 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
407 while( z1 && *z1 ){
408 inherit[0x7f & *(z1++)] =
409 "<span class=\"ueditInheritAnonymous\">&bull;</span>";
410 }
411 free(z2);
412 }
413 if( strcmp(zLogin, "nobody") ){
414 char *z1, *z2;
415 z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
416 while( z1 && *z1 ){
417 inherit[0x7f & *(z1++)] =
418 "<span class=\"ueditInheritNobody\">&bull;</span>";
419 }
420 free(z2);
421 }
422
423 /* Begin generating the page
@@ -420,77 +426,79 @@
426 if( uid ){
427 style_header(mprintf("Edit User %h", zLogin));
428 }else{
429 style_header("Add A New User");
430 }
431 @ <div class="ueditCapBox">
432 @ <form action="%s(g.zPath)" method="post"><div>
433 login_insert_csrf_secret();
434 @ <table>
435 @ <tr>
436 @ <td class="usetupEditLabel">User ID:</td>
437 if( uid ){
438 @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
439 }else{
440 @ <td>(new user)<input type="hidden" name="id" value="0" /></td>
441 }
442 @ </tr>
443 @ <tr>
444 @ <td class="usetupEditLabel">Login:</td>
445 @ <td><input type="text" name="login" value="%h(zLogin)" /></td>
446 @ </tr>
447 @ <tr>
448 @ <td class="usetupEditLabel">Contact&nbsp;Info:</td>
449 @ <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
450 @ </tr>
451 @ <tr>
452 @ <td class="usetupEditLabel">Capabilities:</td>
453 @ <td>
454 #define B(x) inherit[x]
455 if( g.okSetup ){
456 @ <input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup<br />
457 }
458 @ <input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin<br />
459 @ <input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete<br />
460 @ <input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email<br />
461 @ <input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password<br />
462 @ <input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In<br />
463 @ <input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out<br />
464 @ <input type="checkbox" name="ah"%s(oah) />%s(B('h'))History<br />
465 @ <input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader<br />
466 @ <input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer<br />
467 @ <input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone<br />
468 @ <input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki<br />
469 @ <input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki<br />
470 @ <input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki<br />
471 @ <input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki<br />
472 @ <input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments<br />
473 @ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
474 @ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
475 @ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
476 @ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
477 @ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
478 @ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
479 @ </td>
480 @ </tr>
481 @ <tr>
482 @ <td align="right">Password:</td>
483 if( zPw[0] ){
484 /* Obscure the password for all users */
485 @ <td><input type="password" name="pw" value="**********" /></td>
486 }else{
487 /* Show an empty password as an empty input field */
488 @ <td><input type="password" name="pw" value="" /></td>
489 }
490 @ </tr>
491 if( !higherUser ){
492 @ <tr>
493 @ <td>&nbsp;</td>
494 @ <td><input type="submit" name="submit" value="Apply Changes" /></td>
495 @ </tr>
496 }
497 @ </table>
498 @ </div></form>
499 @ </div>
500 @ <h2>Privileges And Capabilities:</h2>
501 @ <ul>
502 if( higherUser ){
503 @ <li><p><font color="blue"><b>
504 @ User %h(zLogin) has Setup privileges and you only have Admin privileges
@@ -497,95 +505,114 @@
505 @ so you are not permitted to make changes to %h(zLogin).
506 @ </b></font></p></li>
507 @
508 }
509 @ <li><p>
510 @ The <span class="capability">Setup</span> user can make arbitrary
511 @ configuration changes. An <span class="usertype">Admin</span> user
512 @ can add other users and change user privileges
513 @ and reset user passwords. Both automatically get all other privileges
514 @ listed below. Use these two settings with discretion.
515 @ </p></li>
516 @
517 @ <li><p>
518 @ The "<span class="ueditInheritNobody"><big>&bull;</big></span>" mark
519 @ indicates the privileges of <span class="usertype">nobody</span> that
520 @ are available to all users regardless of whether or not they are logged in.
521 @ </p></li>
522 @
523 @ <li><p>
524 @ The "<span class="ueditInheritAnonymous"><big>&bull;</big></span>" mark
525 @ indicates the privileges of <span class="usertype">anonymous</span> that
526 @ are inherited by all logged-in users.
527 @ </p></li>
528 @
529 @ <li><p>
530 @ The "<span class="ueditInheritDeveloper"><big>&bull;</big></span>" mark
531 @ indicates the privileges of <span class="usertype">developer</span> that
532 @ are inherited by all users with the
533 @ <span class="capability">Developer</span> privilege.
534 @ </p></li>
535 @
536 @ <li><p>
537 @ The "<span class="ueditInheritReader"><big>&bull;</big></span>" mark
538 @ indicates the privileges of <span class="usertype">reader</span> that
539 @ are inherited by all users with the <span class="capability">Reader</span>
540 @ privilege.
541 @ </p></li>
542 @
543 @ <li><p>
544 @ The <span class="capability">Delete</span> privilege give the user the
545 @ ability to erase wiki, tickets, and attachments that have been added
546 @ by anonymous users. This capability is intended for deletion of spam.
547 @ The delete capability is only in effect for 24 hours after the item
548 @ is first posted. The <span class="usertype">Setup</span> user can
549 @ delete anything at any time.
550 @ </p></li>
551 @
552 @ <li><p>
553 @ The <span class="capability">History</span> privilege allows a user
554 @ to see most hyperlinks. This is recommended ON for most logged-in users
555 @ but OFF for user "nobody" to avoid problems with spiders trying to walk
556 @ every historical version of every baseline and file.
557 @ </p></li>
558 @
559 @ <li><p>
560 @ The <span class="capability">Zip</span> privilege allows a user to
561 @ see the "download as ZIP"
562 @ hyperlink and permits access to the <tt>/zip</tt> page. This allows
563 @ users to download ZIP archives without granting other rights like
564 @ <span class="capability">Read</span> or
565 @ <span class="capability">History</span>. This privilege is recommended for
566 @ user <span class="usertype">nobody</span> so that automatic package
567 @ downloaders can obtain the sources without going through the login
568 @ procedure.
569 @ </p></li>
570 @
571 @ <li><p>
572 @ The <span class="capability">Check-in</span> privilege allows remote
573 @ users to "push". The <span class="capability">Check-out</span> privilege
574 @ allows remote users to "pull". The <span class="capability">Clone</span>
575 @ privilege allows remote users to "clone".
576 @ </p></li>
577 @
578 @ <li><p>
579 @ The <span class="capability">Read Wiki</span>,
580 @ <span class="capability">New Wiki</span>,
581 @ <span class="capability">Append Wiki</span>, and
582 @ <b>Write Wiki</b> privileges control access to wiki pages. The
583 @ <span class="capability">Read Ticket</span>,
584 @ <span class="capability">New Ticket</span>,
585 @ <span class="capability">Append Ticket</span>, and
586 @ <span class="capability">Write Ticket</span> privileges control access
587 @ to trouble tickets.
588 @ The <span class="capability">Ticket Report</span> privilege allows
589 @ the user to create or edit ticket report formats.
590 @ </p></li>
591 @
592 @ <li><p>
593 @ Users with the <span class="capability">Password</span> privilege
594 @ are allowed to change their own password. Recommended ON for most
595 @ users but OFF for special users <span class="usertype">developer</span>,
596 @ <span class="usertype">anonymous</span>,
597 @ and <span class="usertype">nobody</span>.
598 @ </p></li>
599 @
600 @ <li><p>
601 @ The <span class="capability">EMail</span> privilege allows the display of
602 @ sensitive information such as the email address of users and contact
603 @ information on tickets. Recommended OFF for
604 @ <span class="usertype">anonymousy</span> and for
605 @ <span class="usertype">nobody</span> but ON for
606 @ <span class="usertype">developer</span>.
607 @ </p></li>
608 @
609 @ <li><p>
610 @ The <span class="capability">Attachment</span> privilege is needed in
611 @ order to add attachments to tickets or wiki. Write privilege on the
612 @ ticket or wiki is also required.
613 @ </p></li>
614 @
615 @ <li><p>
616 @ Login is prohibited if the password is an empty string.
617 @ </p></li>
618 @ </ul>
@@ -592,42 +619,46 @@
619 @
620 @ <h2>Special Logins</h2>
621 @
622 @ <ul>
623 @ <li><p>
624 @ No login is required for user <span class="usertype">nobody</span>. The
625 @ capabilities of the <span class="usertype">nobody</span> user are
626 @ inherited by all users, regardless of whether or not they are logged in.
627 @ To disable universal access to the repository, make sure no user named
628 @ <span class="usertype">nobody</span> exists or that the
629 @ <span class="usertype">nobody</span> user has no capabilities
630 @ enabled. The password for <span class="usertype">nobody</span> is ignore.
631 @ To avoid problems with spiders overloading the server, it is recommended
632 @ that the <span class="capability">h</span> (History) capability be turned
633 @ off for the <span class="usertype">nobody</span> user.
634 @ </p></li>
635 @
636 @ <li><p>
637 @ Login is required for user <span class="usertype">anonymous</span> but the
638 @ password is displayed on the login screen beside the password entry box
639 @ so anybody who can read should be able to login as anonymous.
640 @ On the other hand, spiders and web-crawlers will typically not
641 @ be able to login. Set the capabilities of the
642 @ <span class="usertype">anonymous</span>
643 @ user to things that you want any human to be able to do, but not any
644 @ spider. Every other logged-in user inherits the privileges of
645 @ <span class="usertype">anonymous</span>.
646 @ </p></li>
647 @
648 @ <li><p>
649 @ The <span class="usertype">developer</span> user is intended as a template
650 @ for trusted users with check-in privileges. When adding new trusted users,
651 @ simply select the <span class="capability">developer</span> privilege to
652 @ cause the new user to inherit all privileges of the
653 @ <span class="usertype">developer</span>
654 @ user. Similarly, the <span class="usertype">reader</span> user is a
655 @ template for users who are allowed more access than
656 @ <span class="usertype">anonymous</span>,
657 @ but less than a <span class="usertype">developer</span>.
658 @ </p></li>
659 @ </ul>
660 style_footer();
661 }
662
663
664 /*
@@ -651,13 +682,14 @@
682 db_set(zVar, iQ ? "1" : "0", 0);
683 iVal = iQ;
684 }
685 }
686 if( iVal ){
687 @ <input type="checkbox" name="%s(zQParm)" checked="checked" />
688 @ <b>%s(zLabel)</b>
689 }else{
690 @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b>
691 }
692 }
693
694 /*
695 ** Generate an entry box for an attribute.
@@ -674,11 +706,11 @@
706 if( zQ && strcmp(zQ,zVal)!=0 ){
707 login_verify_csrf_secret();
708 db_set(zVar, zQ, 0);
709 zVal = zQ;
710 }
711 @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" />
712 @ <b>%s(zLabel)</b>
713 }
714
715 /*
716 ** Generate a text box for an attribute.
@@ -698,11 +730,12 @@
730 db_set(zVar, zQ, 0);
731 z = zQ;
732 }
733 if( rows>0 && cols>0 ){
734 @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
735 if (zLabel && *zLabel)
736 @ <span class="textareaLabel">%s(zLabel)</span>
737 }
738 }
739
740
741 /*
@@ -714,57 +747,57 @@
747 login_needed();
748 }
749
750 style_header("Access Control Settings");
751 db_begin_transaction();
752 @ <form action="%s(g.zBaseURL)/setup_access" method="post"><div>
753 login_insert_csrf_secret();
754 @ <hr />
755 onoff_attribute("Require password for local access",
756 "localauth", "localauth", 0);
757 @ <p>When enabled, the password sign-in is required for
758 @ web access coming from 127.0.0.1. When disabled, web access
759 @ from 127.0.0.1 is allows without any login - the user id is selected
760 @ from the ~/.fossil database. Password login is always required
761 @ for incoming web connections on internet addresses other than
762 @ 127.0.0.1.</p>
763
764 @ <hr />
765 onoff_attribute("Allow REMOTE_USER authentication",
766 "remote_user_ok", "remote_user_ok", 0);
767 @ <p>When enabled, if the REMOTE_USER environment variable is set to the
768 @ login name of a valid user and no other login credentials are available,
769 @ then the REMOTE_USER is accepted as an authenticated user.
770 @ </p>
771
772 @ <hr />
773 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
774 @ <p>The number of hours for which a login is valid. This must be a
775 @ positive number. The default is 8760 hours which is approximately equal
776 @ to a year.</p>
777
778 @ <hr />
779 entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
780 "5000000");
781 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
782 @ to this many bytes, uncompressed. If the client requires more data
783 @ than this, then the client will issue multiple HTTP requests.
784 @ Values below 1 million are not recommended. 5 million is a
785 @ reasonable number.</p>
786
787 @ <hr />
788 onoff_attribute("Show javascript button to fill in CAPTCHA",
789 "auto-captcha", "autocaptcha", 0);
790 @ <p>When enabled, a button appears on the login screen for user
791 @ "anonymous" that will automatically fill in the CAPTCHA password.
792 @ This is less secure that forcing the user to do it manually, but is
793 @ probably secure enough and it is certainly more convenient for
794 @ anonymous users.</p>
795
796 @ <hr />
797 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
798 @ </div></form>
799 db_end_transaction(0);
800 style_footer();
801 }
802
803 /*
@@ -776,42 +809,42 @@
809 login_needed();
810 }
811
812 style_header("Timeline Display Preferences");
813 db_begin_transaction();
814 @ <form action="%s(g.zBaseURL)/setup_timeline" method="post"><div>
815 login_insert_csrf_secret();
816
817 @ <hr />
818 onoff_attribute("Allow block-markup in timeline",
819 "timeline-block-markup", "tbm", 0);
820 @ <p>In timeline displays, check-in comments can be displayed with or
821 @ without block markup (paragraphs, tables, etc.)</p>
822
823 @ <hr />
824 onoff_attribute("Use Universal Coordinated Time (UTC)",
825 "timeline-utc", "utc", 1);
826 @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
827 @ Zulu) instead of in local time.</p>
828
829 @ <hr />
830 onoff_attribute("Show version differences by default",
831 "show-version-diffs", "vdiff", 0);
832 @ <p>On the version-information pages linked from the timeline can either
833 @ show complete diffs of all file changes, or can just list the names of
834 @ the files that have changed. Users can get to either page by
835 @ clicking. This setting selects the default.</p>
836
837 @ <hr />
838 entry_attribute("Max timeline comment length", 6,
839 "timeline-max-comment", "tmc", "0");
840 @ <p>The maximum length of a comment to be displayed in a timeline.
841 @ "0" there is no length limit.</p>
842
843 @ <hr />
844 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
845 @ </div></form>
846 db_end_transaction(0);
847 style_footer();
848 }
849
850 /*
@@ -823,11 +856,11 @@
856 login_needed();
857 }
858
859 style_header("WWW Configuration");
860 db_begin_transaction();
861 @ <form action="%s(g.zBaseURL)/setup_config" method="post"><div>
862 login_insert_csrf_secret();
863 @ <hr />
864 entry_attribute("Project Name", 60, "project-name", "pn", "");
865 @ <p>Give your project a name so visitors know what this site is about.
866 @ The project name will also be used as the RSS feed title.</p>
@@ -840,36 +873,37 @@
873 entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
874 @ <p>Enter the pathname of the page to display when the "Home" menu
875 @ option is selected and when no pathname is
876 @ specified in the URL. For example, if you visit the url:</p>
877 @
878 @ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
879 @
880 @ <p>And you have specified an index page of "/home" the above will
881 @ automatically redirect to:</p>
882 @
883 @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
884 @
885 @ <p>The default "/home" page displays a Wiki page with the same name
886 @ as the Project Name specified above. Some sites prefer to redirect
887 @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
888 @ <hr />
889 onoff_attribute("Use HTML as wiki markup language",
890 "wiki-use-html", "wiki-use-html", 0);
891 @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
892 @ but all other wiki formatting will be ignored. This option is helpful
893 @ if you have chosen to use a rich HTML editor for wiki markup such as
894 @ TinyMCE.</p>
895 @ <p><strong>CAUTION:</strong> when
896 @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
897 @ No sanitization is done. This means that it is very possible for malicious
898 @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
899 @ <p>This should <strong>only</strong> be enabled when wiki editing is limited
900 @ to trusted users. It should <strong>not</strong> be used on a publically
901 @ editable wiki.</p>
902 @ <hr />
903 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
904 @ </div></form>
905 db_end_transaction(0);
906 style_footer();
907 }
908
909 /*
@@ -892,27 +926,27 @@
926 if( P("submit")!=0 ){
927 db_end_transaction(0);
928 cgi_redirect("setup_editcss");
929 }
930 style_header("Edit CSS");
931 @ <form action="%s(g.zBaseURL)/setup_editcss" method="post"><div>
932 login_insert_csrf_secret();
933 @ Edit the CSS below:<br />
934 textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
935 @ <br />
936 @ <input type="submit" name="submit" value="Apply Changes" />
937 @ <input type="submit" name="clear" value="Revert To Default" />
938 @ </div></form>
939 @ <p><span class="note">Note:</span> Press your browser Reload button after
940 @ modifying the CSS in order to pull in the modified CSS file.</p>
941 @ <hr />
942 @ The default CSS is shown below for reference. Other examples
943 @ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
944 @ See also the <a href="setup_header">header</a> and
945 @ <a href="setup_footer">footer</a> editing screens.
946 @ <blockquote><pre>
947 cgi_append_default_css();
948 @ </pre></blockquote>
949 style_footer();
950 db_end_transaction(0);
951 }
952
@@ -930,21 +964,21 @@
964 cgi_replace_parameter("header", zDefaultHeader);
965 }else{
966 textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
967 }
968 style_header("Edit Page Header");
969 @ <form action="%s(g.zBaseURL)/setup_header" method="post"><div>
970 login_insert_csrf_secret();
971 @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
972 @ generate the beginning of every page through start of the main
973 @ menu.</p>
974 textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
975 @ <br />
976 @ <input type="submit" name="submit" value="Apply Changes" />
977 @ <input type="submit" name="clear" value="Revert To Default" />
978 @ </div></form>
979 @ <hr />
980 @ The default header is shown below for reference. Other examples
981 @ of headers can be seen on the <a href="setup_skin">skins page</a>.
982 @ See also the <a href="setup_editcss">CSS</a> and
983 @ <a href="setup_footer">footer</a> editing screeens.
984 @ <blockquote><pre>
@@ -968,20 +1002,20 @@
1002 cgi_replace_parameter("footer", zDefaultFooter);
1003 }else{
1004 textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
1005 }
1006 style_header("Edit Page Footer");
1007 @ <form action="%s(g.zBaseURL)/setup_footer" method="post"><div>
1008 login_insert_csrf_secret();
1009 @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
1010 @ generate the end of every page.</p>
1011 textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
1012 @ <br />
1013 @ <input type="submit" name="submit" value="Apply Changes" />
1014 @ <input type="submit" name="clear" value="Revert To Default" />
1015 @ </div></form>
1016 @ <hr />
1017 @ The default footer is shown below for reference. Other examples
1018 @ of footers can be seen on the <a href="setup_skin">skins page</a>.
1019 @ See also the <a href="setup_editcss">CSS</a> and
1020 @ <a href="setup_header">header</a> editing screens.
1021 @ <blockquote><pre>
@@ -1031,31 +1065,31 @@
1065 cgi_redirect("setup_logo");
1066 }
1067 style_header("Edit Project Logo");
1068 @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
1069 @ like this:</p>
1070 @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" /></p></blockquote>
1071 @
1072 @ <p>The logo is accessible to all users at this URL:
1073 @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
1074 @ The logo may or may not appear on each
1075 @ page depending on the <a href="setup_editcss">CSS</a> and
1076 @ <a href="setup_header">header setup</a>.</p>
1077 @
1078 @ <form action="%s(g.zBaseURL)/setup_logo" method="post"
1079 @ enctype="multipart/form-data"><div>
1080 @ <p>To set a new logo image, select a file to use as the logo using
1081 @ the entry box below and then press the "Change Logo" button.</p>
1082 login_insert_csrf_secret();
1083 @ Logo Image file:
1084 @ <input type="file" name="im" size="60" accept="image/*" /><br />
1085 @ <input type="submit" name="set" value="Change Logo" />
1086 @ <input type="submit" name="clr" value="Revert To Default" />
1087 @ </div></form>
1088 @
1089 @ <p><span class="note">Note:</span> Your browser has probably cached the
1090 @ logo image, so you will probably need to press the Reload button on your
1091 @ browser after changing the logo to provoke your browser to reload the new
1092 @ logo image. </p>
1093 style_footer();
1094 db_end_transaction(0);
1095 }
1096
+11 -9
--- src/sha1.c
+++ src/sha1.c
@@ -1,22 +1,24 @@
11
/*
22
** This implementation of SHA1 is adapted from the example implementation
33
** contained in RFC-3174.
44
*/
5
-#include <stdint.h>
6
-#include <sys/types.h>
7
-#include "config.h"
8
-#include "sha1.h"
9
-
105
/*
116
* If you do not have the ISO standard stdint.h header file, then you
127
* must typdef the following:
138
* name meaning
14
- * uint32_t unsigned 32 bit integer
15
- * uint8_t unsigned 8 bit integer (i.e., unsigned char)
16
- *
17
- */
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
+
1820
#define SHA1HashSize 20
1921
#define shaSuccess 0
2022
#define shaInputTooLong 1
2123
#define shaStateError 2
2224
2325
--- src/sha1.c
+++ src/sha1.c
@@ -1,22 +1,24 @@
1 /*
2 ** This implementation of SHA1 is adapted from the example implementation
3 ** contained in RFC-3174.
4 */
5 #include <stdint.h>
6 #include <sys/types.h>
7 #include "config.h"
8 #include "sha1.h"
9
10 /*
11 * If you do not have the ISO standard stdint.h header file, then you
12 * must typdef the following:
13 * name meaning
14 * uint32_t unsigned 32 bit integer
15 * uint8_t unsigned 8 bit integer (i.e., unsigned char)
16 *
17 */
 
 
 
 
 
 
 
18 #define SHA1HashSize 20
19 #define shaSuccess 0
20 #define shaInputTooLong 1
21 #define shaStateError 2
22
23
--- src/sha1.c
+++ src/sha1.c
@@ -1,22 +1,24 @@
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
+29 -26
--- src/shun.c
+++ src/shun.c
@@ -112,63 +112,63 @@
112112
@ or artifacts that by design or accident interfere with the processing
113113
@ of the repository. Do not shun artifacts merely to remove them from
114114
@ sight - set the "hidden" tag on such artifacts instead.</p>
115115
@
116116
@ <blockquote>
117
- @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
117
+ @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
118118
login_insert_csrf_secret();
119
- @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
120
- @ <input type="submit" name="add" value="Shun">
121
- @ </form>
119
+ @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" />
120
+ @ <input type="submit" name="add" value="Shun" />
121
+ @ </div></form>
122122
@ </blockquote>
123123
@
124124
@ <p>Enter the UUID of a previous shunned artifact to cause it to be
125125
@ accepted again in the repository. The artifact content is not
126126
@ restored because the content is unknown. The only change is that
127127
@ the formerly shunned artifact will be accepted on subsequent sync
128128
@ operations.</p>
129129
@
130130
@ <blockquote>
131
- @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
131
+ @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
132132
login_insert_csrf_secret();
133
- @ <input type="text" name="uuid" size="50">
134
- @ <input type="submit" name="sub" value="Accept">
135
- @ </form>
133
+ @ <input type="text" name="uuid" size="50" />
134
+ @ <input type="submit" name="sub" value="Accept" />
135
+ @ </div></form>
136136
@ </blockquote>
137137
@
138138
@ <p>Press the Rebuild button below to rebuild the respository. The
139139
@ content of newly shunned artifacts is not purged until the repository
140140
@ is rebuilt. On larger repositories, the rebuild may take minute or
141141
@ two, so be patient after pressing the button.</p>
142142
@
143143
@ <blockquote>
144
- @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
144
+ @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
145145
login_insert_csrf_secret();
146
- @ <input type="submit" name="rebuild" value="Rebuild">
147
- @ </form>
146
+ @ <input type="submit" name="rebuild" value="Rebuild" />
147
+ @ </div></form>
148148
@ </blockquote>
149149
@
150
- @ <hr><p>Shunned Artifacts:</p>
151
- @ <blockquote>
150
+ @ <hr /><p>Shunned Artifacts:</p>
151
+ @ <blockquote><p>
152152
db_prepare(&q,
153153
"SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
154154
" FROM shun ORDER BY uuid");
155155
while( db_step(&q)==SQLITE_ROW ){
156156
const char *zUuid = db_column_text(&q, 0);
157157
int stillExists = db_column_int(&q, 1);
158158
cnt++;
159159
if( stillExists ){
160
- @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
160
+ @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br />
161161
}else{
162
- @ <b>%s(zUuid)</b><br>
162
+ @ <b>%s(zUuid)</b><br />
163163
}
164164
}
165165
if( cnt==0 ){
166166
@ <i>no artifacts are shunned on this server</i>
167167
}
168168
db_finalize(&q);
169
- @ </blockquote>
169
+ @ </p></blockquote>
170170
style_footer();
171171
}
172172
173173
/*
174174
** Remove from the BLOB table all artifacts that are in the SHUN table.
@@ -229,14 +229,15 @@
229229
@
230230
@ <p>Click on the "rcvid" to show a list of specific artifacts received
231231
@ by a transaction. After identifying illicit artifacts, remove them
232232
@ using the "Shun" feature.</p>
233233
@
234
- @ <table cellpadding=0 cellspacing=0 border=0>
235
- @ <tr><th>rcvid</th><th width=15>
236
- @ <th>Date</th><th width=15><th>User</th>
237
- @ <th width=15><th>IP&nbsp;Address</th></tr>
234
+ @ <table cellpadding="0" cellspacing="0" border="0">
235
+ @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th>
236
+ @ <th style="padding-right: 15px;text-align: left;">Date</th>
237
+ @ <th style="padding-right: 15px;text-align: left;">User</th>
238
+ @ <th style="text-align: left;">IP&nbsp;Address</th></tr>
238239
cnt = 0;
239240
while( db_step(&q)==SQLITE_ROW ){
240241
int rcvid = db_column_int(&q, 0);
241242
const char *zUser = db_column_text(&q, 1);
242243
const char *zDate = db_column_text(&q, 2);
@@ -245,14 +246,14 @@
245246
style_submenu_element("Older", "Older",
246247
"rcvfromlist?ofst=%d", ofst+30);
247248
}else{
248249
cnt++;
249250
@ <tr>
250
- @ <td><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td><td>
251
- @ <td>%s(zDate)</td><td>
252
- @ <td>%h(zUser)</td><td>
253
- @ <td>&nbsp;%s(zIpAddr)&nbsp</td>
251
+ @ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td>
252
+ @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td>
253
+ @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td>
254
+ @ <td style="text-align: left;">%s(zIpAddr)</td>
254255
@ </tr>
255256
}
256257
}
257258
db_finalize(&q);
258259
@ </table>
@@ -277,11 +278,11 @@
277278
"SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
278279
" FROM rcvfrom LEFT JOIN user USING(uid)"
279280
" WHERE rcvid=%d",
280281
rcvid
281282
);
282
- @ <table cellspacing=15 cellpadding=0 border=0>
283
+ @ <table cellspacing="15" cellpadding="0" border="0">
283284
@ <tr><td valign="top" align="right"><b>rcvid:</b></td>
284285
@ <td valign="top">%d(rcvid)</td></tr>
285286
if( db_step(&q)==SQLITE_ROW ){
286287
const char *zUser = db_column_text(&q, 0);
287288
const char *zDate = db_column_text(&q, 1);
@@ -302,10 +303,12 @@
302303
while( db_step(&q)==SQLITE_ROW ){
303304
int rid = db_column_int(&q, 0);
304305
const char *zUuid = db_column_text(&q, 1);
305306
int size = db_column_int(&q, 2);
306307
@ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
307
- @ (rid: %d(rid), size: %d(size))<br>
308
+ @ (rid: %d(rid), size: %d(size))<br />
308309
}
309310
@ </td></tr>
310311
@ </table>
312
+ db_finalize(&q);
313
+ style_footer();
311314
}
312315
--- src/shun.c
+++ src/shun.c
@@ -112,63 +112,63 @@
112 @ or artifacts that by design or accident interfere with the processing
113 @ of the repository. Do not shun artifacts merely to remove them from
114 @ sight - set the "hidden" tag on such artifacts instead.</p>
115 @
116 @ <blockquote>
117 @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
118 login_insert_csrf_secret();
119 @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
120 @ <input type="submit" name="add" value="Shun">
121 @ </form>
122 @ </blockquote>
123 @
124 @ <p>Enter the UUID of a previous shunned artifact to cause it to be
125 @ accepted again in the repository. The artifact content is not
126 @ restored because the content is unknown. The only change is that
127 @ the formerly shunned artifact will be accepted on subsequent sync
128 @ operations.</p>
129 @
130 @ <blockquote>
131 @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
132 login_insert_csrf_secret();
133 @ <input type="text" name="uuid" size="50">
134 @ <input type="submit" name="sub" value="Accept">
135 @ </form>
136 @ </blockquote>
137 @
138 @ <p>Press the Rebuild button below to rebuild the respository. The
139 @ content of newly shunned artifacts is not purged until the repository
140 @ is rebuilt. On larger repositories, the rebuild may take minute or
141 @ two, so be patient after pressing the button.</p>
142 @
143 @ <blockquote>
144 @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
145 login_insert_csrf_secret();
146 @ <input type="submit" name="rebuild" value="Rebuild">
147 @ </form>
148 @ </blockquote>
149 @
150 @ <hr><p>Shunned Artifacts:</p>
151 @ <blockquote>
152 db_prepare(&q,
153 "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
154 " FROM shun ORDER BY uuid");
155 while( db_step(&q)==SQLITE_ROW ){
156 const char *zUuid = db_column_text(&q, 0);
157 int stillExists = db_column_int(&q, 1);
158 cnt++;
159 if( stillExists ){
160 @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
161 }else{
162 @ <b>%s(zUuid)</b><br>
163 }
164 }
165 if( cnt==0 ){
166 @ <i>no artifacts are shunned on this server</i>
167 }
168 db_finalize(&q);
169 @ </blockquote>
170 style_footer();
171 }
172
173 /*
174 ** Remove from the BLOB table all artifacts that are in the SHUN table.
@@ -229,14 +229,15 @@
229 @
230 @ <p>Click on the "rcvid" to show a list of specific artifacts received
231 @ by a transaction. After identifying illicit artifacts, remove them
232 @ using the "Shun" feature.</p>
233 @
234 @ <table cellpadding=0 cellspacing=0 border=0>
235 @ <tr><th>rcvid</th><th width=15>
236 @ <th>Date</th><th width=15><th>User</th>
237 @ <th width=15><th>IP&nbsp;Address</th></tr>
 
238 cnt = 0;
239 while( db_step(&q)==SQLITE_ROW ){
240 int rcvid = db_column_int(&q, 0);
241 const char *zUser = db_column_text(&q, 1);
242 const char *zDate = db_column_text(&q, 2);
@@ -245,14 +246,14 @@
245 style_submenu_element("Older", "Older",
246 "rcvfromlist?ofst=%d", ofst+30);
247 }else{
248 cnt++;
249 @ <tr>
250 @ <td><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td><td>
251 @ <td>%s(zDate)</td><td>
252 @ <td>%h(zUser)</td><td>
253 @ <td>&nbsp;%s(zIpAddr)&nbsp</td>
254 @ </tr>
255 }
256 }
257 db_finalize(&q);
258 @ </table>
@@ -277,11 +278,11 @@
277 "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
278 " FROM rcvfrom LEFT JOIN user USING(uid)"
279 " WHERE rcvid=%d",
280 rcvid
281 );
282 @ <table cellspacing=15 cellpadding=0 border=0>
283 @ <tr><td valign="top" align="right"><b>rcvid:</b></td>
284 @ <td valign="top">%d(rcvid)</td></tr>
285 if( db_step(&q)==SQLITE_ROW ){
286 const char *zUser = db_column_text(&q, 0);
287 const char *zDate = db_column_text(&q, 1);
@@ -302,10 +303,12 @@
302 while( db_step(&q)==SQLITE_ROW ){
303 int rid = db_column_int(&q, 0);
304 const char *zUuid = db_column_text(&q, 1);
305 int size = db_column_int(&q, 2);
306 @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
307 @ (rid: %d(rid), size: %d(size))<br>
308 }
309 @ </td></tr>
310 @ </table>
 
 
311 }
312
--- src/shun.c
+++ src/shun.c
@@ -112,63 +112,63 @@
112 @ or artifacts that by design or accident interfere with the processing
113 @ of the repository. Do not shun artifacts merely to remove them from
114 @ sight - set the "hidden" tag on such artifacts instead.</p>
115 @
116 @ <blockquote>
117 @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
118 login_insert_csrf_secret();
119 @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" />
120 @ <input type="submit" name="add" value="Shun" />
121 @ </div></form>
122 @ </blockquote>
123 @
124 @ <p>Enter the UUID of a previous shunned artifact to cause it to be
125 @ accepted again in the repository. The artifact content is not
126 @ restored because the content is unknown. The only change is that
127 @ the formerly shunned artifact will be accepted on subsequent sync
128 @ operations.</p>
129 @
130 @ <blockquote>
131 @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
132 login_insert_csrf_secret();
133 @ <input type="text" name="uuid" size="50" />
134 @ <input type="submit" name="sub" value="Accept" />
135 @ </div></form>
136 @ </blockquote>
137 @
138 @ <p>Press the Rebuild button below to rebuild the respository. The
139 @ content of newly shunned artifacts is not purged until the repository
140 @ is rebuilt. On larger repositories, the rebuild may take minute or
141 @ two, so be patient after pressing the button.</p>
142 @
143 @ <blockquote>
144 @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
145 login_insert_csrf_secret();
146 @ <input type="submit" name="rebuild" value="Rebuild" />
147 @ </div></form>
148 @ </blockquote>
149 @
150 @ <hr /><p>Shunned Artifacts:</p>
151 @ <blockquote><p>
152 db_prepare(&q,
153 "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
154 " FROM shun ORDER BY uuid");
155 while( db_step(&q)==SQLITE_ROW ){
156 const char *zUuid = db_column_text(&q, 0);
157 int stillExists = db_column_int(&q, 1);
158 cnt++;
159 if( stillExists ){
160 @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br />
161 }else{
162 @ <b>%s(zUuid)</b><br />
163 }
164 }
165 if( cnt==0 ){
166 @ <i>no artifacts are shunned on this server</i>
167 }
168 db_finalize(&q);
169 @ </p></blockquote>
170 style_footer();
171 }
172
173 /*
174 ** Remove from the BLOB table all artifacts that are in the SHUN table.
@@ -229,14 +229,15 @@
229 @
230 @ <p>Click on the "rcvid" to show a list of specific artifacts received
231 @ by a transaction. After identifying illicit artifacts, remove them
232 @ using the "Shun" feature.</p>
233 @
234 @ <table cellpadding="0" cellspacing="0" border="0">
235 @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th>
236 @ <th style="padding-right: 15px;text-align: left;">Date</th>
237 @ <th style="padding-right: 15px;text-align: left;">User</th>
238 @ <th style="text-align: left;">IP&nbsp;Address</th></tr>
239 cnt = 0;
240 while( db_step(&q)==SQLITE_ROW ){
241 int rcvid = db_column_int(&q, 0);
242 const char *zUser = db_column_text(&q, 1);
243 const char *zDate = db_column_text(&q, 2);
@@ -245,14 +246,14 @@
246 style_submenu_element("Older", "Older",
247 "rcvfromlist?ofst=%d", ofst+30);
248 }else{
249 cnt++;
250 @ <tr>
251 @ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td>
252 @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td>
253 @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td>
254 @ <td style="text-align: left;">%s(zIpAddr)</td>
255 @ </tr>
256 }
257 }
258 db_finalize(&q);
259 @ </table>
@@ -277,11 +278,11 @@
278 "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
279 " FROM rcvfrom LEFT JOIN user USING(uid)"
280 " WHERE rcvid=%d",
281 rcvid
282 );
283 @ <table cellspacing="15" cellpadding="0" border="0">
284 @ <tr><td valign="top" align="right"><b>rcvid:</b></td>
285 @ <td valign="top">%d(rcvid)</td></tr>
286 if( db_step(&q)==SQLITE_ROW ){
287 const char *zUser = db_column_text(&q, 0);
288 const char *zDate = db_column_text(&q, 1);
@@ -302,10 +303,12 @@
303 while( db_step(&q)==SQLITE_ROW ){
304 int rid = db_column_int(&q, 0);
305 const char *zUuid = db_column_text(&q, 1);
306 int size = db_column_int(&q, 2);
307 @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
308 @ (rid: %d(rid), size: %d(size))<br />
309 }
310 @ </td></tr>
311 @ </table>
312 db_finalize(&q);
313 style_footer();
314 }
315
+57 -57
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
2424
/* @-comment: // */
2525
/*
2626
** A black-and-white theme with the project title in a bar across the top
2727
** and no logo image.
2828
*/
29
-static const char zBuiltinSkin1[] =
29
+static const char zBuiltinSkin1[] =
3030
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
3131
@ body {
3232
@ margin: 0ex 1ex;
3333
@ padding: 0px;
3434
@ background-color: white;
3535
@ font-family: sans-serif;
3636
@ }
37
-@
37
+@
3838
@ /* The project logo in the upper left-hand corner of each page */
3939
@ div.logo {
4040
@ display: table-row;
4141
@ text-align: center;
4242
@ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
4444
@ font-weight: bold;
4545
@ background-color: #707070;
4646
@ color: #ffffff;
4747
@ min-width: 200px;
4848
@ }
49
-@
49
+@
5050
@ /* The page title centered at the top of each page */
5151
@ div.title {
5252
@ display: table-cell;
5353
@ font-size: 1.5em;
5454
@ font-weight: bold;
@@ -56,11 +56,11 @@
5656
@ padding: 0 0 0 10px;
5757
@ color: #404040;
5858
@ vertical-align: bottom;
5959
@ width: 100%;
6060
@ }
61
-@
61
+@
6262
@ /* The login status message in the top right-hand corner */
6363
@ div.status {
6464
@ display: table-cell;
6565
@ text-align: right;
6666
@ vertical-align: bottom;
@@ -67,17 +67,17 @@
6767
@ color: #404040;
6868
@ font-size: 0.8em;
6969
@ font-weight: bold;
7070
@ min-width: 200px;
7171
@ }
72
-@
72
+@
7373
@ /* The header across the top of the page */
7474
@ div.header {
7575
@ display: table;
7676
@ width: 100%;
7777
@ }
78
-@
78
+@
7979
@ /* The main menu bar that appears at the top of the page beneath
8080
@ ** the header */
8181
@ div.mainmenu {
8282
@ padding: 5px 10px 5px 10px;
8383
@ font-size: 0.9em;
@@ -85,11 +85,11 @@
8585
@ text-align: center;
8686
@ letter-spacing: 1px;
8787
@ background-color: #404040;
8888
@ color: white;
8989
@ }
90
-@
90
+@
9191
@ /* The submenu bar that *sometimes* appears below the main menu */
9292
@ div.submenu {
9393
@ padding: 3px 10px 3px 0px;
9494
@ font-size: 0.9em;
9595
@ text-align: center;
@@ -103,21 +103,21 @@
103103
@ }
104104
@ div.mainmenu a:hover, div.submenu a:hover {
105105
@ color: #404040;
106106
@ background-color: white;
107107
@ }
108
-@
108
+@
109109
@ /* All page content from the bottom of the menu or submenu down to
110110
@ ** the footer */
111111
@ div.content {
112112
@ padding: 0ex 0ex 0ex 0ex;
113113
@ }
114114
@ /* Hyperlink colors */
115115
@ div.content a { color: #604000; }
116116
@ div.content a:link { color: #604000;}
117117
@ div.content a:visited { color: #600000; }
118
-@
118
+@
119119
@ /* Some pages have section dividers */
120120
@ div.section {
121121
@ margin-bottom: 0px;
122122
@ margin-top: 1em;
123123
@ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124124
@ font-size: 1.2em;
125125
@ font-weight: bold;
126126
@ background-color: #404040;
127127
@ color: white;
128128
@ }
129
-@
129
+@
130130
@ /* The "Date" that occurs on the left hand side of timelines */
131131
@ div.divider {
132132
@ background: #a0a0a0;
133133
@ border: 2px #505050 solid;
134134
@ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135135
@ padding: .25em;
136136
@ margin: .2em 0 .2em 0;
137137
@ float: left;
138138
@ clear: left;
139139
@ }
140
-@
140
+@
141141
@ /* The footer at the very bottom of the page */
142142
@ div.footer {
143143
@ font-size: 0.8em;
144144
@ margin-top: 12px;
145145
@ padding: 5px 10px 5px 10px;
146146
@ text-align: right;
147147
@ background-color: #404040;
148148
@ color: white;
149149
@ }
150
-@
150
+@
151151
@ /* The label/value pairs on (for example) the vinfo page */
152152
@ table.label-value th {
153153
@ vertical-align: top;
154154
@ text-align: right;
155155
@ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165165
@ <body>
166166
@ <div class="header">
167167
@ <div class="logo">
168168
@ <img src="$baseurl/logo" alt="logo">
169169
@ </div>
170
-@ <div class="title"><small>$<project_name></small><br>$<title></div>
170
+@ <div class="title"><small>$<project_name></small><br />$<title></div>
171171
@ <div class="status"><nobr><th1>
172172
@ if {[info exists login]} {
173173
@ puts "Logged in as $login"
174174
@ } else {
175175
@ puts "Not logged in"
@@ -206,39 +206,39 @@
206206
@ html "<a href=''$baseurl/login''>Login</a> "
207207
@ }
208208
@ </th1></div>
209209
@ ');
210210
@ REPLACE INTO config VALUES('footer','<div class="footer">
211
-@ Fossil version $manifest_version $manifest_date
211
+@ Fossil version $manifest_version $manifest_date
212212
@ </div>
213213
@ </body></html>
214214
@ ');
215215
;
216216
217217
/*
218218
** A tan theme with the project title above the user identification
219219
** and no logo image.
220220
*/
221
-static const char zBuiltinSkin2[] =
221
+static const char zBuiltinSkin2[] =
222222
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223223
@ body {
224224
@ margin: 0ex 0ex;
225225
@ padding: 0px;
226226
@ background-color: #fef3bc;
227227
@ font-family: sans-serif;
228228
@ }
229
-@
229
+@
230230
@ /* The project logo in the upper left-hand corner of each page */
231231
@ div.logo {
232232
@ display: inline;
233233
@ text-align: center;
234234
@ vertical-align: bottom;
235235
@ font-weight: bold;
236236
@ font-size: 2.5em;
237237
@ color: #a09048;
238238
@ }
239
-@
239
+@
240240
@ /* The page title centered at the top of each page */
241241
@ div.title {
242242
@ display: table-cell;
243243
@ font-size: 2em;
244244
@ font-weight: bold;
@@ -246,11 +246,11 @@
246246
@ padding: 0 0 0 5px;
247247
@ color: #a09048;
248248
@ vertical-align: bottom;
249249
@ width: 100%;
250250
@ }
251
-@
251
+@
252252
@ /* The login status message in the top right-hand corner */
253253
@ div.status {
254254
@ display: table-cell;
255255
@ text-align: right;
256256
@ vertical-align: bottom;
@@ -257,17 +257,17 @@
257257
@ color: #a09048;
258258
@ padding: 5px 5px 0 0;
259259
@ font-size: 0.8em;
260260
@ font-weight: bold;
261261
@ }
262
-@
262
+@
263263
@ /* The header across the top of the page */
264264
@ div.header {
265265
@ display: table;
266266
@ width: 100%;
267267
@ }
268
-@
268
+@
269269
@ /* The main menu bar that appears at the top of the page beneath
270270
@ ** the header */
271271
@ div.mainmenu {
272272
@ padding: 5px 10px 5px 10px;
273273
@ font-size: 0.9em;
@@ -275,11 +275,11 @@
275275
@ text-align: center;
276276
@ letter-spacing: 1px;
277277
@ background-color: #a09048;
278278
@ color: black;
279279
@ }
280
-@
280
+@
281281
@ /* The submenu bar that *sometimes* appears below the main menu */
282282
@ div.submenu {
283283
@ padding: 3px 10px 3px 0px;
284284
@ font-size: 0.9em;
285285
@ text-align: center;
@@ -293,21 +293,21 @@
293293
@ }
294294
@ div.mainmenu a:hover, div.submenu a:hover {
295295
@ color: #a09048;
296296
@ background-color: white;
297297
@ }
298
-@
298
+@
299299
@ /* All page content from the bottom of the menu or submenu down to
300300
@ ** the footer */
301301
@ div.content {
302302
@ padding: 1ex 5px;
303303
@ }
304304
@ div.content a { color: #706532; }
305305
@ div.content a:link { color: #706532; }
306306
@ div.content a:visited { color: #704032; }
307307
@ div.content a:hover { background-color: white; color: #706532; }
308
-@
308
+@
309309
@ /* Some pages have section dividers */
310310
@ div.section {
311311
@ margin-bottom: 0px;
312312
@ margin-top: 1em;
313313
@ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314314
@ font-size: 1.2em;
315315
@ font-weight: bold;
316316
@ background-color: #a09048;
317317
@ color: white;
318318
@ }
319
-@
319
+@
320320
@ /* The "Date" that occurs on the left hand side of timelines */
321321
@ div.divider {
322322
@ background: #e1d498;
323323
@ border: 2px #a09048 solid;
324324
@ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325325
@ padding: .25em;
326326
@ margin: .2em 0 .2em 0;
327327
@ float: left;
328328
@ clear: left;
329329
@ }
330
-@
330
+@
331331
@ /* The footer at the very bottom of the page */
332332
@ div.footer {
333333
@ font-size: 0.8em;
334334
@ margin-top: 12px;
335335
@ padding: 5px 10px 5px 10px;
336336
@ text-align: right;
337337
@ background-color: #a09048;
338338
@ color: white;
339339
@ }
340
-@
340
+@
341341
@ /* Hyperlink colors */
342342
@ div.footer a { color: white; }
343343
@ div.footer a:link { color: white; }
344344
@ div.footer a:visited { color: white; }
345345
@ div.footer a:hover { background-color: white; color: #558195; }
346
-@
346
+@
347347
@ /* <verbatim> blocks */
348348
@ pre.verbatim {
349349
@ background-color: #f5f5f5;
350350
@ padding: 0.5em;
351351
@ }
352
-@
352
+@
353353
@ /* The label/value pairs on (for example) the ci page */
354354
@ table.label-value th {
355355
@ vertical-align: top;
356356
@ text-align: right;
357357
@ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418418
419419
/*
420420
** Black letters on a white or cream background with the main menu
421421
** stuck on the left-hand side.
422422
*/
423
-static const char zBuiltinSkin3[] =
423
+static const char zBuiltinSkin3[] =
424424
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425425
@ body {
426426
@ margin:0px 0px 0px 0px;
427427
@ padding:0px;
428428
@ font-family:verdana, arial, helvetica, "sans serif";
429429
@ color:#333;
430430
@ background-color:white;
431431
@ }
432
-@
432
+@
433433
@ /* consistent colours */
434434
@ h2 {
435435
@ color: #333;
436436
@ }
437437
@ h3 {
438438
@ color: #333;
439439
@ }
440
-@
440
+@
441441
@ /* The project logo in the upper left-hand corner of each page */
442442
@ div.logo {
443443
@ display: table-cell;
444444
@ text-align: left;
445445
@ vertical-align: bottom;
446446
@ font-weight: bold;
447447
@ color: #333;
448448
@ }
449
-@
449
+@
450450
@ /* The page title centered at the top of each page */
451451
@ div.title {
452452
@ display: table-cell;
453453
@ font-size: 2em;
454454
@ font-weight: bold;
@@ -455,11 +455,11 @@
455455
@ text-align: center;
456456
@ color: #333;
457457
@ vertical-align: bottom;
458458
@ width: 100%;
459459
@ }
460
-@
460
+@
461461
@ /* The login status message in the top right-hand corner */
462462
@ div.status {
463463
@ display: table-cell;
464464
@ padding-right: 10px;
465465
@ text-align: right;
@@ -467,21 +467,21 @@
467467
@ padding-bottom: 5px;
468468
@ color: #333;
469469
@ font-size: 0.8em;
470470
@ font-weight: bold;
471471
@ }
472
-@
472
+@
473473
@ /* The header across the top of the page */
474474
@ div.header {
475475
@ margin:10px 0px 10px 0px;
476476
@ padding:1px 0px 0px 20px;
477477
@ border-style:solid;
478478
@ border-color:black;
479479
@ border-width:1px 0px;
480480
@ background-color:#eee;
481481
@ }
482
-@
482
+@
483483
@ /* The main menu bar that appears at the top left of the page beneath
484484
@ ** the header. Width must be co-ordinated with the container below */
485485
@ div.mainmenu {
486486
@ float: left;
487487
@ margin-left: 10px;
@@ -491,11 +491,11 @@
491491
@ padding:5px;
492492
@ background-color:#eee;
493493
@ border:1px solid #999;
494494
@ width:8em;
495495
@ }
496
-@
496
+@
497497
@ /* Main menu is now a list */
498498
@ div.mainmenu ul {
499499
@ padding: 0;
500500
@ list-style:none;
501501
@ }
@@ -506,17 +506,17 @@
506506
@ }
507507
@ div.mainmenu a:hover {
508508
@ color: #eee;
509509
@ background-color: #333;
510510
@ }
511
-@
511
+@
512512
@ /* Container for the sub-menu and content so they don''t spread
513513
@ ** out underneath the main menu */
514514
@ #container {
515515
@ padding-left: 9em;
516516
@ }
517
-@
517
+@
518518
@ /* The submenu bar that *sometimes* appears below the main menu */
519519
@ div.submenu {
520520
@ padding: 3px 10px 3px 10px;
521521
@ font-size: 0.9em;
522522
@ text-align: center;
@@ -532,18 +532,18 @@
532532
@ }
533533
@ div.submenu a:hover {
534534
@ color: #eee;
535535
@ background-color: #333;
536536
@ }
537
-@
537
+@
538538
@ /* All page content from the bottom of the menu or submenu down to
539539
@ ** the footer */
540540
@ div.content {
541541
@ float right;
542542
@ padding: 2ex 1ex 0ex 2ex;
543543
@ }
544
-@
544
+@
545545
@ /* Some pages have section dividers */
546546
@ div.section {
547547
@ margin-bottom: 0px;
548548
@ margin-top: 1em;
549549
@ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553553
@ border-color:#999;
554554
@ border-width:1px 0px;
555555
@ background-color: #eee;
556556
@ color: #333;
557557
@ }
558
-@
558
+@
559559
@ /* The "Date" that occurs on the left hand side of timelines */
560560
@ div.divider {
561561
@ background: #eee;
562562
@ border: 2px #999 solid;
563563
@ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565565
@ margin: .2em 0 .2em 0;
566566
@ float: left;
567567
@ clear: left;
568568
@ color: #333
569569
@ }
570
-@
570
+@
571571
@ /* The footer at the very bottom of the page */
572572
@ div.footer {
573573
@ font-size: 0.8em;
574574
@ margin-top: 12px;
575575
@ padding: 5px 10px 5px 10px;
576576
@ text-align: right;
577577
@ background-color: #eee;
578578
@ color: #555;
579579
@ }
580
-@
580
+@
581581
@ /* <verbatim> blocks */
582582
@ pre.verbatim {
583583
@ background-color: #f5f5f5;
584584
@ padding: 0.5em;
585585
@ }
586
-@
586
+@
587587
@ /* The label/value pairs on (for example) the ci page */
588588
@ table.label-value th {
589589
@ vertical-align: top;
590590
@ text-align: right;
591591
@ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600600
@ </head>
601601
@ <body>
602602
@ <div class="header">
603603
@ <div class="logo">
604604
@ <!-- <img src="$baseurl/logo" alt="logo"> -->
605
-@ <br><nobr>$<project_name></nobr>
605
+@ <br /><nobr>$<project_name></nobr>
606606
@ </div>
607607
@ <div class="title">$<title></div>
608608
@ <div class="status"><nobr><th1>
609609
@ if {[info exists login]} {
610610
@ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732732
db_begin_transaction();
733733
734734
/* Process requests to delete a user-defined skin */
735735
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736736
style_header("Confirm Custom Skin Delete");
737
- @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
737
+ @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
738738
@ <p>Deletion of a custom skin is a permanent action that cannot
739739
@ be undone. Please confirm that this is what you want to do:</p>
740
- @ <input type="hidden" name="sn" value="%h(P("sn"))">
741
- @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
742
- @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
740
+ @ <input type="hidden" name="sn" value="%h(P("sn"))" />
741
+ @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
742
+ @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
743743
login_insert_csrf_secret();
744
- @ </form>
744
+ @ </div></form>
745745
style_footer();
746746
return;
747747
}
748748
if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749749
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800800
}
801801
}
802802
803803
style_header("Skins");
804804
@ <p>A "skin" is a combination of
805
- @ <a href="setup_editcss">CSS</a>,
805
+ @ <a href="setup_editcss">CSS</a>,
806806
@ <a href="setup_header">Header</a>,
807807
@ <a href="setup_footer">Footer</a>, and
808808
@ <a href="setup_logo">Logo</a> that determines the look and feel
809809
@ of the web interface.</p>
810810
@
@@ -813,15 +813,15 @@
813813
for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814814
z = aBuiltinSkin[i].zName;
815815
if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816816
@ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817817
}else{
818
- @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
819
- @ %h(z).&nbsp;&nbsp;
820
- @ <input type="hidden" name="sn" value="%h(z)">
821
- @ <input type="submit" name="load" value="Use This Skin">
822
- @ </form></li>
818
+ @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
819
+ @ %h(z).&nbsp;&nbsp;
820
+ @ <input type="hidden" name="sn" value="%h(z)" />
821
+ @ <input type="submit" name="load" value="Use This Skin" />
822
+ @ </div></form></li>
823823
}
824824
}
825825
db_prepare(&q,
826826
"SELECT substr(name, 6), value FROM config"
827827
" WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832832
const char *zV = db_column_text(&q, 1);
833833
if( strcmp(zV, zCurrent)==0 ){
834834
@ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835835
}else{
836836
@ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837
- @ %h(zN).&nbsp;&nbsp;
837
+ @ %h(zN).&nbsp;&nbsp;
838838
@ <input type="hidden" name="sn" value="%h(zN)">
839839
@ <input type="submit" name="load" value="Use This Skin">
840840
@ <input type="submit" name="del1" value="Delete This Skin">
841841
@ </form></li>
842842
}
843843
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
24 /* @-comment: // */
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
31 @ body {
32 @ margin: 0ex 1ex;
33 @ padding: 0px;
34 @ background-color: white;
35 @ font-family: sans-serif;
36 @ }
37 @
38 @ /* The project logo in the upper left-hand corner of each page */
39 @ div.logo {
40 @ display: table-row;
41 @ text-align: center;
42 @ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
44 @ font-weight: bold;
45 @ background-color: #707070;
46 @ color: #ffffff;
47 @ min-width: 200px;
48 @ }
49 @
50 @ /* The page title centered at the top of each page */
51 @ div.title {
52 @ display: table-cell;
53 @ font-size: 1.5em;
54 @ font-weight: bold;
@@ -56,11 +56,11 @@
56 @ padding: 0 0 0 10px;
57 @ color: #404040;
58 @ vertical-align: bottom;
59 @ width: 100%;
60 @ }
61 @
62 @ /* The login status message in the top right-hand corner */
63 @ div.status {
64 @ display: table-cell;
65 @ text-align: right;
66 @ vertical-align: bottom;
@@ -67,17 +67,17 @@
67 @ color: #404040;
68 @ font-size: 0.8em;
69 @ font-weight: bold;
70 @ min-width: 200px;
71 @ }
72 @
73 @ /* The header across the top of the page */
74 @ div.header {
75 @ display: table;
76 @ width: 100%;
77 @ }
78 @
79 @ /* The main menu bar that appears at the top of the page beneath
80 @ ** the header */
81 @ div.mainmenu {
82 @ padding: 5px 10px 5px 10px;
83 @ font-size: 0.9em;
@@ -85,11 +85,11 @@
85 @ text-align: center;
86 @ letter-spacing: 1px;
87 @ background-color: #404040;
88 @ color: white;
89 @ }
90 @
91 @ /* The submenu bar that *sometimes* appears below the main menu */
92 @ div.submenu {
93 @ padding: 3px 10px 3px 0px;
94 @ font-size: 0.9em;
95 @ text-align: center;
@@ -103,21 +103,21 @@
103 @ }
104 @ div.mainmenu a:hover, div.submenu a:hover {
105 @ color: #404040;
106 @ background-color: white;
107 @ }
108 @
109 @ /* All page content from the bottom of the menu or submenu down to
110 @ ** the footer */
111 @ div.content {
112 @ padding: 0ex 0ex 0ex 0ex;
113 @ }
114 @ /* Hyperlink colors */
115 @ div.content a { color: #604000; }
116 @ div.content a:link { color: #604000;}
117 @ div.content a:visited { color: #600000; }
118 @
119 @ /* Some pages have section dividers */
120 @ div.section {
121 @ margin-bottom: 0px;
122 @ margin-top: 1em;
123 @ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124 @ font-size: 1.2em;
125 @ font-weight: bold;
126 @ background-color: #404040;
127 @ color: white;
128 @ }
129 @
130 @ /* The "Date" that occurs on the left hand side of timelines */
131 @ div.divider {
132 @ background: #a0a0a0;
133 @ border: 2px #505050 solid;
134 @ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135 @ padding: .25em;
136 @ margin: .2em 0 .2em 0;
137 @ float: left;
138 @ clear: left;
139 @ }
140 @
141 @ /* The footer at the very bottom of the page */
142 @ div.footer {
143 @ font-size: 0.8em;
144 @ margin-top: 12px;
145 @ padding: 5px 10px 5px 10px;
146 @ text-align: right;
147 @ background-color: #404040;
148 @ color: white;
149 @ }
150 @
151 @ /* The label/value pairs on (for example) the vinfo page */
152 @ table.label-value th {
153 @ vertical-align: top;
154 @ text-align: right;
155 @ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165 @ <body>
166 @ <div class="header">
167 @ <div class="logo">
168 @ <img src="$baseurl/logo" alt="logo">
169 @ </div>
170 @ <div class="title"><small>$<project_name></small><br>$<title></div>
171 @ <div class="status"><nobr><th1>
172 @ if {[info exists login]} {
173 @ puts "Logged in as $login"
174 @ } else {
175 @ puts "Not logged in"
@@ -206,39 +206,39 @@
206 @ html "<a href=''$baseurl/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config VALUES('footer','<div class="footer">
211 @ Fossil version $manifest_version $manifest_date
212 @ </div>
213 @ </body></html>
214 @ ');
215 ;
216
217 /*
218 ** A tan theme with the project title above the user identification
219 ** and no logo image.
220 */
221 static const char zBuiltinSkin2[] =
222 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223 @ body {
224 @ margin: 0ex 0ex;
225 @ padding: 0px;
226 @ background-color: #fef3bc;
227 @ font-family: sans-serif;
228 @ }
229 @
230 @ /* The project logo in the upper left-hand corner of each page */
231 @ div.logo {
232 @ display: inline;
233 @ text-align: center;
234 @ vertical-align: bottom;
235 @ font-weight: bold;
236 @ font-size: 2.5em;
237 @ color: #a09048;
238 @ }
239 @
240 @ /* The page title centered at the top of each page */
241 @ div.title {
242 @ display: table-cell;
243 @ font-size: 2em;
244 @ font-weight: bold;
@@ -246,11 +246,11 @@
246 @ padding: 0 0 0 5px;
247 @ color: #a09048;
248 @ vertical-align: bottom;
249 @ width: 100%;
250 @ }
251 @
252 @ /* The login status message in the top right-hand corner */
253 @ div.status {
254 @ display: table-cell;
255 @ text-align: right;
256 @ vertical-align: bottom;
@@ -257,17 +257,17 @@
257 @ color: #a09048;
258 @ padding: 5px 5px 0 0;
259 @ font-size: 0.8em;
260 @ font-weight: bold;
261 @ }
262 @
263 @ /* The header across the top of the page */
264 @ div.header {
265 @ display: table;
266 @ width: 100%;
267 @ }
268 @
269 @ /* The main menu bar that appears at the top of the page beneath
270 @ ** the header */
271 @ div.mainmenu {
272 @ padding: 5px 10px 5px 10px;
273 @ font-size: 0.9em;
@@ -275,11 +275,11 @@
275 @ text-align: center;
276 @ letter-spacing: 1px;
277 @ background-color: #a09048;
278 @ color: black;
279 @ }
280 @
281 @ /* The submenu bar that *sometimes* appears below the main menu */
282 @ div.submenu {
283 @ padding: 3px 10px 3px 0px;
284 @ font-size: 0.9em;
285 @ text-align: center;
@@ -293,21 +293,21 @@
293 @ }
294 @ div.mainmenu a:hover, div.submenu a:hover {
295 @ color: #a09048;
296 @ background-color: white;
297 @ }
298 @
299 @ /* All page content from the bottom of the menu or submenu down to
300 @ ** the footer */
301 @ div.content {
302 @ padding: 1ex 5px;
303 @ }
304 @ div.content a { color: #706532; }
305 @ div.content a:link { color: #706532; }
306 @ div.content a:visited { color: #704032; }
307 @ div.content a:hover { background-color: white; color: #706532; }
308 @
309 @ /* Some pages have section dividers */
310 @ div.section {
311 @ margin-bottom: 0px;
312 @ margin-top: 1em;
313 @ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314 @ font-size: 1.2em;
315 @ font-weight: bold;
316 @ background-color: #a09048;
317 @ color: white;
318 @ }
319 @
320 @ /* The "Date" that occurs on the left hand side of timelines */
321 @ div.divider {
322 @ background: #e1d498;
323 @ border: 2px #a09048 solid;
324 @ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325 @ padding: .25em;
326 @ margin: .2em 0 .2em 0;
327 @ float: left;
328 @ clear: left;
329 @ }
330 @
331 @ /* The footer at the very bottom of the page */
332 @ div.footer {
333 @ font-size: 0.8em;
334 @ margin-top: 12px;
335 @ padding: 5px 10px 5px 10px;
336 @ text-align: right;
337 @ background-color: #a09048;
338 @ color: white;
339 @ }
340 @
341 @ /* Hyperlink colors */
342 @ div.footer a { color: white; }
343 @ div.footer a:link { color: white; }
344 @ div.footer a:visited { color: white; }
345 @ div.footer a:hover { background-color: white; color: #558195; }
346 @
347 @ /* <verbatim> blocks */
348 @ pre.verbatim {
349 @ background-color: #f5f5f5;
350 @ padding: 0.5em;
351 @ }
352 @
353 @ /* The label/value pairs on (for example) the ci page */
354 @ table.label-value th {
355 @ vertical-align: top;
356 @ text-align: right;
357 @ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418
419 /*
420 ** Black letters on a white or cream background with the main menu
421 ** stuck on the left-hand side.
422 */
423 static const char zBuiltinSkin3[] =
424 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425 @ body {
426 @ margin:0px 0px 0px 0px;
427 @ padding:0px;
428 @ font-family:verdana, arial, helvetica, "sans serif";
429 @ color:#333;
430 @ background-color:white;
431 @ }
432 @
433 @ /* consistent colours */
434 @ h2 {
435 @ color: #333;
436 @ }
437 @ h3 {
438 @ color: #333;
439 @ }
440 @
441 @ /* The project logo in the upper left-hand corner of each page */
442 @ div.logo {
443 @ display: table-cell;
444 @ text-align: left;
445 @ vertical-align: bottom;
446 @ font-weight: bold;
447 @ color: #333;
448 @ }
449 @
450 @ /* The page title centered at the top of each page */
451 @ div.title {
452 @ display: table-cell;
453 @ font-size: 2em;
454 @ font-weight: bold;
@@ -455,11 +455,11 @@
455 @ text-align: center;
456 @ color: #333;
457 @ vertical-align: bottom;
458 @ width: 100%;
459 @ }
460 @
461 @ /* The login status message in the top right-hand corner */
462 @ div.status {
463 @ display: table-cell;
464 @ padding-right: 10px;
465 @ text-align: right;
@@ -467,21 +467,21 @@
467 @ padding-bottom: 5px;
468 @ color: #333;
469 @ font-size: 0.8em;
470 @ font-weight: bold;
471 @ }
472 @
473 @ /* The header across the top of the page */
474 @ div.header {
475 @ margin:10px 0px 10px 0px;
476 @ padding:1px 0px 0px 20px;
477 @ border-style:solid;
478 @ border-color:black;
479 @ border-width:1px 0px;
480 @ background-color:#eee;
481 @ }
482 @
483 @ /* The main menu bar that appears at the top left of the page beneath
484 @ ** the header. Width must be co-ordinated with the container below */
485 @ div.mainmenu {
486 @ float: left;
487 @ margin-left: 10px;
@@ -491,11 +491,11 @@
491 @ padding:5px;
492 @ background-color:#eee;
493 @ border:1px solid #999;
494 @ width:8em;
495 @ }
496 @
497 @ /* Main menu is now a list */
498 @ div.mainmenu ul {
499 @ padding: 0;
500 @ list-style:none;
501 @ }
@@ -506,17 +506,17 @@
506 @ }
507 @ div.mainmenu a:hover {
508 @ color: #eee;
509 @ background-color: #333;
510 @ }
511 @
512 @ /* Container for the sub-menu and content so they don''t spread
513 @ ** out underneath the main menu */
514 @ #container {
515 @ padding-left: 9em;
516 @ }
517 @
518 @ /* The submenu bar that *sometimes* appears below the main menu */
519 @ div.submenu {
520 @ padding: 3px 10px 3px 10px;
521 @ font-size: 0.9em;
522 @ text-align: center;
@@ -532,18 +532,18 @@
532 @ }
533 @ div.submenu a:hover {
534 @ color: #eee;
535 @ background-color: #333;
536 @ }
537 @
538 @ /* All page content from the bottom of the menu or submenu down to
539 @ ** the footer */
540 @ div.content {
541 @ float right;
542 @ padding: 2ex 1ex 0ex 2ex;
543 @ }
544 @
545 @ /* Some pages have section dividers */
546 @ div.section {
547 @ margin-bottom: 0px;
548 @ margin-top: 1em;
549 @ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553 @ border-color:#999;
554 @ border-width:1px 0px;
555 @ background-color: #eee;
556 @ color: #333;
557 @ }
558 @
559 @ /* The "Date" that occurs on the left hand side of timelines */
560 @ div.divider {
561 @ background: #eee;
562 @ border: 2px #999 solid;
563 @ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565 @ margin: .2em 0 .2em 0;
566 @ float: left;
567 @ clear: left;
568 @ color: #333
569 @ }
570 @
571 @ /* The footer at the very bottom of the page */
572 @ div.footer {
573 @ font-size: 0.8em;
574 @ margin-top: 12px;
575 @ padding: 5px 10px 5px 10px;
576 @ text-align: right;
577 @ background-color: #eee;
578 @ color: #555;
579 @ }
580 @
581 @ /* <verbatim> blocks */
582 @ pre.verbatim {
583 @ background-color: #f5f5f5;
584 @ padding: 0.5em;
585 @ }
586 @
587 @ /* The label/value pairs on (for example) the ci page */
588 @ table.label-value th {
589 @ vertical-align: top;
590 @ text-align: right;
591 @ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600 @ </head>
601 @ <body>
602 @ <div class="header">
603 @ <div class="logo">
604 @ <!-- <img src="$baseurl/logo" alt="logo"> -->
605 @ <br><nobr>$<project_name></nobr>
606 @ </div>
607 @ <div class="title">$<title></div>
608 @ <div class="status"><nobr><th1>
609 @ if {[info exists login]} {
610 @ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732 db_begin_transaction();
733
734 /* Process requests to delete a user-defined skin */
735 if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736 style_header("Confirm Custom Skin Delete");
737 @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
738 @ <p>Deletion of a custom skin is a permanent action that cannot
739 @ be undone. Please confirm that this is what you want to do:</p>
740 @ <input type="hidden" name="sn" value="%h(P("sn"))">
741 @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
742 @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
743 login_insert_csrf_secret();
744 @ </form>
745 style_footer();
746 return;
747 }
748 if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800 }
801 }
802
803 style_header("Skins");
804 @ <p>A "skin" is a combination of
805 @ <a href="setup_editcss">CSS</a>,
806 @ <a href="setup_header">Header</a>,
807 @ <a href="setup_footer">Footer</a>, and
808 @ <a href="setup_logo">Logo</a> that determines the look and feel
809 @ of the web interface.</p>
810 @
@@ -813,15 +813,15 @@
813 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814 z = aBuiltinSkin[i].zName;
815 if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817 }else{
818 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
819 @ %h(z).&nbsp;&nbsp;
820 @ <input type="hidden" name="sn" value="%h(z)">
821 @ <input type="submit" name="load" value="Use This Skin">
822 @ </form></li>
823 }
824 }
825 db_prepare(&q,
826 "SELECT substr(name, 6), value FROM config"
827 " WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832 const char *zV = db_column_text(&q, 1);
833 if( strcmp(zV, zCurrent)==0 ){
834 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835 }else{
836 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837 @ %h(zN).&nbsp;&nbsp;
838 @ <input type="hidden" name="sn" value="%h(zN)">
839 @ <input type="submit" name="load" value="Use This Skin">
840 @ <input type="submit" name="del1" value="Delete This Skin">
841 @ </form></li>
842 }
843
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
24 /* @-comment: // */
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
31 @ body {
32 @ margin: 0ex 1ex;
33 @ padding: 0px;
34 @ background-color: white;
35 @ font-family: sans-serif;
36 @ }
37 @
38 @ /* The project logo in the upper left-hand corner of each page */
39 @ div.logo {
40 @ display: table-row;
41 @ text-align: center;
42 @ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
44 @ font-weight: bold;
45 @ background-color: #707070;
46 @ color: #ffffff;
47 @ min-width: 200px;
48 @ }
49 @
50 @ /* The page title centered at the top of each page */
51 @ div.title {
52 @ display: table-cell;
53 @ font-size: 1.5em;
54 @ font-weight: bold;
@@ -56,11 +56,11 @@
56 @ padding: 0 0 0 10px;
57 @ color: #404040;
58 @ vertical-align: bottom;
59 @ width: 100%;
60 @ }
61 @
62 @ /* The login status message in the top right-hand corner */
63 @ div.status {
64 @ display: table-cell;
65 @ text-align: right;
66 @ vertical-align: bottom;
@@ -67,17 +67,17 @@
67 @ color: #404040;
68 @ font-size: 0.8em;
69 @ font-weight: bold;
70 @ min-width: 200px;
71 @ }
72 @
73 @ /* The header across the top of the page */
74 @ div.header {
75 @ display: table;
76 @ width: 100%;
77 @ }
78 @
79 @ /* The main menu bar that appears at the top of the page beneath
80 @ ** the header */
81 @ div.mainmenu {
82 @ padding: 5px 10px 5px 10px;
83 @ font-size: 0.9em;
@@ -85,11 +85,11 @@
85 @ text-align: center;
86 @ letter-spacing: 1px;
87 @ background-color: #404040;
88 @ color: white;
89 @ }
90 @
91 @ /* The submenu bar that *sometimes* appears below the main menu */
92 @ div.submenu {
93 @ padding: 3px 10px 3px 0px;
94 @ font-size: 0.9em;
95 @ text-align: center;
@@ -103,21 +103,21 @@
103 @ }
104 @ div.mainmenu a:hover, div.submenu a:hover {
105 @ color: #404040;
106 @ background-color: white;
107 @ }
108 @
109 @ /* All page content from the bottom of the menu or submenu down to
110 @ ** the footer */
111 @ div.content {
112 @ padding: 0ex 0ex 0ex 0ex;
113 @ }
114 @ /* Hyperlink colors */
115 @ div.content a { color: #604000; }
116 @ div.content a:link { color: #604000;}
117 @ div.content a:visited { color: #600000; }
118 @
119 @ /* Some pages have section dividers */
120 @ div.section {
121 @ margin-bottom: 0px;
122 @ margin-top: 1em;
123 @ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124 @ font-size: 1.2em;
125 @ font-weight: bold;
126 @ background-color: #404040;
127 @ color: white;
128 @ }
129 @
130 @ /* The "Date" that occurs on the left hand side of timelines */
131 @ div.divider {
132 @ background: #a0a0a0;
133 @ border: 2px #505050 solid;
134 @ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135 @ padding: .25em;
136 @ margin: .2em 0 .2em 0;
137 @ float: left;
138 @ clear: left;
139 @ }
140 @
141 @ /* The footer at the very bottom of the page */
142 @ div.footer {
143 @ font-size: 0.8em;
144 @ margin-top: 12px;
145 @ padding: 5px 10px 5px 10px;
146 @ text-align: right;
147 @ background-color: #404040;
148 @ color: white;
149 @ }
150 @
151 @ /* The label/value pairs on (for example) the vinfo page */
152 @ table.label-value th {
153 @ vertical-align: top;
154 @ text-align: right;
155 @ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165 @ <body>
166 @ <div class="header">
167 @ <div class="logo">
168 @ <img src="$baseurl/logo" alt="logo">
169 @ </div>
170 @ <div class="title"><small>$<project_name></small><br />$<title></div>
171 @ <div class="status"><nobr><th1>
172 @ if {[info exists login]} {
173 @ puts "Logged in as $login"
174 @ } else {
175 @ puts "Not logged in"
@@ -206,39 +206,39 @@
206 @ html "<a href=''$baseurl/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config VALUES('footer','<div class="footer">
211 @ Fossil version $manifest_version $manifest_date
212 @ </div>
213 @ </body></html>
214 @ ');
215 ;
216
217 /*
218 ** A tan theme with the project title above the user identification
219 ** and no logo image.
220 */
221 static const char zBuiltinSkin2[] =
222 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223 @ body {
224 @ margin: 0ex 0ex;
225 @ padding: 0px;
226 @ background-color: #fef3bc;
227 @ font-family: sans-serif;
228 @ }
229 @
230 @ /* The project logo in the upper left-hand corner of each page */
231 @ div.logo {
232 @ display: inline;
233 @ text-align: center;
234 @ vertical-align: bottom;
235 @ font-weight: bold;
236 @ font-size: 2.5em;
237 @ color: #a09048;
238 @ }
239 @
240 @ /* The page title centered at the top of each page */
241 @ div.title {
242 @ display: table-cell;
243 @ font-size: 2em;
244 @ font-weight: bold;
@@ -246,11 +246,11 @@
246 @ padding: 0 0 0 5px;
247 @ color: #a09048;
248 @ vertical-align: bottom;
249 @ width: 100%;
250 @ }
251 @
252 @ /* The login status message in the top right-hand corner */
253 @ div.status {
254 @ display: table-cell;
255 @ text-align: right;
256 @ vertical-align: bottom;
@@ -257,17 +257,17 @@
257 @ color: #a09048;
258 @ padding: 5px 5px 0 0;
259 @ font-size: 0.8em;
260 @ font-weight: bold;
261 @ }
262 @
263 @ /* The header across the top of the page */
264 @ div.header {
265 @ display: table;
266 @ width: 100%;
267 @ }
268 @
269 @ /* The main menu bar that appears at the top of the page beneath
270 @ ** the header */
271 @ div.mainmenu {
272 @ padding: 5px 10px 5px 10px;
273 @ font-size: 0.9em;
@@ -275,11 +275,11 @@
275 @ text-align: center;
276 @ letter-spacing: 1px;
277 @ background-color: #a09048;
278 @ color: black;
279 @ }
280 @
281 @ /* The submenu bar that *sometimes* appears below the main menu */
282 @ div.submenu {
283 @ padding: 3px 10px 3px 0px;
284 @ font-size: 0.9em;
285 @ text-align: center;
@@ -293,21 +293,21 @@
293 @ }
294 @ div.mainmenu a:hover, div.submenu a:hover {
295 @ color: #a09048;
296 @ background-color: white;
297 @ }
298 @
299 @ /* All page content from the bottom of the menu or submenu down to
300 @ ** the footer */
301 @ div.content {
302 @ padding: 1ex 5px;
303 @ }
304 @ div.content a { color: #706532; }
305 @ div.content a:link { color: #706532; }
306 @ div.content a:visited { color: #704032; }
307 @ div.content a:hover { background-color: white; color: #706532; }
308 @
309 @ /* Some pages have section dividers */
310 @ div.section {
311 @ margin-bottom: 0px;
312 @ margin-top: 1em;
313 @ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314 @ font-size: 1.2em;
315 @ font-weight: bold;
316 @ background-color: #a09048;
317 @ color: white;
318 @ }
319 @
320 @ /* The "Date" that occurs on the left hand side of timelines */
321 @ div.divider {
322 @ background: #e1d498;
323 @ border: 2px #a09048 solid;
324 @ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325 @ padding: .25em;
326 @ margin: .2em 0 .2em 0;
327 @ float: left;
328 @ clear: left;
329 @ }
330 @
331 @ /* The footer at the very bottom of the page */
332 @ div.footer {
333 @ font-size: 0.8em;
334 @ margin-top: 12px;
335 @ padding: 5px 10px 5px 10px;
336 @ text-align: right;
337 @ background-color: #a09048;
338 @ color: white;
339 @ }
340 @
341 @ /* Hyperlink colors */
342 @ div.footer a { color: white; }
343 @ div.footer a:link { color: white; }
344 @ div.footer a:visited { color: white; }
345 @ div.footer a:hover { background-color: white; color: #558195; }
346 @
347 @ /* <verbatim> blocks */
348 @ pre.verbatim {
349 @ background-color: #f5f5f5;
350 @ padding: 0.5em;
351 @ }
352 @
353 @ /* The label/value pairs on (for example) the ci page */
354 @ table.label-value th {
355 @ vertical-align: top;
356 @ text-align: right;
357 @ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418
419 /*
420 ** Black letters on a white or cream background with the main menu
421 ** stuck on the left-hand side.
422 */
423 static const char zBuiltinSkin3[] =
424 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425 @ body {
426 @ margin:0px 0px 0px 0px;
427 @ padding:0px;
428 @ font-family:verdana, arial, helvetica, "sans serif";
429 @ color:#333;
430 @ background-color:white;
431 @ }
432 @
433 @ /* consistent colours */
434 @ h2 {
435 @ color: #333;
436 @ }
437 @ h3 {
438 @ color: #333;
439 @ }
440 @
441 @ /* The project logo in the upper left-hand corner of each page */
442 @ div.logo {
443 @ display: table-cell;
444 @ text-align: left;
445 @ vertical-align: bottom;
446 @ font-weight: bold;
447 @ color: #333;
448 @ }
449 @
450 @ /* The page title centered at the top of each page */
451 @ div.title {
452 @ display: table-cell;
453 @ font-size: 2em;
454 @ font-weight: bold;
@@ -455,11 +455,11 @@
455 @ text-align: center;
456 @ color: #333;
457 @ vertical-align: bottom;
458 @ width: 100%;
459 @ }
460 @
461 @ /* The login status message in the top right-hand corner */
462 @ div.status {
463 @ display: table-cell;
464 @ padding-right: 10px;
465 @ text-align: right;
@@ -467,21 +467,21 @@
467 @ padding-bottom: 5px;
468 @ color: #333;
469 @ font-size: 0.8em;
470 @ font-weight: bold;
471 @ }
472 @
473 @ /* The header across the top of the page */
474 @ div.header {
475 @ margin:10px 0px 10px 0px;
476 @ padding:1px 0px 0px 20px;
477 @ border-style:solid;
478 @ border-color:black;
479 @ border-width:1px 0px;
480 @ background-color:#eee;
481 @ }
482 @
483 @ /* The main menu bar that appears at the top left of the page beneath
484 @ ** the header. Width must be co-ordinated with the container below */
485 @ div.mainmenu {
486 @ float: left;
487 @ margin-left: 10px;
@@ -491,11 +491,11 @@
491 @ padding:5px;
492 @ background-color:#eee;
493 @ border:1px solid #999;
494 @ width:8em;
495 @ }
496 @
497 @ /* Main menu is now a list */
498 @ div.mainmenu ul {
499 @ padding: 0;
500 @ list-style:none;
501 @ }
@@ -506,17 +506,17 @@
506 @ }
507 @ div.mainmenu a:hover {
508 @ color: #eee;
509 @ background-color: #333;
510 @ }
511 @
512 @ /* Container for the sub-menu and content so they don''t spread
513 @ ** out underneath the main menu */
514 @ #container {
515 @ padding-left: 9em;
516 @ }
517 @
518 @ /* The submenu bar that *sometimes* appears below the main menu */
519 @ div.submenu {
520 @ padding: 3px 10px 3px 10px;
521 @ font-size: 0.9em;
522 @ text-align: center;
@@ -532,18 +532,18 @@
532 @ }
533 @ div.submenu a:hover {
534 @ color: #eee;
535 @ background-color: #333;
536 @ }
537 @
538 @ /* All page content from the bottom of the menu or submenu down to
539 @ ** the footer */
540 @ div.content {
541 @ float right;
542 @ padding: 2ex 1ex 0ex 2ex;
543 @ }
544 @
545 @ /* Some pages have section dividers */
546 @ div.section {
547 @ margin-bottom: 0px;
548 @ margin-top: 1em;
549 @ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553 @ border-color:#999;
554 @ border-width:1px 0px;
555 @ background-color: #eee;
556 @ color: #333;
557 @ }
558 @
559 @ /* The "Date" that occurs on the left hand side of timelines */
560 @ div.divider {
561 @ background: #eee;
562 @ border: 2px #999 solid;
563 @ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565 @ margin: .2em 0 .2em 0;
566 @ float: left;
567 @ clear: left;
568 @ color: #333
569 @ }
570 @
571 @ /* The footer at the very bottom of the page */
572 @ div.footer {
573 @ font-size: 0.8em;
574 @ margin-top: 12px;
575 @ padding: 5px 10px 5px 10px;
576 @ text-align: right;
577 @ background-color: #eee;
578 @ color: #555;
579 @ }
580 @
581 @ /* <verbatim> blocks */
582 @ pre.verbatim {
583 @ background-color: #f5f5f5;
584 @ padding: 0.5em;
585 @ }
586 @
587 @ /* The label/value pairs on (for example) the ci page */
588 @ table.label-value th {
589 @ vertical-align: top;
590 @ text-align: right;
591 @ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600 @ </head>
601 @ <body>
602 @ <div class="header">
603 @ <div class="logo">
604 @ <!-- <img src="$baseurl/logo" alt="logo"> -->
605 @ <br /><nobr>$<project_name></nobr>
606 @ </div>
607 @ <div class="title">$<title></div>
608 @ <div class="status"><nobr><th1>
609 @ if {[info exists login]} {
610 @ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732 db_begin_transaction();
733
734 /* Process requests to delete a user-defined skin */
735 if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736 style_header("Confirm Custom Skin Delete");
737 @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
738 @ <p>Deletion of a custom skin is a permanent action that cannot
739 @ be undone. Please confirm that this is what you want to do:</p>
740 @ <input type="hidden" name="sn" value="%h(P("sn"))" />
741 @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
742 @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
743 login_insert_csrf_secret();
744 @ </div></form>
745 style_footer();
746 return;
747 }
748 if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800 }
801 }
802
803 style_header("Skins");
804 @ <p>A "skin" is a combination of
805 @ <a href="setup_editcss">CSS</a>,
806 @ <a href="setup_header">Header</a>,
807 @ <a href="setup_footer">Footer</a>, and
808 @ <a href="setup_logo">Logo</a> that determines the look and feel
809 @ of the web interface.</p>
810 @
@@ -813,15 +813,15 @@
813 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814 z = aBuiltinSkin[i].zName;
815 if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817 }else{
818 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
819 @ %h(z).&nbsp;&nbsp;
820 @ <input type="hidden" name="sn" value="%h(z)" />
821 @ <input type="submit" name="load" value="Use This Skin" />
822 @ </div></form></li>
823 }
824 }
825 db_prepare(&q,
826 "SELECT substr(name, 6), value FROM config"
827 " WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832 const char *zV = db_column_text(&q, 1);
833 if( strcmp(zV, zCurrent)==0 ){
834 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835 }else{
836 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837 @ %h(zN).&nbsp;&nbsp;
838 @ <input type="hidden" name="sn" value="%h(zN)">
839 @ <input type="submit" name="load" value="Use This Skin">
840 @ <input type="submit" name="del1" value="Delete This Skin">
841 @ </form></li>
842 }
843
+57 -57
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
2424
/* @-comment: // */
2525
/*
2626
** A black-and-white theme with the project title in a bar across the top
2727
** and no logo image.
2828
*/
29
-static const char zBuiltinSkin1[] =
29
+static const char zBuiltinSkin1[] =
3030
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
3131
@ body {
3232
@ margin: 0ex 1ex;
3333
@ padding: 0px;
3434
@ background-color: white;
3535
@ font-family: sans-serif;
3636
@ }
37
-@
37
+@
3838
@ /* The project logo in the upper left-hand corner of each page */
3939
@ div.logo {
4040
@ display: table-row;
4141
@ text-align: center;
4242
@ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
4444
@ font-weight: bold;
4545
@ background-color: #707070;
4646
@ color: #ffffff;
4747
@ min-width: 200px;
4848
@ }
49
-@
49
+@
5050
@ /* The page title centered at the top of each page */
5151
@ div.title {
5252
@ display: table-cell;
5353
@ font-size: 1.5em;
5454
@ font-weight: bold;
@@ -56,11 +56,11 @@
5656
@ padding: 0 0 0 10px;
5757
@ color: #404040;
5858
@ vertical-align: bottom;
5959
@ width: 100%;
6060
@ }
61
-@
61
+@
6262
@ /* The login status message in the top right-hand corner */
6363
@ div.status {
6464
@ display: table-cell;
6565
@ text-align: right;
6666
@ vertical-align: bottom;
@@ -67,17 +67,17 @@
6767
@ color: #404040;
6868
@ font-size: 0.8em;
6969
@ font-weight: bold;
7070
@ min-width: 200px;
7171
@ }
72
-@
72
+@
7373
@ /* The header across the top of the page */
7474
@ div.header {
7575
@ display: table;
7676
@ width: 100%;
7777
@ }
78
-@
78
+@
7979
@ /* The main menu bar that appears at the top of the page beneath
8080
@ ** the header */
8181
@ div.mainmenu {
8282
@ padding: 5px 10px 5px 10px;
8383
@ font-size: 0.9em;
@@ -85,11 +85,11 @@
8585
@ text-align: center;
8686
@ letter-spacing: 1px;
8787
@ background-color: #404040;
8888
@ color: white;
8989
@ }
90
-@
90
+@
9191
@ /* The submenu bar that *sometimes* appears below the main menu */
9292
@ div.submenu {
9393
@ padding: 3px 10px 3px 0px;
9494
@ font-size: 0.9em;
9595
@ text-align: center;
@@ -103,21 +103,21 @@
103103
@ }
104104
@ div.mainmenu a:hover, div.submenu a:hover {
105105
@ color: #404040;
106106
@ background-color: white;
107107
@ }
108
-@
108
+@
109109
@ /* All page content from the bottom of the menu or submenu down to
110110
@ ** the footer */
111111
@ div.content {
112112
@ padding: 0ex 0ex 0ex 0ex;
113113
@ }
114114
@ /* Hyperlink colors */
115115
@ div.content a { color: #604000; }
116116
@ div.content a:link { color: #604000;}
117117
@ div.content a:visited { color: #600000; }
118
-@
118
+@
119119
@ /* Some pages have section dividers */
120120
@ div.section {
121121
@ margin-bottom: 0px;
122122
@ margin-top: 1em;
123123
@ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124124
@ font-size: 1.2em;
125125
@ font-weight: bold;
126126
@ background-color: #404040;
127127
@ color: white;
128128
@ }
129
-@
129
+@
130130
@ /* The "Date" that occurs on the left hand side of timelines */
131131
@ div.divider {
132132
@ background: #a0a0a0;
133133
@ border: 2px #505050 solid;
134134
@ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135135
@ padding: .25em;
136136
@ margin: .2em 0 .2em 0;
137137
@ float: left;
138138
@ clear: left;
139139
@ }
140
-@
140
+@
141141
@ /* The footer at the very bottom of the page */
142142
@ div.footer {
143143
@ font-size: 0.8em;
144144
@ margin-top: 12px;
145145
@ padding: 5px 10px 5px 10px;
146146
@ text-align: right;
147147
@ background-color: #404040;
148148
@ color: white;
149149
@ }
150
-@
150
+@
151151
@ /* The label/value pairs on (for example) the vinfo page */
152152
@ table.label-value th {
153153
@ vertical-align: top;
154154
@ text-align: right;
155155
@ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165165
@ <body>
166166
@ <div class="header">
167167
@ <div class="logo">
168168
@ <img src="$baseurl/logo" alt="logo">
169169
@ </div>
170
-@ <div class="title"><small>$<project_name></small><br>$<title></div>
170
+@ <div class="title"><small>$<project_name></small><br />$<title></div>
171171
@ <div class="status"><nobr><th1>
172172
@ if {[info exists login]} {
173173
@ puts "Logged in as $login"
174174
@ } else {
175175
@ puts "Not logged in"
@@ -206,39 +206,39 @@
206206
@ html "<a href=''$baseurl/login''>Login</a> "
207207
@ }
208208
@ </th1></div>
209209
@ ');
210210
@ REPLACE INTO config VALUES('footer','<div class="footer">
211
-@ Fossil version $manifest_version $manifest_date
211
+@ Fossil version $manifest_version $manifest_date
212212
@ </div>
213213
@ </body></html>
214214
@ ');
215215
;
216216
217217
/*
218218
** A tan theme with the project title above the user identification
219219
** and no logo image.
220220
*/
221
-static const char zBuiltinSkin2[] =
221
+static const char zBuiltinSkin2[] =
222222
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223223
@ body {
224224
@ margin: 0ex 0ex;
225225
@ padding: 0px;
226226
@ background-color: #fef3bc;
227227
@ font-family: sans-serif;
228228
@ }
229
-@
229
+@
230230
@ /* The project logo in the upper left-hand corner of each page */
231231
@ div.logo {
232232
@ display: inline;
233233
@ text-align: center;
234234
@ vertical-align: bottom;
235235
@ font-weight: bold;
236236
@ font-size: 2.5em;
237237
@ color: #a09048;
238238
@ }
239
-@
239
+@
240240
@ /* The page title centered at the top of each page */
241241
@ div.title {
242242
@ display: table-cell;
243243
@ font-size: 2em;
244244
@ font-weight: bold;
@@ -246,11 +246,11 @@
246246
@ padding: 0 0 0 5px;
247247
@ color: #a09048;
248248
@ vertical-align: bottom;
249249
@ width: 100%;
250250
@ }
251
-@
251
+@
252252
@ /* The login status message in the top right-hand corner */
253253
@ div.status {
254254
@ display: table-cell;
255255
@ text-align: right;
256256
@ vertical-align: bottom;
@@ -257,17 +257,17 @@
257257
@ color: #a09048;
258258
@ padding: 5px 5px 0 0;
259259
@ font-size: 0.8em;
260260
@ font-weight: bold;
261261
@ }
262
-@
262
+@
263263
@ /* The header across the top of the page */
264264
@ div.header {
265265
@ display: table;
266266
@ width: 100%;
267267
@ }
268
-@
268
+@
269269
@ /* The main menu bar that appears at the top of the page beneath
270270
@ ** the header */
271271
@ div.mainmenu {
272272
@ padding: 5px 10px 5px 10px;
273273
@ font-size: 0.9em;
@@ -275,11 +275,11 @@
275275
@ text-align: center;
276276
@ letter-spacing: 1px;
277277
@ background-color: #a09048;
278278
@ color: black;
279279
@ }
280
-@
280
+@
281281
@ /* The submenu bar that *sometimes* appears below the main menu */
282282
@ div.submenu {
283283
@ padding: 3px 10px 3px 0px;
284284
@ font-size: 0.9em;
285285
@ text-align: center;
@@ -293,21 +293,21 @@
293293
@ }
294294
@ div.mainmenu a:hover, div.submenu a:hover {
295295
@ color: #a09048;
296296
@ background-color: white;
297297
@ }
298
-@
298
+@
299299
@ /* All page content from the bottom of the menu or submenu down to
300300
@ ** the footer */
301301
@ div.content {
302302
@ padding: 1ex 5px;
303303
@ }
304304
@ div.content a { color: #706532; }
305305
@ div.content a:link { color: #706532; }
306306
@ div.content a:visited { color: #704032; }
307307
@ div.content a:hover { background-color: white; color: #706532; }
308
-@
308
+@
309309
@ /* Some pages have section dividers */
310310
@ div.section {
311311
@ margin-bottom: 0px;
312312
@ margin-top: 1em;
313313
@ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314314
@ font-size: 1.2em;
315315
@ font-weight: bold;
316316
@ background-color: #a09048;
317317
@ color: white;
318318
@ }
319
-@
319
+@
320320
@ /* The "Date" that occurs on the left hand side of timelines */
321321
@ div.divider {
322322
@ background: #e1d498;
323323
@ border: 2px #a09048 solid;
324324
@ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325325
@ padding: .25em;
326326
@ margin: .2em 0 .2em 0;
327327
@ float: left;
328328
@ clear: left;
329329
@ }
330
-@
330
+@
331331
@ /* The footer at the very bottom of the page */
332332
@ div.footer {
333333
@ font-size: 0.8em;
334334
@ margin-top: 12px;
335335
@ padding: 5px 10px 5px 10px;
336336
@ text-align: right;
337337
@ background-color: #a09048;
338338
@ color: white;
339339
@ }
340
-@
340
+@
341341
@ /* Hyperlink colors */
342342
@ div.footer a { color: white; }
343343
@ div.footer a:link { color: white; }
344344
@ div.footer a:visited { color: white; }
345345
@ div.footer a:hover { background-color: white; color: #558195; }
346
-@
346
+@
347347
@ /* <verbatim> blocks */
348348
@ pre.verbatim {
349349
@ background-color: #f5f5f5;
350350
@ padding: 0.5em;
351351
@ }
352
-@
352
+@
353353
@ /* The label/value pairs on (for example) the ci page */
354354
@ table.label-value th {
355355
@ vertical-align: top;
356356
@ text-align: right;
357357
@ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418418
419419
/*
420420
** Black letters on a white or cream background with the main menu
421421
** stuck on the left-hand side.
422422
*/
423
-static const char zBuiltinSkin3[] =
423
+static const char zBuiltinSkin3[] =
424424
@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425425
@ body {
426426
@ margin:0px 0px 0px 0px;
427427
@ padding:0px;
428428
@ font-family:verdana, arial, helvetica, "sans serif";
429429
@ color:#333;
430430
@ background-color:white;
431431
@ }
432
-@
432
+@
433433
@ /* consistent colours */
434434
@ h2 {
435435
@ color: #333;
436436
@ }
437437
@ h3 {
438438
@ color: #333;
439439
@ }
440
-@
440
+@
441441
@ /* The project logo in the upper left-hand corner of each page */
442442
@ div.logo {
443443
@ display: table-cell;
444444
@ text-align: left;
445445
@ vertical-align: bottom;
446446
@ font-weight: bold;
447447
@ color: #333;
448448
@ }
449
-@
449
+@
450450
@ /* The page title centered at the top of each page */
451451
@ div.title {
452452
@ display: table-cell;
453453
@ font-size: 2em;
454454
@ font-weight: bold;
@@ -455,11 +455,11 @@
455455
@ text-align: center;
456456
@ color: #333;
457457
@ vertical-align: bottom;
458458
@ width: 100%;
459459
@ }
460
-@
460
+@
461461
@ /* The login status message in the top right-hand corner */
462462
@ div.status {
463463
@ display: table-cell;
464464
@ padding-right: 10px;
465465
@ text-align: right;
@@ -467,21 +467,21 @@
467467
@ padding-bottom: 5px;
468468
@ color: #333;
469469
@ font-size: 0.8em;
470470
@ font-weight: bold;
471471
@ }
472
-@
472
+@
473473
@ /* The header across the top of the page */
474474
@ div.header {
475475
@ margin:10px 0px 10px 0px;
476476
@ padding:1px 0px 0px 20px;
477477
@ border-style:solid;
478478
@ border-color:black;
479479
@ border-width:1px 0px;
480480
@ background-color:#eee;
481481
@ }
482
-@
482
+@
483483
@ /* The main menu bar that appears at the top left of the page beneath
484484
@ ** the header. Width must be co-ordinated with the container below */
485485
@ div.mainmenu {
486486
@ float: left;
487487
@ margin-left: 10px;
@@ -491,11 +491,11 @@
491491
@ padding:5px;
492492
@ background-color:#eee;
493493
@ border:1px solid #999;
494494
@ width:8em;
495495
@ }
496
-@
496
+@
497497
@ /* Main menu is now a list */
498498
@ div.mainmenu ul {
499499
@ padding: 0;
500500
@ list-style:none;
501501
@ }
@@ -506,17 +506,17 @@
506506
@ }
507507
@ div.mainmenu a:hover {
508508
@ color: #eee;
509509
@ background-color: #333;
510510
@ }
511
-@
511
+@
512512
@ /* Container for the sub-menu and content so they don''t spread
513513
@ ** out underneath the main menu */
514514
@ #container {
515515
@ padding-left: 9em;
516516
@ }
517
-@
517
+@
518518
@ /* The submenu bar that *sometimes* appears below the main menu */
519519
@ div.submenu {
520520
@ padding: 3px 10px 3px 10px;
521521
@ font-size: 0.9em;
522522
@ text-align: center;
@@ -532,18 +532,18 @@
532532
@ }
533533
@ div.submenu a:hover {
534534
@ color: #eee;
535535
@ background-color: #333;
536536
@ }
537
-@
537
+@
538538
@ /* All page content from the bottom of the menu or submenu down to
539539
@ ** the footer */
540540
@ div.content {
541541
@ float right;
542542
@ padding: 2ex 1ex 0ex 2ex;
543543
@ }
544
-@
544
+@
545545
@ /* Some pages have section dividers */
546546
@ div.section {
547547
@ margin-bottom: 0px;
548548
@ margin-top: 1em;
549549
@ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553553
@ border-color:#999;
554554
@ border-width:1px 0px;
555555
@ background-color: #eee;
556556
@ color: #333;
557557
@ }
558
-@
558
+@
559559
@ /* The "Date" that occurs on the left hand side of timelines */
560560
@ div.divider {
561561
@ background: #eee;
562562
@ border: 2px #999 solid;
563563
@ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565565
@ margin: .2em 0 .2em 0;
566566
@ float: left;
567567
@ clear: left;
568568
@ color: #333
569569
@ }
570
-@
570
+@
571571
@ /* The footer at the very bottom of the page */
572572
@ div.footer {
573573
@ font-size: 0.8em;
574574
@ margin-top: 12px;
575575
@ padding: 5px 10px 5px 10px;
576576
@ text-align: right;
577577
@ background-color: #eee;
578578
@ color: #555;
579579
@ }
580
-@
580
+@
581581
@ /* <verbatim> blocks */
582582
@ pre.verbatim {
583583
@ background-color: #f5f5f5;
584584
@ padding: 0.5em;
585585
@ }
586
-@
586
+@
587587
@ /* The label/value pairs on (for example) the ci page */
588588
@ table.label-value th {
589589
@ vertical-align: top;
590590
@ text-align: right;
591591
@ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600600
@ </head>
601601
@ <body>
602602
@ <div class="header">
603603
@ <div class="logo">
604604
@ <!-- <img src="$baseurl/logo" alt="logo"> -->
605
-@ <br><nobr>$<project_name></nobr>
605
+@ <br /><nobr>$<project_name></nobr>
606606
@ </div>
607607
@ <div class="title">$<title></div>
608608
@ <div class="status"><nobr><th1>
609609
@ if {[info exists login]} {
610610
@ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732732
db_begin_transaction();
733733
734734
/* Process requests to delete a user-defined skin */
735735
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736736
style_header("Confirm Custom Skin Delete");
737
- @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
737
+ @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
738738
@ <p>Deletion of a custom skin is a permanent action that cannot
739739
@ be undone. Please confirm that this is what you want to do:</p>
740
- @ <input type="hidden" name="sn" value="%h(P("sn"))">
741
- @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
742
- @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
740
+ @ <input type="hidden" name="sn" value="%h(P("sn"))" />
741
+ @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
742
+ @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
743743
login_insert_csrf_secret();
744
- @ </form>
744
+ @ </div></form>
745745
style_footer();
746746
return;
747747
}
748748
if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749749
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800800
}
801801
}
802802
803803
style_header("Skins");
804804
@ <p>A "skin" is a combination of
805
- @ <a href="setup_editcss">CSS</a>,
805
+ @ <a href="setup_editcss">CSS</a>,
806806
@ <a href="setup_header">Header</a>,
807807
@ <a href="setup_footer">Footer</a>, and
808808
@ <a href="setup_logo">Logo</a> that determines the look and feel
809809
@ of the web interface.</p>
810810
@
@@ -813,15 +813,15 @@
813813
for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814814
z = aBuiltinSkin[i].zName;
815815
if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816816
@ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817817
}else{
818
- @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
819
- @ %h(z).&nbsp;&nbsp;
820
- @ <input type="hidden" name="sn" value="%h(z)">
821
- @ <input type="submit" name="load" value="Use This Skin">
822
- @ </form></li>
818
+ @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
819
+ @ %h(z).&nbsp;&nbsp;
820
+ @ <input type="hidden" name="sn" value="%h(z)" />
821
+ @ <input type="submit" name="load" value="Use This Skin" />
822
+ @ </div></form></li>
823823
}
824824
}
825825
db_prepare(&q,
826826
"SELECT substr(name, 6), value FROM config"
827827
" WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832832
const char *zV = db_column_text(&q, 1);
833833
if( strcmp(zV, zCurrent)==0 ){
834834
@ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835835
}else{
836836
@ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837
- @ %h(zN).&nbsp;&nbsp;
837
+ @ %h(zN).&nbsp;&nbsp;
838838
@ <input type="hidden" name="sn" value="%h(zN)">
839839
@ <input type="submit" name="load" value="Use This Skin">
840840
@ <input type="submit" name="del1" value="Delete This Skin">
841841
@ </form></li>
842842
}
843843
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
24 /* @-comment: // */
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
31 @ body {
32 @ margin: 0ex 1ex;
33 @ padding: 0px;
34 @ background-color: white;
35 @ font-family: sans-serif;
36 @ }
37 @
38 @ /* The project logo in the upper left-hand corner of each page */
39 @ div.logo {
40 @ display: table-row;
41 @ text-align: center;
42 @ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
44 @ font-weight: bold;
45 @ background-color: #707070;
46 @ color: #ffffff;
47 @ min-width: 200px;
48 @ }
49 @
50 @ /* The page title centered at the top of each page */
51 @ div.title {
52 @ display: table-cell;
53 @ font-size: 1.5em;
54 @ font-weight: bold;
@@ -56,11 +56,11 @@
56 @ padding: 0 0 0 10px;
57 @ color: #404040;
58 @ vertical-align: bottom;
59 @ width: 100%;
60 @ }
61 @
62 @ /* The login status message in the top right-hand corner */
63 @ div.status {
64 @ display: table-cell;
65 @ text-align: right;
66 @ vertical-align: bottom;
@@ -67,17 +67,17 @@
67 @ color: #404040;
68 @ font-size: 0.8em;
69 @ font-weight: bold;
70 @ min-width: 200px;
71 @ }
72 @
73 @ /* The header across the top of the page */
74 @ div.header {
75 @ display: table;
76 @ width: 100%;
77 @ }
78 @
79 @ /* The main menu bar that appears at the top of the page beneath
80 @ ** the header */
81 @ div.mainmenu {
82 @ padding: 5px 10px 5px 10px;
83 @ font-size: 0.9em;
@@ -85,11 +85,11 @@
85 @ text-align: center;
86 @ letter-spacing: 1px;
87 @ background-color: #404040;
88 @ color: white;
89 @ }
90 @
91 @ /* The submenu bar that *sometimes* appears below the main menu */
92 @ div.submenu {
93 @ padding: 3px 10px 3px 0px;
94 @ font-size: 0.9em;
95 @ text-align: center;
@@ -103,21 +103,21 @@
103 @ }
104 @ div.mainmenu a:hover, div.submenu a:hover {
105 @ color: #404040;
106 @ background-color: white;
107 @ }
108 @
109 @ /* All page content from the bottom of the menu or submenu down to
110 @ ** the footer */
111 @ div.content {
112 @ padding: 0ex 0ex 0ex 0ex;
113 @ }
114 @ /* Hyperlink colors */
115 @ div.content a { color: #604000; }
116 @ div.content a:link { color: #604000;}
117 @ div.content a:visited { color: #600000; }
118 @
119 @ /* Some pages have section dividers */
120 @ div.section {
121 @ margin-bottom: 0px;
122 @ margin-top: 1em;
123 @ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124 @ font-size: 1.2em;
125 @ font-weight: bold;
126 @ background-color: #404040;
127 @ color: white;
128 @ }
129 @
130 @ /* The "Date" that occurs on the left hand side of timelines */
131 @ div.divider {
132 @ background: #a0a0a0;
133 @ border: 2px #505050 solid;
134 @ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135 @ padding: .25em;
136 @ margin: .2em 0 .2em 0;
137 @ float: left;
138 @ clear: left;
139 @ }
140 @
141 @ /* The footer at the very bottom of the page */
142 @ div.footer {
143 @ font-size: 0.8em;
144 @ margin-top: 12px;
145 @ padding: 5px 10px 5px 10px;
146 @ text-align: right;
147 @ background-color: #404040;
148 @ color: white;
149 @ }
150 @
151 @ /* The label/value pairs on (for example) the vinfo page */
152 @ table.label-value th {
153 @ vertical-align: top;
154 @ text-align: right;
155 @ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165 @ <body>
166 @ <div class="header">
167 @ <div class="logo">
168 @ <img src="$baseurl/logo" alt="logo">
169 @ </div>
170 @ <div class="title"><small>$<project_name></small><br>$<title></div>
171 @ <div class="status"><nobr><th1>
172 @ if {[info exists login]} {
173 @ puts "Logged in as $login"
174 @ } else {
175 @ puts "Not logged in"
@@ -206,39 +206,39 @@
206 @ html "<a href=''$baseurl/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config VALUES('footer','<div class="footer">
211 @ Fossil version $manifest_version $manifest_date
212 @ </div>
213 @ </body></html>
214 @ ');
215 ;
216
217 /*
218 ** A tan theme with the project title above the user identification
219 ** and no logo image.
220 */
221 static const char zBuiltinSkin2[] =
222 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223 @ body {
224 @ margin: 0ex 0ex;
225 @ padding: 0px;
226 @ background-color: #fef3bc;
227 @ font-family: sans-serif;
228 @ }
229 @
230 @ /* The project logo in the upper left-hand corner of each page */
231 @ div.logo {
232 @ display: inline;
233 @ text-align: center;
234 @ vertical-align: bottom;
235 @ font-weight: bold;
236 @ font-size: 2.5em;
237 @ color: #a09048;
238 @ }
239 @
240 @ /* The page title centered at the top of each page */
241 @ div.title {
242 @ display: table-cell;
243 @ font-size: 2em;
244 @ font-weight: bold;
@@ -246,11 +246,11 @@
246 @ padding: 0 0 0 5px;
247 @ color: #a09048;
248 @ vertical-align: bottom;
249 @ width: 100%;
250 @ }
251 @
252 @ /* The login status message in the top right-hand corner */
253 @ div.status {
254 @ display: table-cell;
255 @ text-align: right;
256 @ vertical-align: bottom;
@@ -257,17 +257,17 @@
257 @ color: #a09048;
258 @ padding: 5px 5px 0 0;
259 @ font-size: 0.8em;
260 @ font-weight: bold;
261 @ }
262 @
263 @ /* The header across the top of the page */
264 @ div.header {
265 @ display: table;
266 @ width: 100%;
267 @ }
268 @
269 @ /* The main menu bar that appears at the top of the page beneath
270 @ ** the header */
271 @ div.mainmenu {
272 @ padding: 5px 10px 5px 10px;
273 @ font-size: 0.9em;
@@ -275,11 +275,11 @@
275 @ text-align: center;
276 @ letter-spacing: 1px;
277 @ background-color: #a09048;
278 @ color: black;
279 @ }
280 @
281 @ /* The submenu bar that *sometimes* appears below the main menu */
282 @ div.submenu {
283 @ padding: 3px 10px 3px 0px;
284 @ font-size: 0.9em;
285 @ text-align: center;
@@ -293,21 +293,21 @@
293 @ }
294 @ div.mainmenu a:hover, div.submenu a:hover {
295 @ color: #a09048;
296 @ background-color: white;
297 @ }
298 @
299 @ /* All page content from the bottom of the menu or submenu down to
300 @ ** the footer */
301 @ div.content {
302 @ padding: 1ex 5px;
303 @ }
304 @ div.content a { color: #706532; }
305 @ div.content a:link { color: #706532; }
306 @ div.content a:visited { color: #704032; }
307 @ div.content a:hover { background-color: white; color: #706532; }
308 @
309 @ /* Some pages have section dividers */
310 @ div.section {
311 @ margin-bottom: 0px;
312 @ margin-top: 1em;
313 @ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314 @ font-size: 1.2em;
315 @ font-weight: bold;
316 @ background-color: #a09048;
317 @ color: white;
318 @ }
319 @
320 @ /* The "Date" that occurs on the left hand side of timelines */
321 @ div.divider {
322 @ background: #e1d498;
323 @ border: 2px #a09048 solid;
324 @ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325 @ padding: .25em;
326 @ margin: .2em 0 .2em 0;
327 @ float: left;
328 @ clear: left;
329 @ }
330 @
331 @ /* The footer at the very bottom of the page */
332 @ div.footer {
333 @ font-size: 0.8em;
334 @ margin-top: 12px;
335 @ padding: 5px 10px 5px 10px;
336 @ text-align: right;
337 @ background-color: #a09048;
338 @ color: white;
339 @ }
340 @
341 @ /* Hyperlink colors */
342 @ div.footer a { color: white; }
343 @ div.footer a:link { color: white; }
344 @ div.footer a:visited { color: white; }
345 @ div.footer a:hover { background-color: white; color: #558195; }
346 @
347 @ /* <verbatim> blocks */
348 @ pre.verbatim {
349 @ background-color: #f5f5f5;
350 @ padding: 0.5em;
351 @ }
352 @
353 @ /* The label/value pairs on (for example) the ci page */
354 @ table.label-value th {
355 @ vertical-align: top;
356 @ text-align: right;
357 @ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418
419 /*
420 ** Black letters on a white or cream background with the main menu
421 ** stuck on the left-hand side.
422 */
423 static const char zBuiltinSkin3[] =
424 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425 @ body {
426 @ margin:0px 0px 0px 0px;
427 @ padding:0px;
428 @ font-family:verdana, arial, helvetica, "sans serif";
429 @ color:#333;
430 @ background-color:white;
431 @ }
432 @
433 @ /* consistent colours */
434 @ h2 {
435 @ color: #333;
436 @ }
437 @ h3 {
438 @ color: #333;
439 @ }
440 @
441 @ /* The project logo in the upper left-hand corner of each page */
442 @ div.logo {
443 @ display: table-cell;
444 @ text-align: left;
445 @ vertical-align: bottom;
446 @ font-weight: bold;
447 @ color: #333;
448 @ }
449 @
450 @ /* The page title centered at the top of each page */
451 @ div.title {
452 @ display: table-cell;
453 @ font-size: 2em;
454 @ font-weight: bold;
@@ -455,11 +455,11 @@
455 @ text-align: center;
456 @ color: #333;
457 @ vertical-align: bottom;
458 @ width: 100%;
459 @ }
460 @
461 @ /* The login status message in the top right-hand corner */
462 @ div.status {
463 @ display: table-cell;
464 @ padding-right: 10px;
465 @ text-align: right;
@@ -467,21 +467,21 @@
467 @ padding-bottom: 5px;
468 @ color: #333;
469 @ font-size: 0.8em;
470 @ font-weight: bold;
471 @ }
472 @
473 @ /* The header across the top of the page */
474 @ div.header {
475 @ margin:10px 0px 10px 0px;
476 @ padding:1px 0px 0px 20px;
477 @ border-style:solid;
478 @ border-color:black;
479 @ border-width:1px 0px;
480 @ background-color:#eee;
481 @ }
482 @
483 @ /* The main menu bar that appears at the top left of the page beneath
484 @ ** the header. Width must be co-ordinated with the container below */
485 @ div.mainmenu {
486 @ float: left;
487 @ margin-left: 10px;
@@ -491,11 +491,11 @@
491 @ padding:5px;
492 @ background-color:#eee;
493 @ border:1px solid #999;
494 @ width:8em;
495 @ }
496 @
497 @ /* Main menu is now a list */
498 @ div.mainmenu ul {
499 @ padding: 0;
500 @ list-style:none;
501 @ }
@@ -506,17 +506,17 @@
506 @ }
507 @ div.mainmenu a:hover {
508 @ color: #eee;
509 @ background-color: #333;
510 @ }
511 @
512 @ /* Container for the sub-menu and content so they don''t spread
513 @ ** out underneath the main menu */
514 @ #container {
515 @ padding-left: 9em;
516 @ }
517 @
518 @ /* The submenu bar that *sometimes* appears below the main menu */
519 @ div.submenu {
520 @ padding: 3px 10px 3px 10px;
521 @ font-size: 0.9em;
522 @ text-align: center;
@@ -532,18 +532,18 @@
532 @ }
533 @ div.submenu a:hover {
534 @ color: #eee;
535 @ background-color: #333;
536 @ }
537 @
538 @ /* All page content from the bottom of the menu or submenu down to
539 @ ** the footer */
540 @ div.content {
541 @ float right;
542 @ padding: 2ex 1ex 0ex 2ex;
543 @ }
544 @
545 @ /* Some pages have section dividers */
546 @ div.section {
547 @ margin-bottom: 0px;
548 @ margin-top: 1em;
549 @ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553 @ border-color:#999;
554 @ border-width:1px 0px;
555 @ background-color: #eee;
556 @ color: #333;
557 @ }
558 @
559 @ /* The "Date" that occurs on the left hand side of timelines */
560 @ div.divider {
561 @ background: #eee;
562 @ border: 2px #999 solid;
563 @ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565 @ margin: .2em 0 .2em 0;
566 @ float: left;
567 @ clear: left;
568 @ color: #333
569 @ }
570 @
571 @ /* The footer at the very bottom of the page */
572 @ div.footer {
573 @ font-size: 0.8em;
574 @ margin-top: 12px;
575 @ padding: 5px 10px 5px 10px;
576 @ text-align: right;
577 @ background-color: #eee;
578 @ color: #555;
579 @ }
580 @
581 @ /* <verbatim> blocks */
582 @ pre.verbatim {
583 @ background-color: #f5f5f5;
584 @ padding: 0.5em;
585 @ }
586 @
587 @ /* The label/value pairs on (for example) the ci page */
588 @ table.label-value th {
589 @ vertical-align: top;
590 @ text-align: right;
591 @ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600 @ </head>
601 @ <body>
602 @ <div class="header">
603 @ <div class="logo">
604 @ <!-- <img src="$baseurl/logo" alt="logo"> -->
605 @ <br><nobr>$<project_name></nobr>
606 @ </div>
607 @ <div class="title">$<title></div>
608 @ <div class="status"><nobr><th1>
609 @ if {[info exists login]} {
610 @ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732 db_begin_transaction();
733
734 /* Process requests to delete a user-defined skin */
735 if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736 style_header("Confirm Custom Skin Delete");
737 @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
738 @ <p>Deletion of a custom skin is a permanent action that cannot
739 @ be undone. Please confirm that this is what you want to do:</p>
740 @ <input type="hidden" name="sn" value="%h(P("sn"))">
741 @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
742 @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
743 login_insert_csrf_secret();
744 @ </form>
745 style_footer();
746 return;
747 }
748 if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800 }
801 }
802
803 style_header("Skins");
804 @ <p>A "skin" is a combination of
805 @ <a href="setup_editcss">CSS</a>,
806 @ <a href="setup_header">Header</a>,
807 @ <a href="setup_footer">Footer</a>, and
808 @ <a href="setup_logo">Logo</a> that determines the look and feel
809 @ of the web interface.</p>
810 @
@@ -813,15 +813,15 @@
813 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814 z = aBuiltinSkin[i].zName;
815 if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817 }else{
818 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
819 @ %h(z).&nbsp;&nbsp;
820 @ <input type="hidden" name="sn" value="%h(z)">
821 @ <input type="submit" name="load" value="Use This Skin">
822 @ </form></li>
823 }
824 }
825 db_prepare(&q,
826 "SELECT substr(name, 6), value FROM config"
827 " WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832 const char *zV = db_column_text(&q, 1);
833 if( strcmp(zV, zCurrent)==0 ){
834 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835 }else{
836 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837 @ %h(zN).&nbsp;&nbsp;
838 @ <input type="hidden" name="sn" value="%h(zN)">
839 @ <input type="submit" name="load" value="Use This Skin">
840 @ <input type="submit" name="del1" value="Delete This Skin">
841 @ </form></li>
842 }
843
--- src/skins.c
+++ src/skins.c
@@ -24,19 +24,19 @@
24 /* @-comment: // */
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
31 @ body {
32 @ margin: 0ex 1ex;
33 @ padding: 0px;
34 @ background-color: white;
35 @ font-family: sans-serif;
36 @ }
37 @
38 @ /* The project logo in the upper left-hand corner of each page */
39 @ div.logo {
40 @ display: table-row;
41 @ text-align: center;
42 @ /* vertical-align: bottom;*/
@@ -44,11 +44,11 @@
44 @ font-weight: bold;
45 @ background-color: #707070;
46 @ color: #ffffff;
47 @ min-width: 200px;
48 @ }
49 @
50 @ /* The page title centered at the top of each page */
51 @ div.title {
52 @ display: table-cell;
53 @ font-size: 1.5em;
54 @ font-weight: bold;
@@ -56,11 +56,11 @@
56 @ padding: 0 0 0 10px;
57 @ color: #404040;
58 @ vertical-align: bottom;
59 @ width: 100%;
60 @ }
61 @
62 @ /* The login status message in the top right-hand corner */
63 @ div.status {
64 @ display: table-cell;
65 @ text-align: right;
66 @ vertical-align: bottom;
@@ -67,17 +67,17 @@
67 @ color: #404040;
68 @ font-size: 0.8em;
69 @ font-weight: bold;
70 @ min-width: 200px;
71 @ }
72 @
73 @ /* The header across the top of the page */
74 @ div.header {
75 @ display: table;
76 @ width: 100%;
77 @ }
78 @
79 @ /* The main menu bar that appears at the top of the page beneath
80 @ ** the header */
81 @ div.mainmenu {
82 @ padding: 5px 10px 5px 10px;
83 @ font-size: 0.9em;
@@ -85,11 +85,11 @@
85 @ text-align: center;
86 @ letter-spacing: 1px;
87 @ background-color: #404040;
88 @ color: white;
89 @ }
90 @
91 @ /* The submenu bar that *sometimes* appears below the main menu */
92 @ div.submenu {
93 @ padding: 3px 10px 3px 0px;
94 @ font-size: 0.9em;
95 @ text-align: center;
@@ -103,21 +103,21 @@
103 @ }
104 @ div.mainmenu a:hover, div.submenu a:hover {
105 @ color: #404040;
106 @ background-color: white;
107 @ }
108 @
109 @ /* All page content from the bottom of the menu or submenu down to
110 @ ** the footer */
111 @ div.content {
112 @ padding: 0ex 0ex 0ex 0ex;
113 @ }
114 @ /* Hyperlink colors */
115 @ div.content a { color: #604000; }
116 @ div.content a:link { color: #604000;}
117 @ div.content a:visited { color: #600000; }
118 @
119 @ /* Some pages have section dividers */
120 @ div.section {
121 @ margin-bottom: 0px;
122 @ margin-top: 1em;
123 @ padding: 1px 1px 1px 1px;
@@ -124,11 +124,11 @@
124 @ font-size: 1.2em;
125 @ font-weight: bold;
126 @ background-color: #404040;
127 @ color: white;
128 @ }
129 @
130 @ /* The "Date" that occurs on the left hand side of timelines */
131 @ div.divider {
132 @ background: #a0a0a0;
133 @ border: 2px #505050 solid;
134 @ font-size: 1em; font-weight: normal;
@@ -135,21 +135,21 @@
135 @ padding: .25em;
136 @ margin: .2em 0 .2em 0;
137 @ float: left;
138 @ clear: left;
139 @ }
140 @
141 @ /* The footer at the very bottom of the page */
142 @ div.footer {
143 @ font-size: 0.8em;
144 @ margin-top: 12px;
145 @ padding: 5px 10px 5px 10px;
146 @ text-align: right;
147 @ background-color: #404040;
148 @ color: white;
149 @ }
150 @
151 @ /* The label/value pairs on (for example) the vinfo page */
152 @ table.label-value th {
153 @ vertical-align: top;
154 @ text-align: right;
155 @ padding: 0.2ex 2ex;
@@ -165,11 +165,11 @@
165 @ <body>
166 @ <div class="header">
167 @ <div class="logo">
168 @ <img src="$baseurl/logo" alt="logo">
169 @ </div>
170 @ <div class="title"><small>$<project_name></small><br />$<title></div>
171 @ <div class="status"><nobr><th1>
172 @ if {[info exists login]} {
173 @ puts "Logged in as $login"
174 @ } else {
175 @ puts "Not logged in"
@@ -206,39 +206,39 @@
206 @ html "<a href=''$baseurl/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config VALUES('footer','<div class="footer">
211 @ Fossil version $manifest_version $manifest_date
212 @ </div>
213 @ </body></html>
214 @ ');
215 ;
216
217 /*
218 ** A tan theme with the project title above the user identification
219 ** and no logo image.
220 */
221 static const char zBuiltinSkin2[] =
222 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223 @ body {
224 @ margin: 0ex 0ex;
225 @ padding: 0px;
226 @ background-color: #fef3bc;
227 @ font-family: sans-serif;
228 @ }
229 @
230 @ /* The project logo in the upper left-hand corner of each page */
231 @ div.logo {
232 @ display: inline;
233 @ text-align: center;
234 @ vertical-align: bottom;
235 @ font-weight: bold;
236 @ font-size: 2.5em;
237 @ color: #a09048;
238 @ }
239 @
240 @ /* The page title centered at the top of each page */
241 @ div.title {
242 @ display: table-cell;
243 @ font-size: 2em;
244 @ font-weight: bold;
@@ -246,11 +246,11 @@
246 @ padding: 0 0 0 5px;
247 @ color: #a09048;
248 @ vertical-align: bottom;
249 @ width: 100%;
250 @ }
251 @
252 @ /* The login status message in the top right-hand corner */
253 @ div.status {
254 @ display: table-cell;
255 @ text-align: right;
256 @ vertical-align: bottom;
@@ -257,17 +257,17 @@
257 @ color: #a09048;
258 @ padding: 5px 5px 0 0;
259 @ font-size: 0.8em;
260 @ font-weight: bold;
261 @ }
262 @
263 @ /* The header across the top of the page */
264 @ div.header {
265 @ display: table;
266 @ width: 100%;
267 @ }
268 @
269 @ /* The main menu bar that appears at the top of the page beneath
270 @ ** the header */
271 @ div.mainmenu {
272 @ padding: 5px 10px 5px 10px;
273 @ font-size: 0.9em;
@@ -275,11 +275,11 @@
275 @ text-align: center;
276 @ letter-spacing: 1px;
277 @ background-color: #a09048;
278 @ color: black;
279 @ }
280 @
281 @ /* The submenu bar that *sometimes* appears below the main menu */
282 @ div.submenu {
283 @ padding: 3px 10px 3px 0px;
284 @ font-size: 0.9em;
285 @ text-align: center;
@@ -293,21 +293,21 @@
293 @ }
294 @ div.mainmenu a:hover, div.submenu a:hover {
295 @ color: #a09048;
296 @ background-color: white;
297 @ }
298 @
299 @ /* All page content from the bottom of the menu or submenu down to
300 @ ** the footer */
301 @ div.content {
302 @ padding: 1ex 5px;
303 @ }
304 @ div.content a { color: #706532; }
305 @ div.content a:link { color: #706532; }
306 @ div.content a:visited { color: #704032; }
307 @ div.content a:hover { background-color: white; color: #706532; }
308 @
309 @ /* Some pages have section dividers */
310 @ div.section {
311 @ margin-bottom: 0px;
312 @ margin-top: 1em;
313 @ padding: 3px 3px 0 3px;
@@ -314,11 +314,11 @@
314 @ font-size: 1.2em;
315 @ font-weight: bold;
316 @ background-color: #a09048;
317 @ color: white;
318 @ }
319 @
320 @ /* The "Date" that occurs on the left hand side of timelines */
321 @ div.divider {
322 @ background: #e1d498;
323 @ border: 2px #a09048 solid;
324 @ font-size: 1em; font-weight: normal;
@@ -325,33 +325,33 @@
325 @ padding: .25em;
326 @ margin: .2em 0 .2em 0;
327 @ float: left;
328 @ clear: left;
329 @ }
330 @
331 @ /* The footer at the very bottom of the page */
332 @ div.footer {
333 @ font-size: 0.8em;
334 @ margin-top: 12px;
335 @ padding: 5px 10px 5px 10px;
336 @ text-align: right;
337 @ background-color: #a09048;
338 @ color: white;
339 @ }
340 @
341 @ /* Hyperlink colors */
342 @ div.footer a { color: white; }
343 @ div.footer a:link { color: white; }
344 @ div.footer a:visited { color: white; }
345 @ div.footer a:hover { background-color: white; color: #558195; }
346 @
347 @ /* <verbatim> blocks */
348 @ pre.verbatim {
349 @ background-color: #f5f5f5;
350 @ padding: 0.5em;
351 @ }
352 @
353 @ /* The label/value pairs on (for example) the ci page */
354 @ table.label-value th {
355 @ vertical-align: top;
356 @ text-align: right;
357 @ padding: 0.2ex 2ex;
@@ -418,37 +418,37 @@
418
419 /*
420 ** Black letters on a white or cream background with the main menu
421 ** stuck on the left-hand side.
422 */
423 static const char zBuiltinSkin3[] =
424 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
425 @ body {
426 @ margin:0px 0px 0px 0px;
427 @ padding:0px;
428 @ font-family:verdana, arial, helvetica, "sans serif";
429 @ color:#333;
430 @ background-color:white;
431 @ }
432 @
433 @ /* consistent colours */
434 @ h2 {
435 @ color: #333;
436 @ }
437 @ h3 {
438 @ color: #333;
439 @ }
440 @
441 @ /* The project logo in the upper left-hand corner of each page */
442 @ div.logo {
443 @ display: table-cell;
444 @ text-align: left;
445 @ vertical-align: bottom;
446 @ font-weight: bold;
447 @ color: #333;
448 @ }
449 @
450 @ /* The page title centered at the top of each page */
451 @ div.title {
452 @ display: table-cell;
453 @ font-size: 2em;
454 @ font-weight: bold;
@@ -455,11 +455,11 @@
455 @ text-align: center;
456 @ color: #333;
457 @ vertical-align: bottom;
458 @ width: 100%;
459 @ }
460 @
461 @ /* The login status message in the top right-hand corner */
462 @ div.status {
463 @ display: table-cell;
464 @ padding-right: 10px;
465 @ text-align: right;
@@ -467,21 +467,21 @@
467 @ padding-bottom: 5px;
468 @ color: #333;
469 @ font-size: 0.8em;
470 @ font-weight: bold;
471 @ }
472 @
473 @ /* The header across the top of the page */
474 @ div.header {
475 @ margin:10px 0px 10px 0px;
476 @ padding:1px 0px 0px 20px;
477 @ border-style:solid;
478 @ border-color:black;
479 @ border-width:1px 0px;
480 @ background-color:#eee;
481 @ }
482 @
483 @ /* The main menu bar that appears at the top left of the page beneath
484 @ ** the header. Width must be co-ordinated with the container below */
485 @ div.mainmenu {
486 @ float: left;
487 @ margin-left: 10px;
@@ -491,11 +491,11 @@
491 @ padding:5px;
492 @ background-color:#eee;
493 @ border:1px solid #999;
494 @ width:8em;
495 @ }
496 @
497 @ /* Main menu is now a list */
498 @ div.mainmenu ul {
499 @ padding: 0;
500 @ list-style:none;
501 @ }
@@ -506,17 +506,17 @@
506 @ }
507 @ div.mainmenu a:hover {
508 @ color: #eee;
509 @ background-color: #333;
510 @ }
511 @
512 @ /* Container for the sub-menu and content so they don''t spread
513 @ ** out underneath the main menu */
514 @ #container {
515 @ padding-left: 9em;
516 @ }
517 @
518 @ /* The submenu bar that *sometimes* appears below the main menu */
519 @ div.submenu {
520 @ padding: 3px 10px 3px 10px;
521 @ font-size: 0.9em;
522 @ text-align: center;
@@ -532,18 +532,18 @@
532 @ }
533 @ div.submenu a:hover {
534 @ color: #eee;
535 @ background-color: #333;
536 @ }
537 @
538 @ /* All page content from the bottom of the menu or submenu down to
539 @ ** the footer */
540 @ div.content {
541 @ float right;
542 @ padding: 2ex 1ex 0ex 2ex;
543 @ }
544 @
545 @ /* Some pages have section dividers */
546 @ div.section {
547 @ margin-bottom: 0px;
548 @ margin-top: 1em;
549 @ padding: 1px 1px 1px 1px;
@@ -553,11 +553,11 @@
553 @ border-color:#999;
554 @ border-width:1px 0px;
555 @ background-color: #eee;
556 @ color: #333;
557 @ }
558 @
559 @ /* The "Date" that occurs on the left hand side of timelines */
560 @ div.divider {
561 @ background: #eee;
562 @ border: 2px #999 solid;
563 @ font-size: 1em; font-weight: normal;
@@ -565,27 +565,27 @@
565 @ margin: .2em 0 .2em 0;
566 @ float: left;
567 @ clear: left;
568 @ color: #333
569 @ }
570 @
571 @ /* The footer at the very bottom of the page */
572 @ div.footer {
573 @ font-size: 0.8em;
574 @ margin-top: 12px;
575 @ padding: 5px 10px 5px 10px;
576 @ text-align: right;
577 @ background-color: #eee;
578 @ color: #555;
579 @ }
580 @
581 @ /* <verbatim> blocks */
582 @ pre.verbatim {
583 @ background-color: #f5f5f5;
584 @ padding: 0.5em;
585 @ }
586 @
587 @ /* The label/value pairs on (for example) the ci page */
588 @ table.label-value th {
589 @ vertical-align: top;
590 @ text-align: right;
591 @ padding: 0.2ex 2ex;
@@ -600,11 +600,11 @@
600 @ </head>
601 @ <body>
602 @ <div class="header">
603 @ <div class="logo">
604 @ <!-- <img src="$baseurl/logo" alt="logo"> -->
605 @ <br /><nobr>$<project_name></nobr>
606 @ </div>
607 @ <div class="title">$<title></div>
608 @ <div class="status"><nobr><th1>
609 @ if {[info exists login]} {
610 @ puts "Logged in as $login"
@@ -732,18 +732,18 @@
732 db_begin_transaction();
733
734 /* Process requests to delete a user-defined skin */
735 if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
736 style_header("Confirm Custom Skin Delete");
737 @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
738 @ <p>Deletion of a custom skin is a permanent action that cannot
739 @ be undone. Please confirm that this is what you want to do:</p>
740 @ <input type="hidden" name="sn" value="%h(P("sn"))" />
741 @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
742 @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
743 login_insert_csrf_secret();
744 @ </div></form>
745 style_footer();
746 return;
747 }
748 if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
749 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
@@ -800,11 +800,11 @@
800 }
801 }
802
803 style_header("Skins");
804 @ <p>A "skin" is a combination of
805 @ <a href="setup_editcss">CSS</a>,
806 @ <a href="setup_header">Header</a>,
807 @ <a href="setup_footer">Footer</a>, and
808 @ <a href="setup_logo">Logo</a> that determines the look and feel
809 @ of the web interface.</p>
810 @
@@ -813,15 +813,15 @@
813 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
814 z = aBuiltinSkin[i].zName;
815 if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
816 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
817 }else{
818 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
819 @ %h(z).&nbsp;&nbsp;
820 @ <input type="hidden" name="sn" value="%h(z)" />
821 @ <input type="submit" name="load" value="Use This Skin" />
822 @ </div></form></li>
823 }
824 }
825 db_prepare(&q,
826 "SELECT substr(name, 6), value FROM config"
827 " WHERE name GLOB 'skin:*'"
@@ -832,11 +832,11 @@
832 const char *zV = db_column_text(&q, 1);
833 if( strcmp(zV, zCurrent)==0 ){
834 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
835 }else{
836 @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
837 @ %h(zN).&nbsp;&nbsp;
838 @ <input type="hidden" name="sn" value="%h(zN)">
839 @ <input type="submit" name="load" value="Use This Skin">
840 @ <input type="submit" name="del1" value="Delete This Skin">
841 @ </form></li>
842 }
843
+1948 -1245
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
11
/******************************************************************************
22
** This file is an amalgamation of many separate C source files from SQLite
3
-** version 3.7.1. By combining all the individual C code files into this
3
+** version 3.7.2. By combining all the individual C code files into this
44
** single large file, the entire code can be compiled as a one translation
55
** unit. This allows many compilers to do optimizations that would not be
66
** possible if the files were compiled separately. Performance improvements
77
** of 5% are more are commonly seen when SQLite is compiled as a single
88
** translation unit.
@@ -213,24 +213,25 @@
213213
*/
214214
#ifndef SQLITE_MAX_VARIABLE_NUMBER
215215
# define SQLITE_MAX_VARIABLE_NUMBER 999
216216
#endif
217217
218
-/* Maximum page size. The upper bound on this value is 32768. This a limit
219
-** imposed by the necessity of storing the value in a 2-byte unsigned integer
220
-** and the fact that the page size must be a power of 2.
218
+/* Maximum page size. The upper bound on this value is 65536. This a limit
219
+** imposed by the use of 16-bit offsets within each page.
221220
**
222
-** If this limit is changed, then the compiled library is technically
223
-** incompatible with an SQLite library compiled with a different limit. If
224
-** a process operating on a database with a page-size of 65536 bytes
225
-** crashes, then an instance of SQLite compiled with the default page-size
226
-** limit will not be able to rollback the aborted transaction. This could
227
-** lead to database corruption.
221
+** Earlier versions of SQLite allowed the user to change this value at
222
+** compile time. This is no longer permitted, on the grounds that it creates
223
+** a library that is technically incompatible with an SQLite library
224
+** compiled with a different limit. If a process operating on a database
225
+** with a page-size of 65536 bytes crashes, then an instance of SQLite
226
+** compiled with the default page-size limit will not be able to rollback
227
+** the aborted transaction. This could lead to database corruption.
228228
*/
229
-#ifndef SQLITE_MAX_PAGE_SIZE
230
-# define SQLITE_MAX_PAGE_SIZE 32768
229
+#ifdef SQLITE_MAX_PAGE_SIZE
230
+# undef SQLITE_MAX_PAGE_SIZE
231231
#endif
232
+#define SQLITE_MAX_PAGE_SIZE 65536
232233
233234
234235
/*
235236
** The default size of a database page.
236237
*/
@@ -631,23 +632,23 @@
631632
** be held constant and Z will be incremented or else Y will be incremented
632633
** and Z will be reset to zero.
633634
**
634635
** Since version 3.6.18, SQLite source code has been stored in the
635636
** <a href="http://www.fossil-scm.org/">Fossil configuration management
636
-** system</a>. ^The SQLITE_SOURCE_ID macro evalutes to
637
+** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
637638
** a string which identifies a particular check-in of SQLite
638639
** within its configuration management system. ^The SQLITE_SOURCE_ID
639640
** string contains the date and time of the check-in (UTC) and an SHA1
640641
** hash of the entire source tree.
641642
**
642643
** See also: [sqlite3_libversion()],
643644
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
644645
** [sqlite_version()] and [sqlite_source_id()].
645646
*/
646
-#define SQLITE_VERSION "3.7.1"
647
-#define SQLITE_VERSION_NUMBER 3007001
648
-#define SQLITE_SOURCE_ID "2010-08-05 03:21:40 fbe70e1106bcc5086ceb9d8f39cc39baf3643092"
647
+#define SQLITE_VERSION "3.7.2"
648
+#define SQLITE_VERSION_NUMBER 3007002
649
+#define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3"
649650
650651
/*
651652
** CAPI3REF: Run-Time Library Version Numbers
652653
** KEYWORDS: sqlite3_version, sqlite3_sourceid
653654
**
@@ -688,19 +689,19 @@
688689
** ^The sqlite3_compileoption_used() function returns 0 or 1
689690
** indicating whether the specified option was defined at
690691
** compile time. ^The SQLITE_ prefix may be omitted from the
691692
** option name passed to sqlite3_compileoption_used().
692693
**
693
-** ^The sqlite3_compileoption_get() function allows interating
694
+** ^The sqlite3_compileoption_get() function allows iterating
694695
** over the list of options that were defined at compile time by
695696
** returning the N-th compile time option string. ^If N is out of range,
696697
** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
697698
** prefix is omitted from any strings returned by
698699
** sqlite3_compileoption_get().
699700
**
700701
** ^Support for the diagnostic functions sqlite3_compileoption_used()
701
-** and sqlite3_compileoption_get() may be omitted by specifing the
702
+** and sqlite3_compileoption_get() may be omitted by specifying the
702703
** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
703704
**
704705
** See also: SQL functions [sqlite_compileoption_used()] and
705706
** [sqlite_compileoption_get()] and the [compile_options pragma].
706707
*/
@@ -802,11 +803,11 @@
802803
/*
803804
** CAPI3REF: Closing A Database Connection
804805
**
805806
** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
806807
** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
807
-** successfullly destroyed and all associated resources are deallocated.
808
+** successfully destroyed and all associated resources are deallocated.
808809
**
809810
** Applications must [sqlite3_finalize | finalize] all [prepared statements]
810811
** and [sqlite3_blob_close | close] all [BLOB handles] associated with
811812
** the [sqlite3] object prior to attempting to close the object. ^If
812813
** sqlite3_close() is called on a [database connection] that still has
@@ -1229,16 +1230,25 @@
12291230
** layer a hint of how large the database file will grow to be during the
12301231
** current transaction. This hint is not guaranteed to be accurate but it
12311232
** is often close. The underlying VFS might choose to preallocate database
12321233
** file space based on this hint in order to help writes to the database
12331234
** file run faster.
1235
+**
1236
+** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
1237
+** extends and truncates the database file in chunks of a size specified
1238
+** by the user. The fourth argument to [sqlite3_file_control()] should
1239
+** point to an integer (type int) containing the new chunk-size to use
1240
+** for the nominated database. Allocating database file space in large
1241
+** chunks (say 1MB at a time), may reduce file-system fragmentation and
1242
+** improve performance on some systems.
12341243
*/
12351244
#define SQLITE_FCNTL_LOCKSTATE 1
12361245
#define SQLITE_GET_LOCKPROXYFILE 2
12371246
#define SQLITE_SET_LOCKPROXYFILE 3
12381247
#define SQLITE_LAST_ERRNO 4
12391248
#define SQLITE_FCNTL_SIZE_HINT 5
1249
+#define SQLITE_FCNTL_CHUNK_SIZE 6
12401250
12411251
/*
12421252
** CAPI3REF: Mutex Handle
12431253
**
12441254
** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -3197,11 +3207,11 @@
31973207
** <li> @VVV
31983208
** <li> $VVV
31993209
** </ul>
32003210
**
32013211
** In the templates above, NNN represents an integer literal,
3202
-** and VVV represents an alphanumeric identifer.)^ ^The values of these
3212
+** and VVV represents an alphanumeric identifier.)^ ^The values of these
32033213
** parameters (also called "host parameter names" or "SQL parameters")
32043214
** can be set using the sqlite3_bind_*() routines defined here.
32053215
**
32063216
** ^The first argument to the sqlite3_bind_*() routines is always
32073217
** a pointer to the [sqlite3_stmt] object returned from
@@ -3976,11 +3986,11 @@
39763986
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
39773987
39783988
/*
39793989
** CAPI3REF: Obtain Aggregate Function Context
39803990
**
3981
-** Implementions of aggregate SQL functions use this
3991
+** Implementations of aggregate SQL functions use this
39823992
** routine to allocate memory for storing their state.
39833993
**
39843994
** ^The first time the sqlite3_aggregate_context(C,N) routine is called
39853995
** for a particular aggregate function, SQLite
39863996
** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -4248,11 +4258,11 @@
42484258
** the routine expects pointers to 16-bit word aligned strings
42494259
** of UTF-16 in the native byte order.
42504260
**
42514261
** A pointer to the user supplied routine must be passed as the fifth
42524262
** argument. ^If it is NULL, this is the same as deleting the collation
4253
-** sequence (so that SQLite cannot call it anymore).
4263
+** sequence (so that SQLite cannot call it any more).
42544264
** ^Each time the application supplied function is invoked, it is passed
42554265
** as its first parameter a copy of the void* passed as the fourth argument
42564266
** to sqlite3_create_collation() or sqlite3_create_collation16().
42574267
**
42584268
** ^The remaining arguments to the application-supplied routine are two strings,
@@ -5466,11 +5476,11 @@
54665476
** of passing a NULL pointer instead of a valid mutex handle are undefined
54675477
** (i.e. it is acceptable to provide an implementation that segfaults if
54685478
** it is passed a NULL pointer).
54695479
**
54705480
** The xMutexInit() method must be threadsafe. ^It must be harmless to
5471
-** invoke xMutexInit() mutiple times within the same process and without
5481
+** invoke xMutexInit() multiple times within the same process and without
54725482
** intervening calls to xMutexEnd(). Second and subsequent calls to
54735483
** xMutexInit() must be no-ops.
54745484
**
54755485
** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
54765486
** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5636,11 +5646,11 @@
56365646
56375647
/*
56385648
** CAPI3REF: SQLite Runtime Status
56395649
**
56405650
** ^This interface is used to retrieve runtime status information
5641
-** about the preformance of SQLite, and optionally to reset various
5651
+** about the performance of SQLite, and optionally to reset various
56425652
** highwater marks. ^The first argument is an integer code for
56435653
** the specific parameter to measure. ^(Recognized integer codes
56445654
** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
56455655
** ^The current value of the parameter is returned into *pCurrent.
56465656
** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5762,11 +5772,11 @@
57625772
** ^This interface is used to retrieve runtime status information
57635773
** about a single [database connection]. ^The first argument is the
57645774
** database connection object to be interrogated. ^The second argument
57655775
** is an integer constant, taken from the set of
57665776
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5767
-** determiness the parameter to interrogate. The set of
5777
+** determines the parameter to interrogate. The set of
57685778
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
57695779
** to grow in future releases of SQLite.
57705780
**
57715781
** ^The current value of the requested parameter is written into *pCur
57725782
** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -6184,11 +6194,11 @@
61846194
**
61856195
** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
61866196
**
61876197
** ^Each call to sqlite3_backup_step() sets two values inside
61886198
** the [sqlite3_backup] object: the number of pages still to be backed
6189
-** up and the total number of pages in the source databae file.
6199
+** up and the total number of pages in the source database file.
61906200
** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
61916201
** retrieve these two values, respectively.
61926202
**
61936203
** ^The values returned by these functions are only updated by
61946204
** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -6280,11 +6290,11 @@
62806290
** ^(There may be at most one unlock-notify callback registered by a
62816291
** blocked connection. If sqlite3_unlock_notify() is called when the
62826292
** blocked connection already has a registered unlock-notify callback,
62836293
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
62846294
** called with a NULL pointer as its second argument, then any existing
6285
-** unlock-notify callback is cancelled. ^The blocked connections
6295
+** unlock-notify callback is canceled. ^The blocked connections
62866296
** unlock-notify callback may also be canceled by closing the blocked
62876297
** connection using [sqlite3_close()].
62886298
**
62896299
** The unlock-notify callback is not reentrant. If an application invokes
62906300
** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -6362,11 +6372,11 @@
63626372
/*
63636373
** CAPI3REF: String Comparison
63646374
**
63656375
** ^The [sqlite3_strnicmp()] API allows applications and extensions to
63666376
** compare the contents of two buffers containing UTF-8 strings in a
6367
-** case-indendent fashion, using the same definition of case independence
6377
+** case-independent fashion, using the same definition of case independence
63686378
** that SQLite uses internally when comparing identifiers.
63696379
*/
63706380
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
63716381
63726382
/*
@@ -7868,11 +7878,11 @@
78687878
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager);
78697879
SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
78707880
78717881
/* Functions used to configure a Pager object. */
78727882
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
7873
-SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*, int);
7883
+SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
78747884
SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
78757885
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
78767886
SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int);
78777887
SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
78787888
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
@@ -7895,11 +7905,11 @@
78957905
SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*);
78967906
SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *);
78977907
SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *);
78987908
78997909
/* Functions used to manage pager transactions and savepoints. */
7900
-SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*, int*);
7910
+SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*);
79017911
SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
79027912
SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
79037913
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
79047914
SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager);
79057915
SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
@@ -9183,13 +9193,13 @@
91839193
** argument to sqlite3VdbeKeyCompare and is used to control the
91849194
** comparison of the two index keys.
91859195
*/
91869196
struct KeyInfo {
91879197
sqlite3 *db; /* The database connection */
9188
- u8 enc; /* Text encoding - one of the TEXT_Utf* values */
9198
+ u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
91899199
u16 nField; /* Number of entries in aColl[] */
9190
- u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */
9200
+ u8 *aSortOrder; /* Sort order for each column. May be NULL */
91919201
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
91929202
};
91939203
91949204
/*
91959205
** An instance of the following structure holds information about a
@@ -14331,11 +14341,11 @@
1433114341
1433214342
/*
1433314343
** Set the "type" of an allocation.
1433414344
*/
1433514345
SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
14336
- if( p ){
14346
+ if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
1433714347
struct MemBlockHdr *pHdr;
1433814348
pHdr = sqlite3MemsysGetHeader(p);
1433914349
assert( pHdr->iForeGuard==FOREGUARD );
1434014350
pHdr->eType = eType;
1434114351
}
@@ -14350,11 +14360,11 @@
1435014360
**
1435114361
** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
1435214362
*/
1435314363
SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
1435414364
int rc = 1;
14355
- if( p ){
14365
+ if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
1435614366
struct MemBlockHdr *pHdr;
1435714367
pHdr = sqlite3MemsysGetHeader(p);
1435814368
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
1435914369
if( (pHdr->eType&eType)==0 ){
1436014370
rc = 0;
@@ -14372,11 +14382,11 @@
1437214382
**
1437314383
** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
1437414384
*/
1437514385
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
1437614386
int rc = 1;
14377
- if( p ){
14387
+ if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
1437814388
struct MemBlockHdr *pHdr;
1437914389
pHdr = sqlite3MemsysGetHeader(p);
1438014390
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
1438114391
if( (pHdr->eType&eType)!=0 ){
1438214392
rc = 0;
@@ -16624,10 +16634,11 @@
1662416634
#else
1662516635
/* Use the built-in recursive mutexes if they are available.
1662616636
*/
1662716637
pthread_mutex_lock(&p->mutex);
1662816638
#if SQLITE_MUTEX_NREF
16639
+ assert( p->nRef>0 || p->owner==0 );
1662916640
p->owner = pthread_self();
1663016641
p->nRef++;
1663116642
#endif
1663216643
#endif
1663316644
@@ -16696,10 +16707,11 @@
1669616707
*/
1669716708
static void pthreadMutexLeave(sqlite3_mutex *p){
1669816709
assert( pthreadMutexHeld(p) );
1669916710
#if SQLITE_MUTEX_NREF
1670016711
p->nRef--;
16712
+ if( p->nRef==0 ) p->owner = 0;
1670116713
#endif
1670216714
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
1670316715
1670416716
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
1670516717
if( p->nRef==0 ){
@@ -16960,11 +16972,11 @@
1696016972
** allocated mutex. SQLite is careful to deallocate every
1696116973
** mutex that it allocates.
1696216974
*/
1696316975
static void winMutexFree(sqlite3_mutex *p){
1696416976
assert( p );
16965
- assert( p->nRef==0 );
16977
+ assert( p->nRef==0 && p->owner==0 );
1696616978
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
1696716979
DeleteCriticalSection(&p->mutex);
1696816980
sqlite3_free(p);
1696916981
}
1697016982
@@ -16984,10 +16996,11 @@
1698416996
DWORD tid = GetCurrentThreadId();
1698516997
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
1698616998
#endif
1698716999
EnterCriticalSection(&p->mutex);
1698817000
#ifdef SQLITE_DEBUG
17001
+ assert( p->nRef>0 || p->owner==0 );
1698917002
p->owner = tid;
1699017003
p->nRef++;
1699117004
if( p->trace ){
1699217005
printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
1699317006
}
@@ -17037,10 +17050,11 @@
1703717050
#ifndef NDEBUG
1703817051
DWORD tid = GetCurrentThreadId();
1703917052
assert( p->nRef>0 );
1704017053
assert( p->owner==tid );
1704117054
p->nRef--;
17055
+ if( p->nRef==0 ) p->owner = 0;
1704217056
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
1704317057
#endif
1704417058
LeaveCriticalSection(&p->mutex);
1704517059
#ifdef SQLITE_DEBUG
1704617060
if( p->trace ){
@@ -22609,10 +22623,11 @@
2260922623
void *lockingContext; /* Locking style specific state */
2261022624
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
2261122625
int fileFlags; /* Miscellanous flags */
2261222626
const char *zPath; /* Name of the file */
2261322627
unixShm *pShm; /* Shared memory segment information */
22628
+ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
2261422629
#if SQLITE_ENABLE_LOCKING_STYLE
2261522630
int openFlags; /* The flags specified at open() */
2261622631
#endif
2261722632
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
2261822633
unsigned fsFlags; /* cached details from statfs() */
@@ -25367,19 +25382,21 @@
2536725382
offset += wrote;
2536825383
pBuf = &((char*)pBuf)[wrote];
2536925384
}
2537025385
SimulateIOError(( wrote=(-1), amt=1 ));
2537125386
SimulateDiskfullError(( wrote=0, amt=1 ));
25387
+
2537225388
if( amt>0 ){
2537325389
if( wrote<0 ){
2537425390
/* lastErrno set by seekAndWrite */
2537525391
return SQLITE_IOERR_WRITE;
2537625392
}else{
2537725393
pFile->lastErrno = 0; /* not a system error */
2537825394
return SQLITE_FULL;
2537925395
}
2538025396
}
25397
+
2538125398
return SQLITE_OK;
2538225399
}
2538325400
2538425401
#ifdef SQLITE_TEST
2538525402
/*
@@ -25577,16 +25594,27 @@
2557725594
2557825595
/*
2557925596
** Truncate an open file to a specified size
2558025597
*/
2558125598
static int unixTruncate(sqlite3_file *id, i64 nByte){
25599
+ unixFile *pFile = (unixFile *)id;
2558225600
int rc;
25583
- assert( id );
25601
+ assert( pFile );
2558425602
SimulateIOError( return SQLITE_IOERR_TRUNCATE );
25585
- rc = ftruncate(((unixFile*)id)->h, (off_t)nByte);
25603
+
25604
+ /* If the user has configured a chunk-size for this file, truncate the
25605
+ ** file so that it consists of an integer number of chunks (i.e. the
25606
+ ** actual file size after the operation may be larger than the requested
25607
+ ** size).
25608
+ */
25609
+ if( pFile->szChunk ){
25610
+ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
25611
+ }
25612
+
25613
+ rc = ftruncate(pFile->h, (off_t)nByte);
2558625614
if( rc ){
25587
- ((unixFile*)id)->lastErrno = errno;
25615
+ pFile->lastErrno = errno;
2558825616
return SQLITE_IOERR_TRUNCATE;
2558925617
}else{
2559025618
#ifndef NDEBUG
2559125619
/* If we are doing a normal write to a database file (as opposed to
2559225620
** doing a hot-journal rollback or a write to some file other than a
@@ -25593,12 +25621,12 @@
2559325621
** normal database file) and we truncate the file to zero length,
2559425622
** that effectively updates the change counter. This might happen
2559525623
** when restoring a database using the backup API from a zero-length
2559625624
** source.
2559725625
*/
25598
- if( ((unixFile*)id)->inNormalWrite && nByte==0 ){
25599
- ((unixFile*)id)->transCntrChng = 1;
25626
+ if( pFile->inNormalWrite && nByte==0 ){
25627
+ pFile->transCntrChng = 1;
2560025628
}
2560125629
#endif
2560225630
2560325631
return SQLITE_OK;
2560425632
}
@@ -25637,10 +25665,58 @@
2563725665
** proxying locking division.
2563825666
*/
2563925667
static int proxyFileControl(sqlite3_file*,int,void*);
2564025668
#endif
2564125669
25670
+/*
25671
+** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
25672
+** file-control operation.
25673
+**
25674
+** If the user has configured a chunk-size for this file, it could be
25675
+** that the file needs to be extended at this point. Otherwise, the
25676
+** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
25677
+*/
25678
+static int fcntlSizeHint(unixFile *pFile, i64 nByte){
25679
+ if( pFile->szChunk ){
25680
+ i64 nSize; /* Required file size */
25681
+ struct stat buf; /* Used to hold return values of fstat() */
25682
+
25683
+ if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
25684
+
25685
+ nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
25686
+ if( nSize>(i64)buf.st_size ){
25687
+#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
25688
+ if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){
25689
+ return SQLITE_IOERR_WRITE;
25690
+ }
25691
+#else
25692
+ /* If the OS does not have posix_fallocate(), fake it. First use
25693
+ ** ftruncate() to set the file size, then write a single byte to
25694
+ ** the last byte in each block within the extended region. This
25695
+ ** is the same technique used by glibc to implement posix_fallocate()
25696
+ ** on systems that do not have a real fallocate() system call.
25697
+ */
25698
+ int nBlk = buf.st_blksize; /* File-system block size */
25699
+ i64 iWrite; /* Next offset to write to */
25700
+ int nWrite; /* Return value from seekAndWrite() */
25701
+
25702
+ if( ftruncate(pFile->h, nSize) ){
25703
+ pFile->lastErrno = errno;
25704
+ return SQLITE_IOERR_TRUNCATE;
25705
+ }
25706
+ iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
25707
+ do {
25708
+ nWrite = seekAndWrite(pFile, iWrite, "", 1);
25709
+ iWrite += nBlk;
25710
+ } while( nWrite==1 && iWrite<nSize );
25711
+ if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
25712
+#endif
25713
+ }
25714
+ }
25715
+
25716
+ return SQLITE_OK;
25717
+}
2564225718
2564325719
/*
2564425720
** Information and control of an open file handle.
2564525721
*/
2564625722
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
@@ -25650,18 +25726,17 @@
2565025726
return SQLITE_OK;
2565125727
}
2565225728
case SQLITE_LAST_ERRNO: {
2565325729
*(int*)pArg = ((unixFile*)id)->lastErrno;
2565425730
return SQLITE_OK;
25731
+ }
25732
+ case SQLITE_FCNTL_CHUNK_SIZE: {
25733
+ ((unixFile*)id)->szChunk = *(int *)pArg;
25734
+ return SQLITE_OK;
2565525735
}
2565625736
case SQLITE_FCNTL_SIZE_HINT: {
25657
-#if 0 /* No performance advantage seen on Linux */
25658
- sqlite3_int64 szFile = *(sqlite3_int64*)pArg;
25659
- unixFile *pFile = (unixFile*)id;
25660
- ftruncate(pFile->h, szFile);
25661
-#endif
25662
- return SQLITE_OK;
25737
+ return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
2566325738
}
2566425739
#ifndef NDEBUG
2566525740
/* The pager calls this method to signal that it has done
2566625741
** a rollback and that the database is therefore unchanged and
2566725742
** it hence it is OK for the transaction change counter to be
@@ -28522,11 +28597,11 @@
2852228597
}else{
2852328598
if( pCtx->conchFile ){
2852428599
pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
2852528600
sqlite3_free(pCtx->conchFile);
2852628601
}
28527
- sqlite3_free(pCtx->lockProxyPath);
28602
+ sqlite3DbFree(0, pCtx->lockProxyPath);
2852828603
sqlite3_free(pCtx->conchFilePath);
2852928604
sqlite3_free(pCtx);
2853028605
}
2853128606
OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
2853228607
(rc==SQLITE_OK ? "ok" : "failed")));
@@ -28713,13 +28788,13 @@
2871328788
}
2871428789
rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
2871528790
if( rc ) return rc;
2871628791
sqlite3_free(conchFile);
2871728792
}
28718
- sqlite3_free(pCtx->lockProxyPath);
28793
+ sqlite3DbFree(0, pCtx->lockProxyPath);
2871928794
sqlite3_free(pCtx->conchFilePath);
28720
- sqlite3_free(pCtx->dbPath);
28795
+ sqlite3DbFree(0, pCtx->dbPath);
2872128796
/* restore the original locking context and pMethod then close it */
2872228797
pFile->lockingContext = pCtx->oldLockingContext;
2872328798
pFile->pMethod = pCtx->pOldMethod;
2872428799
sqlite3_free(pCtx);
2872528800
return pFile->pMethod->xClose(id);
@@ -29161,10 +29236,11 @@
2916129236
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
2916229237
DWORD lastErrno; /* The Windows errno from the last I/O error */
2916329238
DWORD sectorSize; /* Sector size of the device file is on */
2916429239
winShm *pShm; /* Instance of shared memory on this file */
2916529240
const char *zPath; /* Full pathname of this file */
29241
+ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
2916629242
#if SQLITE_OS_WINCE
2916729243
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
2916829244
HANDLE hMutex; /* Mutex used to control access to shared lock */
2916929245
HANDLE hShared; /* Shared memory segment used for locking */
2917029246
winceLock local; /* Locks obtained by this instance of winFile */
@@ -29671,10 +29747,46 @@
2967129747
2967229748
/*****************************************************************************
2967329749
** The next group of routines implement the I/O methods specified
2967429750
** by the sqlite3_io_methods object.
2967529751
******************************************************************************/
29752
+
29753
+/*
29754
+** Some microsoft compilers lack this definition.
29755
+*/
29756
+#ifndef INVALID_SET_FILE_POINTER
29757
+# define INVALID_SET_FILE_POINTER ((DWORD)-1)
29758
+#endif
29759
+
29760
+/*
29761
+** Move the current position of the file handle passed as the first
29762
+** argument to offset iOffset within the file. If successful, return 0.
29763
+** Otherwise, set pFile->lastErrno and return non-zero.
29764
+*/
29765
+static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
29766
+ LONG upperBits; /* Most sig. 32 bits of new offset */
29767
+ LONG lowerBits; /* Least sig. 32 bits of new offset */
29768
+ DWORD dwRet; /* Value returned by SetFilePointer() */
29769
+
29770
+ upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
29771
+ lowerBits = (LONG)(iOffset & 0xffffffff);
29772
+
29773
+ /* API oddity: If successful, SetFilePointer() returns a dword
29774
+ ** containing the lower 32-bits of the new file-offset. Or, if it fails,
29775
+ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
29776
+ ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
29777
+ ** whether an error has actually occured, it is also necessary to call
29778
+ ** GetLastError().
29779
+ */
29780
+ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29781
+ if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
29782
+ pFile->lastErrno = GetLastError();
29783
+ return 1;
29784
+ }
29785
+
29786
+ return 0;
29787
+}
2967629788
2967729789
/*
2967829790
** Close a file.
2967929791
**
2968029792
** It is reported that an attempt to close a handle might sometimes
@@ -29714,17 +29826,10 @@
2971429826
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
2971529827
OpenCounter(-1);
2971629828
return rc ? SQLITE_OK : SQLITE_IOERR;
2971729829
}
2971829830
29719
-/*
29720
-** Some microsoft compilers lack this definition.
29721
-*/
29722
-#ifndef INVALID_SET_FILE_POINTER
29723
-# define INVALID_SET_FILE_POINTER ((DWORD)-1)
29724
-#endif
29725
-
2972629831
/*
2972729832
** Read data from a file into a buffer. Return SQLITE_OK if all
2972829833
** bytes were read successfully and SQLITE_IOERR if anything goes
2972929834
** wrong.
2973029835
*/
@@ -29732,112 +29837,108 @@
2973229837
sqlite3_file *id, /* File to read from */
2973329838
void *pBuf, /* Write content into this buffer */
2973429839
int amt, /* Number of bytes to read */
2973529840
sqlite3_int64 offset /* Begin reading at this offset */
2973629841
){
29737
- LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
29738
- LONG lowerBits = (LONG)(offset & 0xffffffff);
29739
- DWORD rc;
29740
- winFile *pFile = (winFile*)id;
29741
- DWORD error;
29742
- DWORD got;
29842
+ winFile *pFile = (winFile*)id; /* file handle */
29843
+ DWORD nRead; /* Number of bytes actually read from file */
2974329844
2974429845
assert( id!=0 );
2974529846
SimulateIOError(return SQLITE_IOERR_READ);
2974629847
OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
29747
- rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29748
- if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29749
- pFile->lastErrno = error;
29848
+
29849
+ if( seekWinFile(pFile, offset) ){
2975029850
return SQLITE_FULL;
2975129851
}
29752
- if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){
29852
+ if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
2975329853
pFile->lastErrno = GetLastError();
2975429854
return SQLITE_IOERR_READ;
2975529855
}
29756
- if( got==(DWORD)amt ){
29757
- return SQLITE_OK;
29758
- }else{
29856
+ if( nRead<(DWORD)amt ){
2975929857
/* Unread parts of the buffer must be zero-filled */
29760
- memset(&((char*)pBuf)[got], 0, amt-got);
29858
+ memset(&((char*)pBuf)[nRead], 0, amt-nRead);
2976129859
return SQLITE_IOERR_SHORT_READ;
2976229860
}
29861
+
29862
+ return SQLITE_OK;
2976329863
}
2976429864
2976529865
/*
2976629866
** Write data from a buffer into a file. Return SQLITE_OK on success
2976729867
** or some other error code on failure.
2976829868
*/
2976929869
static int winWrite(
29770
- sqlite3_file *id, /* File to write into */
29771
- const void *pBuf, /* The bytes to be written */
29772
- int amt, /* Number of bytes to write */
29773
- sqlite3_int64 offset /* Offset into the file to begin writing at */
29870
+ sqlite3_file *id, /* File to write into */
29871
+ const void *pBuf, /* The bytes to be written */
29872
+ int amt, /* Number of bytes to write */
29873
+ sqlite3_int64 offset /* Offset into the file to begin writing at */
2977429874
){
29775
- LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
29776
- LONG lowerBits = (LONG)(offset & 0xffffffff);
29777
- DWORD rc;
29778
- winFile *pFile = (winFile*)id;
29779
- DWORD error;
29780
- DWORD wrote = 0;
29781
-
29782
- assert( id!=0 );
29875
+ int rc; /* True if error has occured, else false */
29876
+ winFile *pFile = (winFile*)id; /* File handle */
29877
+
29878
+ assert( amt>0 );
29879
+ assert( pFile );
2978329880
SimulateIOError(return SQLITE_IOERR_WRITE);
2978429881
SimulateDiskfullError(return SQLITE_FULL);
29785
- OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
29786
- rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29787
- if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29788
- pFile->lastErrno = error;
29789
- if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29790
- return SQLITE_FULL;
29791
- }else{
29792
- return SQLITE_IOERR_WRITE;
29793
- }
29794
- }
29795
- assert( amt>0 );
29796
- while(
29797
- amt>0
29798
- && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0
29799
- && wrote>0
29800
- ){
29801
- amt -= wrote;
29802
- pBuf = &((char*)pBuf)[wrote];
29803
- }
29804
- if( !rc || amt>(int)wrote ){
29805
- pFile->lastErrno = GetLastError();
29806
- if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29807
- return SQLITE_FULL;
29808
- }else{
29809
- return SQLITE_IOERR_WRITE;
29810
- }
29882
+
29883
+ OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
29884
+
29885
+ rc = seekWinFile(pFile, offset);
29886
+ if( rc==0 ){
29887
+ u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
29888
+ int nRem = amt; /* Number of bytes yet to be written */
29889
+ DWORD nWrite; /* Bytes written by each WriteFile() call */
29890
+
29891
+ while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
29892
+ aRem += nWrite;
29893
+ nRem -= nWrite;
29894
+ }
29895
+ if( nRem>0 ){
29896
+ pFile->lastErrno = GetLastError();
29897
+ rc = 1;
29898
+ }
29899
+ }
29900
+
29901
+ if( rc ){
29902
+ if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29903
+ return SQLITE_FULL;
29904
+ }
29905
+ return SQLITE_IOERR_WRITE;
2981129906
}
2981229907
return SQLITE_OK;
2981329908
}
2981429909
2981529910
/*
2981629911
** Truncate an open file to a specified size
2981729912
*/
2981829913
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
29819
- LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
29820
- LONG lowerBits = (LONG)(nByte & 0xffffffff);
29821
- DWORD dwRet;
29822
- winFile *pFile = (winFile*)id;
29823
- DWORD error;
29824
- int rc = SQLITE_OK;
29825
-
29826
- assert( id!=0 );
29914
+ winFile *pFile = (winFile*)id; /* File handle object */
29915
+ int rc = SQLITE_OK; /* Return code for this function */
29916
+
29917
+ assert( pFile );
29918
+
2982729919
OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
2982829920
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
29829
- dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29830
- if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29831
- pFile->lastErrno = error;
29921
+
29922
+ /* If the user has configured a chunk-size for this file, truncate the
29923
+ ** file so that it consists of an integer number of chunks (i.e. the
29924
+ ** actual file size after the operation may be larger than the requested
29925
+ ** size).
29926
+ */
29927
+ if( pFile->szChunk ){
29928
+ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
29929
+ }
29930
+
29931
+ /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
29932
+ if( seekWinFile(pFile, nByte) ){
2983229933
rc = SQLITE_IOERR_TRUNCATE;
29833
- /* SetEndOfFile will fail if nByte is negative */
29834
- }else if( !SetEndOfFile(pFile->h) ){
29934
+ }else if( 0==SetEndOfFile(pFile->h) ){
2983529935
pFile->lastErrno = GetLastError();
2983629936
rc = SQLITE_IOERR_TRUNCATE;
2983729937
}
29838
- OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed"));
29938
+
29939
+ OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
2983929940
return rc;
2984029941
}
2984129942
2984229943
#ifdef SQLITE_TEST
2984329944
/*
@@ -30197,10 +30298,14 @@
3019730298
return SQLITE_OK;
3019830299
}
3019930300
case SQLITE_LAST_ERRNO: {
3020030301
*(int*)pArg = (int)((winFile*)id)->lastErrno;
3020130302
return SQLITE_OK;
30303
+ }
30304
+ case SQLITE_FCNTL_CHUNK_SIZE: {
30305
+ ((winFile*)id)->szChunk = *(int *)pArg;
30306
+ return SQLITE_OK;
3020230307
}
3020330308
case SQLITE_FCNTL_SIZE_HINT: {
3020430309
sqlite3_int64 sz = *(sqlite3_int64*)pArg;
3020530310
SimulateIOErrorBenign(1);
3020630311
winTruncate(id, sz);
@@ -31749,11 +31854,11 @@
3174931854
** start of a transaction, and is thus usually less than a few thousand,
3175031855
** but can be as large as 2 billion for a really big database.
3175131856
*/
3175231857
3175331858
/* Size of the Bitvec structure in bytes. */
31754
-#define BITVEC_SZ (sizeof(void*)*128) /* 512 on 32bit. 1024 on 64bit */
31859
+#define BITVEC_SZ 512
3175531860
3175631861
/* Round the union size down to the nearest pointer boundary, since that's how
3175731862
** it will be aligned within the Bitvec struct. */
3175831863
#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
3175931864
@@ -32907,10 +33012,29 @@
3290733012
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
3290833013
sqlite3_free(p);
3290933014
}
3291033015
}
3291133016
33017
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
33018
+/*
33019
+** Return the size of a pache allocation
33020
+*/
33021
+static int pcache1MemSize(void *p){
33022
+ assert( sqlite3_mutex_held(pcache1.mutex) );
33023
+ if( p>=pcache1.pStart && p<pcache1.pEnd ){
33024
+ return pcache1.szSlot;
33025
+ }else{
33026
+ int iSize;
33027
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
33028
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
33029
+ iSize = sqlite3MallocSize(p);
33030
+ sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
33031
+ return iSize;
33032
+ }
33033
+}
33034
+#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
33035
+
3291233036
/*
3291333037
** Allocate a new page object initially associated with cache pCache.
3291433038
*/
3291533039
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
3291633040
int nByte = sizeof(PgHdr1) + pCache->szPage;
@@ -33455,11 +33579,11 @@
3345533579
int nFree = 0;
3345633580
if( pcache1.pStart==0 ){
3345733581
PgHdr1 *p;
3345833582
pcache1EnterMutex();
3345933583
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
33460
- nFree += sqlite3MallocSize(PGHDR1_TO_PAGE(p));
33584
+ nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
3346133585
pcache1PinPage(p);
3346233586
pcache1RemoveFromHash(p);
3346333587
pcache1FreePage(p);
3346433588
}
3346533589
pcache1LeaveMutex();
@@ -33964,11 +34088,11 @@
3396434088
# define sqlite3WalOpen(x,y,z) 0
3396534089
# define sqlite3WalClose(w,x,y,z) 0
3396634090
# define sqlite3WalBeginReadTransaction(y,z) 0
3396734091
# define sqlite3WalEndReadTransaction(z)
3396834092
# define sqlite3WalRead(v,w,x,y,z) 0
33969
-# define sqlite3WalDbsize(y,z)
34093
+# define sqlite3WalDbsize(y) 0
3397034094
# define sqlite3WalBeginWriteTransaction(y) 0
3397134095
# define sqlite3WalEndWriteTransaction(x) 0
3397234096
# define sqlite3WalUndo(x,y,z) 0
3397334097
# define sqlite3WalSavepoint(y,z)
3397434098
# define sqlite3WalSavepointUndo(y,z) 0
@@ -34000,13 +34124,12 @@
3400034124
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal);
3400134125
3400234126
/* Read a page from the write-ahead log, if it is present. */
3400334127
SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
3400434128
34005
-/* Return the size of the database as it existed at the beginning
34006
-** of the snapshot */
34007
-SQLITE_PRIVATE void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno);
34129
+/* If the WAL is not empty, return the size of the database. */
34130
+SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal);
3400834131
3400934132
/* Obtain or release the WRITER lock. */
3401034133
SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal);
3401134134
SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal);
3401234135
@@ -34048,12 +34171,16 @@
3404834171
#endif /* _WAL_H_ */
3404934172
3405034173
/************** End of wal.h *************************************************/
3405134174
/************** Continuing where we left off in pager.c **********************/
3405234175
34053
-/*
34054
-******************** NOTES ON THE DESIGN OF THE PAGER ************************
34176
+
34177
+/******************* NOTES ON THE DESIGN OF THE PAGER ************************
34178
+**
34179
+** This comment block describes invariants that hold when using a rollback
34180
+** journal. These invariants do not apply for journal_mode=WAL,
34181
+** journal_mode=MEMORY, or journal_mode=OFF.
3405534182
**
3405634183
** Within this comment block, a page is deemed to have been synced
3405734184
** automatically as soon as it is written when PRAGMA synchronous=OFF.
3405834185
** Otherwise, the page is not synced until the xSync method of the VFS
3405934186
** is called successfully on the file containing the page.
@@ -34083,11 +34210,11 @@
3408334210
** both the content in the database when the rollback journal was written
3408434211
** and the content in the database at the beginning of the current
3408534212
** transaction.
3408634213
**
3408734214
** (3) Writes to the database file are an integer multiple of the page size
34088
-** in length and are aligned to a page boundary.
34215
+** in length and are aligned on a page boundary.
3408934216
**
3409034217
** (4) Reads from the database file are either aligned on a page boundary and
3409134218
** an integer multiple of the page size in length or are taken from the
3409234219
** first 100 bytes of the database file.
3409334220
**
@@ -34114,11 +34241,12 @@
3411434241
** method is a no-op, but that does not change the fact the SQLite will
3411534242
** invoke it.)
3411634243
**
3411734244
** (9) Whenever the database file is modified, at least one bit in the range
3411834245
** of bytes from 24 through 39 inclusive will be changed prior to releasing
34119
-** the EXCLUSIVE lock.
34246
+** the EXCLUSIVE lock, thus signaling other connections on the same
34247
+** database to flush their caches.
3412034248
**
3412134249
** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less
3412234250
** than one billion transactions.
3412334251
**
3412434252
** (11) A database file is well-formed at the beginning and at the conclusion
@@ -34127,11 +34255,12 @@
3412734255
** (12) An EXCLUSIVE lock is held on the database file when writing to
3412834256
** the database file.
3412934257
**
3413034258
** (13) A SHARED lock is held on the database file while reading any
3413134259
** content out of the database file.
34132
-*/
34260
+**
34261
+******************************************************************************/
3413334262
3413434263
/*
3413534264
** Macros for troubleshooting. Normally turned off
3413634265
*/
3413734266
#if 0
@@ -34152,62 +34281,283 @@
3415234281
*/
3415334282
#define PAGERID(p) ((int)(p->fd))
3415434283
#define FILEHANDLEID(fd) ((int)fd)
3415534284
3415634285
/*
34157
-** The page cache as a whole is always in one of the following
34158
-** states:
34159
-**
34160
-** PAGER_UNLOCK The page cache is not currently reading or
34161
-** writing the database file. There is no
34162
-** data held in memory. This is the initial
34163
-** state.
34164
-**
34165
-** PAGER_SHARED The page cache is reading the database.
34166
-** Writing is not permitted. There can be
34167
-** multiple readers accessing the same database
34168
-** file at the same time.
34169
-**
34170
-** PAGER_RESERVED This process has reserved the database for writing
34171
-** but has not yet made any changes. Only one process
34172
-** at a time can reserve the database. The original
34173
-** database file has not been modified so other
34174
-** processes may still be reading the on-disk
34175
-** database file.
34176
-**
34177
-** PAGER_EXCLUSIVE The page cache is writing the database.
34178
-** Access is exclusive. No other processes or
34179
-** threads can be reading or writing while one
34180
-** process is writing.
34181
-**
34182
-** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE
34183
-** after all dirty pages have been written to the
34184
-** database file and the file has been synced to
34185
-** disk. All that remains to do is to remove or
34186
-** truncate the journal file and the transaction
34187
-** will be committed.
34188
-**
34189
-** The page cache comes up in PAGER_UNLOCK. The first time a
34190
-** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED.
34191
-** After all pages have been released using sqlite_page_unref(),
34192
-** the state transitions back to PAGER_UNLOCK. The first time
34193
-** that sqlite3PagerWrite() is called, the state transitions to
34194
-** PAGER_RESERVED. (Note that sqlite3PagerWrite() can only be
34195
-** called on an outstanding page which means that the pager must
34196
-** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
34197
-** PAGER_RESERVED means that there is an open rollback journal.
34198
-** The transition to PAGER_EXCLUSIVE occurs before any changes
34199
-** are made to the database file, though writes to the rollback
34200
-** journal occurs with just PAGER_RESERVED. After an sqlite3PagerRollback()
34201
-** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED,
34202
-** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode.
34203
-*/
34204
-#define PAGER_UNLOCK 0
34205
-#define PAGER_SHARED 1 /* same as SHARED_LOCK */
34206
-#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
34207
-#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
34208
-#define PAGER_SYNCED 5
34286
+** The Pager.eState variable stores the current 'state' of a pager. A
34287
+** pager may be in any one of the seven states shown in the following
34288
+** state diagram.
34289
+**
34290
+** OPEN <------+------+
34291
+** | | |
34292
+** V | |
34293
+** +---------> READER-------+ |
34294
+** | | |
34295
+** | V |
34296
+** |<-------WRITER_LOCKED------> ERROR
34297
+** | | ^
34298
+** | V |
34299
+** |<------WRITER_CACHEMOD-------->|
34300
+** | | |
34301
+** | V |
34302
+** |<-------WRITER_DBMOD---------->|
34303
+** | | |
34304
+** | V |
34305
+** +<------WRITER_FINISHED-------->+
34306
+**
34307
+**
34308
+** List of state transitions and the C [function] that performs each:
34309
+**
34310
+** OPEN -> READER [sqlite3PagerSharedLock]
34311
+** READER -> OPEN [pager_unlock]
34312
+**
34313
+** READER -> WRITER_LOCKED [sqlite3PagerBegin]
34314
+** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal]
34315
+** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal]
34316
+** WRITER_DBMOD -> WRITER_FINISHED [sqlite3PagerCommitPhaseOne]
34317
+** WRITER_*** -> READER [pager_end_transaction]
34318
+**
34319
+** WRITER_*** -> ERROR [pager_error]
34320
+** ERROR -> OPEN [pager_unlock]
34321
+**
34322
+**
34323
+** OPEN:
34324
+**
34325
+** The pager starts up in this state. Nothing is guaranteed in this
34326
+** state - the file may or may not be locked and the database size is
34327
+** unknown. The database may not be read or written.
34328
+**
34329
+** * No read or write transaction is active.
34330
+** * Any lock, or no lock at all, may be held on the database file.
34331
+** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted.
34332
+**
34333
+** READER:
34334
+**
34335
+** In this state all the requirements for reading the database in
34336
+** rollback (non-WAL) mode are met. Unless the pager is (or recently
34337
+** was) in exclusive-locking mode, a user-level read transaction is
34338
+** open. The database size is known in this state.
34339
+**
34340
+** A connection running with locking_mode=normal enters this state when
34341
+** it opens a read-transaction on the database and returns to state
34342
+** OPEN after the read-transaction is completed. However a connection
34343
+** running in locking_mode=exclusive (including temp databases) remains in
34344
+** this state even after the read-transaction is closed. The only way
34345
+** a locking_mode=exclusive connection can transition from READER to OPEN
34346
+** is via the ERROR state (see below).
34347
+**
34348
+** * A read transaction may be active (but a write-transaction cannot).
34349
+** * A SHARED or greater lock is held on the database file.
34350
+** * The dbSize variable may be trusted (even if a user-level read
34351
+** transaction is not active). The dbOrigSize and dbFileSize variables
34352
+** may not be trusted at this point.
34353
+** * If the database is a WAL database, then the WAL connection is open.
34354
+** * Even if a read-transaction is not open, it is guaranteed that
34355
+** there is no hot-journal in the file-system.
34356
+**
34357
+** WRITER_LOCKED:
34358
+**
34359
+** The pager moves to this state from READER when a write-transaction
34360
+** is first opened on the database. In WRITER_LOCKED state, all locks
34361
+** required to start a write-transaction are held, but no actual
34362
+** modifications to the cache or database have taken place.
34363
+**
34364
+** In rollback mode, a RESERVED or (if the transaction was opened with
34365
+** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when
34366
+** moving to this state, but the journal file is not written to or opened
34367
+** to in this state. If the transaction is committed or rolled back while
34368
+** in WRITER_LOCKED state, all that is required is to unlock the database
34369
+** file.
34370
+**
34371
+** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file.
34372
+** If the connection is running with locking_mode=exclusive, an attempt
34373
+** is made to obtain an EXCLUSIVE lock on the database file.
34374
+**
34375
+** * A write transaction is active.
34376
+** * If the connection is open in rollback-mode, a RESERVED or greater
34377
+** lock is held on the database file.
34378
+** * If the connection is open in WAL-mode, a WAL write transaction
34379
+** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully
34380
+** called).
34381
+** * The dbSize, dbOrigSize and dbFileSize variables are all valid.
34382
+** * The contents of the pager cache have not been modified.
34383
+** * The journal file may or may not be open.
34384
+** * Nothing (not even the first header) has been written to the journal.
34385
+**
34386
+** WRITER_CACHEMOD:
34387
+**
34388
+** A pager moves from WRITER_LOCKED state to this state when a page is
34389
+** first modified by the upper layer. In rollback mode the journal file
34390
+** is opened (if it is not already open) and a header written to the
34391
+** start of it. The database file on disk has not been modified.
34392
+**
34393
+** * A write transaction is active.
34394
+** * A RESERVED or greater lock is held on the database file.
34395
+** * The journal file is open and the first header has been written
34396
+** to it, but the header has not been synced to disk.
34397
+** * The contents of the page cache have been modified.
34398
+**
34399
+** WRITER_DBMOD:
34400
+**
34401
+** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
34402
+** when it modifies the contents of the database file. WAL connections
34403
+** never enter this state (since they do not modify the database file,
34404
+** just the log file).
34405
+**
34406
+** * A write transaction is active.
34407
+** * An EXCLUSIVE or greater lock is held on the database file.
34408
+** * The journal file is open and the first header has been written
34409
+** and synced to disk.
34410
+** * The contents of the page cache have been modified (and possibly
34411
+** written to disk).
34412
+**
34413
+** WRITER_FINISHED:
34414
+**
34415
+** It is not possible for a WAL connection to enter this state.
34416
+**
34417
+** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
34418
+** state after the entire transaction has been successfully written into the
34419
+** database file. In this state the transaction may be committed simply
34420
+** by finalizing the journal file. Once in WRITER_FINISHED state, it is
34421
+** not possible to modify the database further. At this point, the upper
34422
+** layer must either commit or rollback the transaction.
34423
+**
34424
+** * A write transaction is active.
34425
+** * An EXCLUSIVE or greater lock is held on the database file.
34426
+** * All writing and syncing of journal and database data has finished.
34427
+** If no error occured, all that remains is to finalize the journal to
34428
+** commit the transaction. If an error did occur, the caller will need
34429
+** to rollback the transaction.
34430
+**
34431
+** ERROR:
34432
+**
34433
+** The ERROR state is entered when an IO or disk-full error (including
34434
+** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it
34435
+** difficult to be sure that the in-memory pager state (cache contents,
34436
+** db size etc.) are consistent with the contents of the file-system.
34437
+**
34438
+** Temporary pager files may enter the ERROR state, but in-memory pagers
34439
+** cannot.
34440
+**
34441
+** For example, if an IO error occurs while performing a rollback,
34442
+** the contents of the page-cache may be left in an inconsistent state.
34443
+** At this point it would be dangerous to change back to READER state
34444
+** (as usually happens after a rollback). Any subsequent readers might
34445
+** report database corruption (due to the inconsistent cache), and if
34446
+** they upgrade to writers, they may inadvertently corrupt the database
34447
+** file. To avoid this hazard, the pager switches into the ERROR state
34448
+** instead of READER following such an error.
34449
+**
34450
+** Once it has entered the ERROR state, any attempt to use the pager
34451
+** to read or write data returns an error. Eventually, once all
34452
+** outstanding transactions have been abandoned, the pager is able to
34453
+** transition back to OPEN state, discarding the contents of the
34454
+** page-cache and any other in-memory state at the same time. Everything
34455
+** is reloaded from disk (and, if necessary, hot-journal rollback peformed)
34456
+** when a read-transaction is next opened on the pager (transitioning
34457
+** the pager into READER state). At that point the system has recovered
34458
+** from the error.
34459
+**
34460
+** Specifically, the pager jumps into the ERROR state if:
34461
+**
34462
+** 1. An error occurs while attempting a rollback. This happens in
34463
+** function sqlite3PagerRollback().
34464
+**
34465
+** 2. An error occurs while attempting to finalize a journal file
34466
+** following a commit in function sqlite3PagerCommitPhaseTwo().
34467
+**
34468
+** 3. An error occurs while attempting to write to the journal or
34469
+** database file in function pagerStress() in order to free up
34470
+** memory.
34471
+**
34472
+** In other cases, the error is returned to the b-tree layer. The b-tree
34473
+** layer then attempts a rollback operation. If the error condition
34474
+** persists, the pager enters the ERROR state via condition (1) above.
34475
+**
34476
+** Condition (3) is necessary because it can be triggered by a read-only
34477
+** statement executed within a transaction. In this case, if the error
34478
+** code were simply returned to the user, the b-tree layer would not
34479
+** automatically attempt a rollback, as it assumes that an error in a
34480
+** read-only statement cannot leave the pager in an internally inconsistent
34481
+** state.
34482
+**
34483
+** * The Pager.errCode variable is set to something other than SQLITE_OK.
34484
+** * There are one or more outstanding references to pages (after the
34485
+** last reference is dropped the pager should move back to OPEN state).
34486
+** * The pager is not an in-memory pager.
34487
+**
34488
+**
34489
+** Notes:
34490
+**
34491
+** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
34492
+** connection is open in WAL mode. A WAL connection is always in one
34493
+** of the first four states.
34494
+**
34495
+** * Normally, a connection open in exclusive mode is never in PAGER_OPEN
34496
+** state. There are two exceptions: immediately after exclusive-mode has
34497
+** been turned on (and before any read or write transactions are
34498
+** executed), and when the pager is leaving the "error state".
34499
+**
34500
+** * See also: assert_pager_state().
34501
+*/
34502
+#define PAGER_OPEN 0
34503
+#define PAGER_READER 1
34504
+#define PAGER_WRITER_LOCKED 2
34505
+#define PAGER_WRITER_CACHEMOD 3
34506
+#define PAGER_WRITER_DBMOD 4
34507
+#define PAGER_WRITER_FINISHED 5
34508
+#define PAGER_ERROR 6
34509
+
34510
+/*
34511
+** The Pager.eLock variable is almost always set to one of the
34512
+** following locking-states, according to the lock currently held on
34513
+** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
34514
+** This variable is kept up to date as locks are taken and released by
34515
+** the pagerLockDb() and pagerUnlockDb() wrappers.
34516
+**
34517
+** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY
34518
+** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not
34519
+** the operation was successful. In these circumstances pagerLockDb() and
34520
+** pagerUnlockDb() take a conservative approach - eLock is always updated
34521
+** when unlocking the file, and only updated when locking the file if the
34522
+** VFS call is successful. This way, the Pager.eLock variable may be set
34523
+** to a less exclusive (lower) value than the lock that is actually held
34524
+** at the system level, but it is never set to a more exclusive value.
34525
+**
34526
+** This is usually safe. If an xUnlock fails or appears to fail, there may
34527
+** be a few redundant xLock() calls or a lock may be held for longer than
34528
+** required, but nothing really goes wrong.
34529
+**
34530
+** The exception is when the database file is unlocked as the pager moves
34531
+** from ERROR to OPEN state. At this point there may be a hot-journal file
34532
+** in the file-system that needs to be rolled back (as part of a OPEN->SHARED
34533
+** transition, by the same pager or any other). If the call to xUnlock()
34534
+** fails at this point and the pager is left holding an EXCLUSIVE lock, this
34535
+** can confuse the call to xCheckReservedLock() call made later as part
34536
+** of hot-journal detection.
34537
+**
34538
+** xCheckReservedLock() is defined as returning true "if there is a RESERVED
34539
+** lock held by this process or any others". So xCheckReservedLock may
34540
+** return true because the caller itself is holding an EXCLUSIVE lock (but
34541
+** doesn't know it because of a previous error in xUnlock). If this happens
34542
+** a hot-journal may be mistaken for a journal being created by an active
34543
+** transaction in another process, causing SQLite to read from the database
34544
+** without rolling it back.
34545
+**
34546
+** To work around this, if a call to xUnlock() fails when unlocking the
34547
+** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It
34548
+** is only changed back to a real locking state after a successful call
34549
+** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition
34550
+** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK
34551
+** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE
34552
+** lock on the database file before attempting to roll it back. See function
34553
+** PagerSharedLock() for more detail.
34554
+**
34555
+** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in
34556
+** PAGER_OPEN state.
34557
+*/
34558
+#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1)
3420934559
3421034560
/*
3421134561
** A macro used for invoking the codec if there is one
3421234562
*/
3421334563
#ifdef SQLITE_HAS_CODEC
@@ -34253,37 +34603,32 @@
3425334603
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
3425434604
#endif
3425534605
};
3425634606
3425734607
/*
34258
-** A open page cache is an instance of the following structure.
34259
-**
34260
-** errCode
34261
-**
34262
-** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
34263
-** or SQLITE_FULL. Once one of the first three errors occurs, it persists
34264
-** and is returned as the result of every major pager API call. The
34265
-** SQLITE_FULL return code is slightly different. It persists only until the
34266
-** next successful rollback is performed on the pager cache. Also,
34267
-** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup()
34268
-** APIs, they may still be used successfully.
34269
-**
34270
-** dbSizeValid, dbSize, dbOrigSize, dbFileSize
34271
-**
34272
-** Managing the size of the database file in pages is a little complicated.
34273
-** The variable Pager.dbSize contains the number of pages that the database
34274
-** image currently contains. As the database image grows or shrinks this
34275
-** variable is updated. The variable Pager.dbFileSize contains the number
34276
-** of pages in the database file. This may be different from Pager.dbSize
34277
-** if some pages have been appended to the database image but not yet written
34278
-** out from the cache to the actual file on disk. Or if the image has been
34279
-** truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable
34280
-** contains the number of pages in the database image when the current
34281
-** transaction was opened. The contents of all three of these variables is
34282
-** only guaranteed to be correct if the boolean Pager.dbSizeValid is true.
34283
-**
34284
-** TODO: Under what conditions is dbSizeValid set? Cleared?
34608
+** A open page cache is an instance of struct Pager. A description of
34609
+** some of the more important member variables follows:
34610
+**
34611
+** eState
34612
+**
34613
+** The current 'state' of the pager object. See the comment and state
34614
+** diagram above for a description of the pager state.
34615
+**
34616
+** eLock
34617
+**
34618
+** For a real on-disk database, the current lock held on the database file -
34619
+** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
34620
+**
34621
+** For a temporary or in-memory database (neither of which require any
34622
+** locks), this variable is always set to EXCLUSIVE_LOCK. Since such
34623
+** databases always have Pager.exclusiveMode==1, this tricks the pager
34624
+** logic into thinking that it already has all the locks it will ever
34625
+** need (and no reason to release them).
34626
+**
34627
+** In some (obscure) circumstances, this variable may also be set to
34628
+** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for
34629
+** details.
3428534630
**
3428634631
** changeCountDone
3428734632
**
3428834633
** This boolean variable is used to make sure that the change-counter
3428934634
** (the 4-byte header field at byte offset 24 of the database file) is
@@ -34298,28 +34643,10 @@
3429834643
**
3429934644
** This mechanism means that when running in exclusive mode, a connection
3430034645
** need only update the change-counter once, for the first transaction
3430134646
** committed.
3430234647
**
34303
-** dbModified
34304
-**
34305
-** The dbModified flag is set whenever a database page is dirtied.
34306
-** It is cleared at the end of each transaction.
34307
-**
34308
-** It is used when committing or otherwise ending a transaction. If
34309
-** the dbModified flag is clear then less work has to be done.
34310
-**
34311
-** journalStarted
34312
-**
34313
-** This flag is set during a write-transaction after the first
34314
-** journal-header is written and synced to disk.
34315
-**
34316
-** After this has happened, new pages appended to the database
34317
-** do not need the PGHDR_NEED_SYNC flag set, as they do not need
34318
-** to wait for a journal sync before they can be written out to
34319
-** the database file (see function pager_write()).
34320
-**
3432134648
** setMaster
3432234649
**
3432334650
** When PagerCommitPhaseOne() is called to commit a transaction, it may
3432434651
** (or may not) specify a master-journal name to be written into the
3432534652
** journal file before it is synced to disk.
@@ -34326,84 +34653,146 @@
3432634653
**
3432734654
** Whether or not a journal file contains a master-journal pointer affects
3432834655
** the way in which the journal file is finalized after the transaction is
3432934656
** committed or rolled back when running in "journal_mode=PERSIST" mode.
3433034657
** If a journal file does not contain a master-journal pointer, it is
34331
-** finalized by overwriting the first journal header with zeroes. If,
34332
-** on the other hand, it does contain a master-journal pointer, the
34333
-** journal file is finalized by truncating it to zero bytes, just as if
34334
-** the connection were running in "journal_mode=truncate" mode.
34658
+** finalized by overwriting the first journal header with zeroes. If
34659
+** it does contain a master-journal pointer the journal file is finalized
34660
+** by truncating it to zero bytes, just as if the connection were
34661
+** running in "journal_mode=truncate" mode.
3433534662
**
3433634663
** Journal files that contain master journal pointers cannot be finalized
3433734664
** simply by overwriting the first journal-header with zeroes, as the
3433834665
** master journal pointer could interfere with hot-journal rollback of any
3433934666
** subsequently interrupted transaction that reuses the journal file.
3434034667
**
3434134668
** The flag is cleared as soon as the journal file is finalized (either
3434234669
** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the
3434334670
** journal file from being successfully finalized, the setMaster flag
34344
-** is cleared anyway.
34671
+** is cleared anyway (and the pager will move to ERROR state).
3434534672
**
3434634673
** doNotSpill, doNotSyncSpill
3434734674
**
34348
-** When enabled, cache spills are prohibited. The doNotSpill variable
34349
-** inhibits all cache spill and doNotSyncSpill inhibits those spills that
34350
-** would require a journal sync. The doNotSyncSpill is set and cleared
34351
-** by sqlite3PagerWrite() in order to prevent a journal sync from happening
34352
-** in between the journalling of two pages on the same sector. The
34353
-** doNotSpill value set to prevent pagerStress() from trying to use
34354
-** the journal during a rollback.
34355
-**
34356
-** needSync
34357
-**
34358
-** TODO: It might be easier to set this variable in writeJournalHdr()
34359
-** and writeMasterJournal() only. Change its meaning to "unsynced data
34360
-** has been written to the journal".
34675
+** These two boolean variables control the behaviour of cache-spills
34676
+** (calls made by the pcache module to the pagerStress() routine to
34677
+** write cached data to the file-system in order to free up memory).
34678
+**
34679
+** When doNotSpill is non-zero, writing to the database from pagerStress()
34680
+** is disabled altogether. This is done in a very obscure case that
34681
+** comes up during savepoint rollback that requires the pcache module
34682
+** to allocate a new page to prevent the journal file from being written
34683
+** while it is being traversed by code in pager_playback().
34684
+**
34685
+** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
34686
+** is permitted, but syncing the journal file is not. This flag is set
34687
+** by sqlite3PagerWrite() when the file-system sector-size is larger than
34688
+** the database page-size in order to prevent a journal sync from happening
34689
+** in between the journalling of two pages on the same sector.
3436134690
**
3436234691
** subjInMemory
3436334692
**
3436434693
** This is a boolean variable. If true, then any required sub-journal
3436534694
** is opened as an in-memory journal file. If false, then in-memory
3436634695
** sub-journals are only used for in-memory pager files.
34696
+**
34697
+** This variable is updated by the upper layer each time a new
34698
+** write-transaction is opened.
34699
+**
34700
+** dbSize, dbOrigSize, dbFileSize
34701
+**
34702
+** Variable dbSize is set to the number of pages in the database file.
34703
+** It is valid in PAGER_READER and higher states (all states except for
34704
+** OPEN and ERROR).
34705
+**
34706
+** dbSize is set based on the size of the database file, which may be
34707
+** larger than the size of the database (the value stored at offset
34708
+** 28 of the database header by the btree). If the size of the file
34709
+** is not an integer multiple of the page-size, the value stored in
34710
+** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2).
34711
+** Except, any file that is greater than 0 bytes in size is considered
34712
+** to have at least one page. (i.e. a 1KB file with 2K page-size leads
34713
+** to dbSize==1).
34714
+**
34715
+** During a write-transaction, if pages with page-numbers greater than
34716
+** dbSize are modified in the cache, dbSize is updated accordingly.
34717
+** Similarly, if the database is truncated using PagerTruncateImage(),
34718
+** dbSize is updated.
34719
+**
34720
+** Variables dbOrigSize and dbFileSize are valid in states
34721
+** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize
34722
+** variable at the start of the transaction. It is used during rollback,
34723
+** and to determine whether or not pages need to be journalled before
34724
+** being modified.
34725
+**
34726
+** Throughout a write-transaction, dbFileSize contains the size of
34727
+** the file on disk in pages. It is set to a copy of dbSize when the
34728
+** write-transaction is first opened, and updated when VFS calls are made
34729
+** to write or truncate the database file on disk.
34730
+**
34731
+** The only reason the dbFileSize variable is required is to suppress
34732
+** unnecessary calls to xTruncate() after committing a transaction. If,
34733
+** when a transaction is committed, the dbFileSize variable indicates
34734
+** that the database file is larger than the database image (Pager.dbSize),
34735
+** pager_truncate() is called. The pager_truncate() call uses xFilesize()
34736
+** to measure the database file on disk, and then truncates it if required.
34737
+** dbFileSize is not used when rolling back a transaction. In this case
34738
+** pager_truncate() is called unconditionally (which means there may be
34739
+** a call to xFilesize() that is not strictly required). In either case,
34740
+** pager_truncate() may cause the file to become smaller or larger.
34741
+**
34742
+** dbHintSize
34743
+**
34744
+** The dbHintSize variable is used to limit the number of calls made to
34745
+** the VFS xFileControl(FCNTL_SIZE_HINT) method.
34746
+**
34747
+** dbHintSize is set to a copy of the dbSize variable when a
34748
+** write-transaction is opened (at the same time as dbFileSize and
34749
+** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called,
34750
+** dbHintSize is increased to the number of pages that correspond to the
34751
+** size-hint passed to the method call. See pager_write_pagelist() for
34752
+** details.
34753
+**
34754
+** errCode
34755
+**
34756
+** The Pager.errCode variable is only ever used in PAGER_ERROR state. It
34757
+** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
34758
+** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
34759
+** sub-codes.
3436734760
*/
3436834761
struct Pager {
3436934762
sqlite3_vfs *pVfs; /* OS functions to use for IO */
3437034763
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
34371
- u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */
34764
+ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
3437234765
u8 useJournal; /* Use a rollback journal on this file */
3437334766
u8 noReadlock; /* Do not bother to obtain readlocks */
3437434767
u8 noSync; /* Do not sync the journal if true */
3437534768
u8 fullSync; /* Do extra syncs of the journal for robustness */
3437634769
u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
3437734770
u8 tempFile; /* zFilename is a temporary file */
3437834771
u8 readOnly; /* True for a read-only database */
3437934772
u8 memDb; /* True to inhibit all file I/O */
3438034773
34381
- /* The following block contains those class members that are dynamically
34382
- ** modified during normal operations. The other variables in this structure
34383
- ** are either constant throughout the lifetime of the pager, or else
34384
- ** used to store configuration parameters that affect the way the pager
34385
- ** operates.
34386
- **
34387
- ** The 'state' variable is described in more detail along with the
34388
- ** descriptions of the values it may take - PAGER_UNLOCK etc. Many of the
34389
- ** other variables in this block are described in the comment directly
34390
- ** above this class definition.
34774
+ /**************************************************************************
34775
+ ** The following block contains those class members that change during
34776
+ ** routine opertion. Class members not in this block are either fixed
34777
+ ** when the pager is first created or else only change when there is a
34778
+ ** significant mode change (such as changing the page_size, locking_mode,
34779
+ ** or the journal_mode). From another view, these class members describe
34780
+ ** the "state" of the pager, while other class members describe the
34781
+ ** "configuration" of the pager.
3439134782
*/
34392
- u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
34393
- u8 dbModified; /* True if there are any changes to the Db */
34394
- u8 needSync; /* True if an fsync() is needed on the journal */
34395
- u8 journalStarted; /* True if header of journal is synced */
34783
+ u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */
34784
+ u8 eLock; /* Current lock held on database file */
3439634785
u8 changeCountDone; /* Set after incrementing the change-counter */
3439734786
u8 setMaster; /* True if a m-j name has been written to jrnl */
3439834787
u8 doNotSpill; /* Do not spill the cache when non-zero */
3439934788
u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
34400
- u8 dbSizeValid; /* Set when dbSize is correct */
3440134789
u8 subjInMemory; /* True to use in-memory sub-journals */
3440234790
Pgno dbSize; /* Number of pages in the database */
3440334791
Pgno dbOrigSize; /* dbSize before the current transaction */
3440434792
Pgno dbFileSize; /* Number of pages in the database file */
34793
+ Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */
3440534794
int errCode; /* One of several kinds of errors */
3440634795
int nRec; /* Pages journalled since last j-header written */
3440734796
u32 cksumInit; /* Quasi-random value added to every checksum */
3440834797
u32 nSubRec; /* Number of records written to sub-journal */
3440934798
Bitvec *pInJournal; /* One bit for each page in the database file */
@@ -34410,21 +34799,25 @@
3441034799
sqlite3_file *fd; /* File descriptor for database */
3441134800
sqlite3_file *jfd; /* File descriptor for main journal */
3441234801
sqlite3_file *sjfd; /* File descriptor for sub-journal */
3441334802
i64 journalOff; /* Current write offset in the journal file */
3441434803
i64 journalHdr; /* Byte offset to previous journal header */
34415
- i64 journalSizeLimit; /* Size limit for persistent journal files */
34804
+ sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
3441634805
PagerSavepoint *aSavepoint; /* Array of active savepoints */
3441734806
int nSavepoint; /* Number of elements in aSavepoint[] */
3441834807
char dbFileVers[16]; /* Changes whenever database file changes */
34419
- u32 sectorSize; /* Assumed sector size during rollback */
34808
+ /*
34809
+ ** End of the routinely-changing class members
34810
+ ***************************************************************************/
3442034811
3442134812
u16 nExtra; /* Add this many bytes to each in-memory page */
3442234813
i16 nReserve; /* Number of unused bytes at end of each page */
3442334814
u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
34815
+ u32 sectorSize; /* Assumed sector size during rollback */
3442434816
int pageSize; /* Number of bytes in a page */
3442534817
Pgno mxPgno; /* Maximum allowed size of the database */
34818
+ i64 journalSizeLimit; /* Size limit for persistent journal files */
3442634819
char *zFilename; /* Name of the database file */
3442734820
char *zJournal; /* Name of the journal file */
3442834821
int (*xBusyHandler)(void*); /* Function to call when busy */
3442934822
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
3443034823
#ifdef SQLITE_TEST
@@ -34438,11 +34831,10 @@
3443834831
void (*xCodecFree)(void*); /* Destructor for the codec */
3443934832
void *pCodec; /* First argument to xCodec... methods */
3444034833
#endif
3444134834
char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
3444234835
PCache *pPCache; /* Pointer to page cache object */
34443
- sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
3444434836
#ifndef SQLITE_OMIT_WAL
3444534837
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
3444634838
char *zWal; /* File name for write-ahead log */
3444734839
#endif
3444834840
};
@@ -34517,26 +34909,225 @@
3451734909
/*
3451834910
** The maximum legal page number is (2^31 - 1).
3451934911
*/
3452034912
#define PAGER_MAX_PGNO 2147483647
3452134913
34914
+/*
34915
+** The argument to this macro is a file descriptor (type sqlite3_file*).
34916
+** Return 0 if it is not open, or non-zero (but not 1) if it is.
34917
+**
34918
+** This is so that expressions can be written as:
34919
+**
34920
+** if( isOpen(pPager->jfd) ){ ...
34921
+**
34922
+** instead of
34923
+**
34924
+** if( pPager->jfd->pMethods ){ ...
34925
+*/
34926
+#define isOpen(pFd) ((pFd)->pMethods)
34927
+
34928
+/*
34929
+** Return true if this pager uses a write-ahead log instead of the usual
34930
+** rollback journal. Otherwise false.
34931
+*/
34932
+#ifndef SQLITE_OMIT_WAL
34933
+static int pagerUseWal(Pager *pPager){
34934
+ return (pPager->pWal!=0);
34935
+}
34936
+#else
34937
+# define pagerUseWal(x) 0
34938
+# define pagerRollbackWal(x) 0
34939
+# define pagerWalFrames(v,w,x,y,z) 0
34940
+# define pagerOpenWalIfPresent(z) SQLITE_OK
34941
+# define pagerBeginReadTransaction(z) SQLITE_OK
34942
+#endif
34943
+
3452234944
#ifndef NDEBUG
3452334945
/*
3452434946
** Usage:
3452534947
**
3452634948
** assert( assert_pager_state(pPager) );
34949
+**
34950
+** This function runs many asserts to try to find inconsistencies in
34951
+** the internal state of the Pager object.
3452734952
*/
34528
-static int assert_pager_state(Pager *pPager){
34953
+static int assert_pager_state(Pager *p){
34954
+ Pager *pPager = p;
3452934955
34530
- /* A temp-file is always in PAGER_EXCLUSIVE or PAGER_SYNCED state. */
34531
- assert( pPager->tempFile==0 || pPager->state>=PAGER_EXCLUSIVE );
34956
+ /* State must be valid. */
34957
+ assert( p->eState==PAGER_OPEN
34958
+ || p->eState==PAGER_READER
34959
+ || p->eState==PAGER_WRITER_LOCKED
34960
+ || p->eState==PAGER_WRITER_CACHEMOD
34961
+ || p->eState==PAGER_WRITER_DBMOD
34962
+ || p->eState==PAGER_WRITER_FINISHED
34963
+ || p->eState==PAGER_ERROR
34964
+ );
3453234965
34533
- /* The changeCountDone flag is always set for temp-files */
34534
- assert( pPager->tempFile==0 || pPager->changeCountDone );
34966
+ /* Regardless of the current state, a temp-file connection always behaves
34967
+ ** as if it has an exclusive lock on the database file. It never updates
34968
+ ** the change-counter field, so the changeCountDone flag is always set.
34969
+ */
34970
+ assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK );
34971
+ assert( p->tempFile==0 || pPager->changeCountDone );
34972
+
34973
+ /* If the useJournal flag is clear, the journal-mode must be "OFF".
34974
+ ** And if the journal-mode is "OFF", the journal file must not be open.
34975
+ */
34976
+ assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
34977
+ assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
34978
+
34979
+ /* Check that MEMDB implies noSync. And an in-memory journal. Since
34980
+ ** this means an in-memory pager performs no IO at all, it cannot encounter
34981
+ ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing
34982
+ ** a journal file. (although the in-memory journal implementation may
34983
+ ** return SQLITE_IOERR_NOMEM while the journal file is being written). It
34984
+ ** is therefore not possible for an in-memory pager to enter the ERROR
34985
+ ** state.
34986
+ */
34987
+ if( MEMDB ){
34988
+ assert( p->noSync );
34989
+ assert( p->journalMode==PAGER_JOURNALMODE_OFF
34990
+ || p->journalMode==PAGER_JOURNALMODE_MEMORY
34991
+ );
34992
+ assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN );
34993
+ assert( pagerUseWal(p)==0 );
34994
+ }
34995
+
34996
+ /* If changeCountDone is set, a RESERVED lock or greater must be held
34997
+ ** on the file.
34998
+ */
34999
+ assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );
35000
+ assert( p->eLock!=PENDING_LOCK );
35001
+
35002
+ switch( p->eState ){
35003
+ case PAGER_OPEN:
35004
+ assert( !MEMDB );
35005
+ assert( pPager->errCode==SQLITE_OK );
35006
+ assert( sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile );
35007
+ break;
35008
+
35009
+ case PAGER_READER:
35010
+ assert( pPager->errCode==SQLITE_OK );
35011
+ assert( p->eLock!=UNKNOWN_LOCK );
35012
+ assert( p->eLock>=SHARED_LOCK || p->noReadlock );
35013
+ break;
35014
+
35015
+ case PAGER_WRITER_LOCKED:
35016
+ assert( p->eLock!=UNKNOWN_LOCK );
35017
+ assert( pPager->errCode==SQLITE_OK );
35018
+ if( !pagerUseWal(pPager) ){
35019
+ assert( p->eLock>=RESERVED_LOCK );
35020
+ }
35021
+ assert( pPager->dbSize==pPager->dbOrigSize );
35022
+ assert( pPager->dbOrigSize==pPager->dbFileSize );
35023
+ assert( pPager->dbOrigSize==pPager->dbHintSize );
35024
+ assert( pPager->setMaster==0 );
35025
+ break;
35026
+
35027
+ case PAGER_WRITER_CACHEMOD:
35028
+ assert( p->eLock!=UNKNOWN_LOCK );
35029
+ assert( pPager->errCode==SQLITE_OK );
35030
+ if( !pagerUseWal(pPager) ){
35031
+ /* It is possible that if journal_mode=wal here that neither the
35032
+ ** journal file nor the WAL file are open. This happens during
35033
+ ** a rollback transaction that switches from journal_mode=off
35034
+ ** to journal_mode=wal.
35035
+ */
35036
+ assert( p->eLock>=RESERVED_LOCK );
35037
+ assert( isOpen(p->jfd)
35038
+ || p->journalMode==PAGER_JOURNALMODE_OFF
35039
+ || p->journalMode==PAGER_JOURNALMODE_WAL
35040
+ );
35041
+ }
35042
+ assert( pPager->dbOrigSize==pPager->dbFileSize );
35043
+ assert( pPager->dbOrigSize==pPager->dbHintSize );
35044
+ break;
35045
+
35046
+ case PAGER_WRITER_DBMOD:
35047
+ assert( p->eLock==EXCLUSIVE_LOCK );
35048
+ assert( pPager->errCode==SQLITE_OK );
35049
+ assert( !pagerUseWal(pPager) );
35050
+ assert( p->eLock>=EXCLUSIVE_LOCK );
35051
+ assert( isOpen(p->jfd)
35052
+ || p->journalMode==PAGER_JOURNALMODE_OFF
35053
+ || p->journalMode==PAGER_JOURNALMODE_WAL
35054
+ );
35055
+ assert( pPager->dbOrigSize<=pPager->dbHintSize );
35056
+ break;
35057
+
35058
+ case PAGER_WRITER_FINISHED:
35059
+ assert( p->eLock==EXCLUSIVE_LOCK );
35060
+ assert( pPager->errCode==SQLITE_OK );
35061
+ assert( !pagerUseWal(pPager) );
35062
+ assert( isOpen(p->jfd)
35063
+ || p->journalMode==PAGER_JOURNALMODE_OFF
35064
+ || p->journalMode==PAGER_JOURNALMODE_WAL
35065
+ );
35066
+ break;
35067
+
35068
+ case PAGER_ERROR:
35069
+ /* There must be at least one outstanding reference to the pager if
35070
+ ** in ERROR state. Otherwise the pager should have already dropped
35071
+ ** back to OPEN state.
35072
+ */
35073
+ assert( pPager->errCode!=SQLITE_OK );
35074
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
35075
+ break;
35076
+ }
3453535077
3453635078
return 1;
3453735079
}
35080
+
35081
+/*
35082
+** Return a pointer to a human readable string in a static buffer
35083
+** containing the state of the Pager object passed as an argument. This
35084
+** is intended to be used within debuggers. For example, as an alternative
35085
+** to "print *pPager" in gdb:
35086
+**
35087
+** (gdb) printf "%s", print_pager_state(pPager)
35088
+*/
35089
+static char *print_pager_state(Pager *p){
35090
+ static char zRet[1024];
35091
+
35092
+ sqlite3_snprintf(1024, zRet,
35093
+ "Filename: %s\n"
35094
+ "State: %s errCode=%d\n"
35095
+ "Lock: %s\n"
35096
+ "Locking mode: locking_mode=%s\n"
35097
+ "Journal mode: journal_mode=%s\n"
35098
+ "Backing store: tempFile=%d memDb=%d useJournal=%d\n"
35099
+ "Journal: journalOff=%lld journalHdr=%lld\n"
35100
+ "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n"
35101
+ , p->zFilename
35102
+ , p->eState==PAGER_OPEN ? "OPEN" :
35103
+ p->eState==PAGER_READER ? "READER" :
35104
+ p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" :
35105
+ p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
35106
+ p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" :
35107
+ p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
35108
+ p->eState==PAGER_ERROR ? "ERROR" : "?error?"
35109
+ , (int)p->errCode
35110
+ , p->eLock==NO_LOCK ? "NO_LOCK" :
35111
+ p->eLock==RESERVED_LOCK ? "RESERVED" :
35112
+ p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" :
35113
+ p->eLock==SHARED_LOCK ? "SHARED" :
35114
+ p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?"
35115
+ , p->exclusiveMode ? "exclusive" : "normal"
35116
+ , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" :
35117
+ p->journalMode==PAGER_JOURNALMODE_OFF ? "off" :
35118
+ p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" :
35119
+ p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" :
35120
+ p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" :
35121
+ p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?"
35122
+ , (int)p->tempFile, (int)p->memDb, (int)p->useJournal
35123
+ , p->journalOff, p->journalHdr
35124
+ , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize
35125
+ );
35126
+
35127
+ return zRet;
35128
+}
3453835129
#endif
3453935130
3454035131
/*
3454135132
** Return true if it is necessary to write page *pPg into the sub-journal.
3454235133
** A page needs to be written into the sub-journal if there exists one
@@ -34584,10 +35175,11 @@
3458435175
3458535176
/*
3458635177
** Write a 32-bit integer into a string buffer in big-endian byte order.
3458735178
*/
3458835179
#define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
35180
+
3458935181
3459035182
/*
3459135183
** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
3459235184
** on success or an error code is something goes wrong.
3459335185
*/
@@ -34596,31 +35188,57 @@
3459635188
put32bits(ac, val);
3459735189
return sqlite3OsWrite(fd, ac, 4, offset);
3459835190
}
3459935191
3460035192
/*
34601
-** The argument to this macro is a file descriptor (type sqlite3_file*).
34602
-** Return 0 if it is not open, or non-zero (but not 1) if it is.
34603
-**
34604
-** This is so that expressions can be written as:
34605
-**
34606
-** if( isOpen(pPager->jfd) ){ ...
34607
-**
34608
-** instead of
34609
-**
34610
-** if( pPager->jfd->pMethods ){ ...
34611
-*/
34612
-#define isOpen(pFd) ((pFd)->pMethods)
35193
+** Unlock the database file to level eLock, which must be either NO_LOCK
35194
+** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
35195
+** succeeds, set the Pager.eLock variable to match the (attempted) new lock.
35196
+**
35197
+** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
35198
+** called, do not modify it. See the comment above the #define of
35199
+** UNKNOWN_LOCK for an explanation of this.
35200
+*/
35201
+static int pagerUnlockDb(Pager *pPager, int eLock){
35202
+ int rc = SQLITE_OK;
35203
+
35204
+ assert( !pPager->exclusiveMode );
35205
+ assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
35206
+ assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
35207
+ if( isOpen(pPager->fd) ){
35208
+ assert( pPager->eLock>=eLock );
35209
+ rc = sqlite3OsUnlock(pPager->fd, eLock);
35210
+ if( pPager->eLock!=UNKNOWN_LOCK ){
35211
+ pPager->eLock = (u8)eLock;
35212
+ }
35213
+ IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
35214
+ }
35215
+ return rc;
35216
+}
3461335217
3461435218
/*
34615
-** If file pFd is open, call sqlite3OsUnlock() on it.
35219
+** Lock the database file to level eLock, which must be either SHARED_LOCK,
35220
+** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
35221
+** Pager.eLock variable to the new locking state.
35222
+**
35223
+** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
35224
+** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
35225
+** See the comment above the #define of UNKNOWN_LOCK for an explanation
35226
+** of this.
3461635227
*/
34617
-static int osUnlock(sqlite3_file *pFd, int eLock){
34618
- if( !isOpen(pFd) ){
34619
- return SQLITE_OK;
35228
+static int pagerLockDb(Pager *pPager, int eLock){
35229
+ int rc = SQLITE_OK;
35230
+
35231
+ assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
35232
+ if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
35233
+ rc = sqlite3OsLock(pPager->fd, eLock);
35234
+ if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
35235
+ pPager->eLock = (u8)eLock;
35236
+ IOTRACE(("LOCK %p %d\n", pPager, eLock))
35237
+ }
3462035238
}
34621
- return sqlite3OsUnlock(pFd, eLock);
35239
+ return rc;
3462235240
}
3462335241
3462435242
/*
3462535243
** This function determines whether or not the atomic-write optimization
3462635244
** can be used with this pager. The optimization can be used if:
@@ -34692,17 +35310,18 @@
3469235310
** that the page is either dirty or still matches the calculated page-hash.
3469335311
*/
3469435312
#define CHECK_PAGE(x) checkPage(x)
3469535313
static void checkPage(PgHdr *pPg){
3469635314
Pager *pPager = pPg->pPager;
34697
- assert( !pPg->pageHash || pPager->errCode
34698
- || (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
35315
+ assert( pPager->eState!=PAGER_ERROR );
35316
+ assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
3469935317
}
3470035318
3470135319
#else
3470235320
#define pager_datahash(X,Y) 0
3470335321
#define pager_pagehash(X) 0
35322
+#define pager_set_pagehash(X)
3470435323
#define CHECK_PAGE(x)
3470535324
#endif /* SQLITE_CHECK_PAGES */
3470635325
3470735326
/*
3470835327
** When this is called the journal file for pager pPager must be open.
@@ -34865,11 +35484,11 @@
3486535484
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
3486635485
*/
3486735486
static int writeJournalHdr(Pager *pPager){
3486835487
int rc = SQLITE_OK; /* Return code */
3486935488
char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */
34870
- u32 nHeader = pPager->pageSize; /* Size of buffer pointed to by zHeader */
35489
+ u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */
3487135490
u32 nWrite; /* Bytes of header sector written */
3487235491
int ii; /* Loop counter */
3487335492
3487435493
assert( isOpen(pPager->jfd) ); /* Journal file must be open. */
3487535494
@@ -34908,11 +35527,11 @@
3490835527
**
3490935528
** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
3491035529
** that garbage data is never appended to the journal file.
3491135530
*/
3491235531
assert( isOpen(pPager->fd) || pPager->noSync );
34913
- if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
35532
+ if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
3491435533
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
3491535534
){
3491635535
memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
3491735536
put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
3491835537
}else{
@@ -35032,18 +35651,25 @@
3503235651
}
3503335652
3503435653
if( pPager->journalOff==0 ){
3503535654
u32 iPageSize; /* Page-size field of journal header */
3503635655
u32 iSectorSize; /* Sector-size field of journal header */
35037
- u16 iPageSize16; /* Copy of iPageSize in 16-bit variable */
3503835656
3503935657
/* Read the page-size and sector-size journal header fields. */
3504035658
if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize))
3504135659
|| SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize))
3504235660
){
3504335661
return rc;
3504435662
}
35663
+
35664
+ /* Versions of SQLite prior to 3.5.8 set the page-size field of the
35665
+ ** journal header to zero. In this case, assume that the Pager.pageSize
35666
+ ** variable is already set to the correct page size.
35667
+ */
35668
+ if( iPageSize==0 ){
35669
+ iPageSize = pPager->pageSize;
35670
+ }
3504535671
3504635672
/* Check that the values read from the page-size and sector-size fields
3504735673
** are within range. To be 'in range', both values need to be a power
3504835674
** of two greater than or equal to 512 or 32, and not greater than their
3504935675
** respective compile time maximum limits.
@@ -35062,14 +35688,12 @@
3506235688
3506335689
/* Update the page-size to match the value read from the journal.
3506435690
** Use a testcase() macro to make sure that malloc failure within
3506535691
** PagerSetPagesize() is tested.
3506635692
*/
35067
- iPageSize16 = (u16)iPageSize;
35068
- rc = sqlite3PagerSetPagesize(pPager, &iPageSize16, -1);
35693
+ rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1);
3506935694
testcase( rc!=SQLITE_OK );
35070
- assert( rc!=SQLITE_OK || iPageSize16==(u16)iPageSize );
3507135695
3507235696
/* Update the assumed sector-size to match the value used by
3507335697
** the process that created this journal. If this journal was
3507435698
** created by a process other than this one, then this routine
3507535699
** is being called from within pager_playback(). The local value
@@ -35108,10 +35732,12 @@
3510835732
i64 iHdrOff; /* Offset of header in journal file */
3510935733
i64 jrnlSize; /* Size of journal file on disk */
3511035734
u32 cksum = 0; /* Checksum of string zMaster */
3511135735
3511235736
assert( pPager->setMaster==0 );
35737
+ assert( !pagerUseWal(pPager) );
35738
+
3511335739
if( !zMaster
3511435740
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
3511535741
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
3511635742
){
3511735743
return SQLITE_OK;
@@ -35144,11 +35770,10 @@
3514435770
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
3514535771
){
3514635772
return rc;
3514735773
}
3514835774
pPager->journalOff += (nMaster+20);
35149
- pPager->needSync = !pPager->noSync;
3515035775
3515135776
/* If the pager is in peristent-journal mode, then the physical
3515235777
** journal-file may extend past the end of the master-journal name
3515335778
** and 8 bytes of magic data just written to the file. This is
3515435779
** dangerous because the code to rollback a hot-journal file
@@ -35180,21 +35805,15 @@
3518035805
(void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
3518135806
return p;
3518235807
}
3518335808
3518435809
/*
35185
-** Unless the pager is in error-state, discard all in-memory pages. If
35186
-** the pager is in error-state, then this call is a no-op.
35187
-**
35188
-** TODO: Why can we not reset the pager while in error state?
35810
+** Discard the entire contents of the in-memory page-cache.
3518935811
*/
3519035812
static void pager_reset(Pager *pPager){
35191
- if( SQLITE_OK==pPager->errCode ){
35192
- sqlite3BackupRestart(pPager->pBackup);
35193
- sqlite3PcacheClear(pPager->pPCache);
35194
- pPager->dbSizeValid = 0;
35195
- }
35813
+ sqlite3BackupRestart(pPager->pBackup);
35814
+ sqlite3PcacheClear(pPager->pPCache);
3519635815
}
3519735816
3519835817
/*
3519935818
** Free all structures in the Pager.aSavepoint[] array and set both
3520035819
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
@@ -35233,38 +35852,43 @@
3523335852
}
3523435853
return rc;
3523535854
}
3523635855
3523735856
/*
35238
-** Return true if this pager uses a write-ahead log instead of the usual
35239
-** rollback journal. Otherwise false.
35240
-*/
35241
-#ifndef SQLITE_OMIT_WAL
35242
-static int pagerUseWal(Pager *pPager){
35243
- return (pPager->pWal!=0);
35244
-}
35245
-#else
35246
-# define pagerUseWal(x) 0
35247
-# define pagerRollbackWal(x) 0
35248
-# define pagerWalFrames(v,w,x,y,z) 0
35249
-# define pagerOpenWalIfPresent(z) SQLITE_OK
35250
-# define pagerBeginReadTransaction(z) SQLITE_OK
35251
-#endif
35252
-
35253
-/*
35254
-** Unlock the database file. This function is a no-op if the pager
35255
-** is in exclusive mode.
35857
+** This function is a no-op if the pager is in exclusive mode and not
35858
+** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
35859
+** state.
3525635860
**
35257
-** If the pager is currently in error state, discard the contents of
35258
-** the cache and reset the Pager structure internal state. If there is
35259
-** an open journal-file, then the next time a shared-lock is obtained
35260
-** on the pager file (by this or any other process), it will be
35261
-** treated as a hot-journal and rolled back.
35861
+** If the pager is not in exclusive-access mode, the database file is
35862
+** completely unlocked. If the file is unlocked and the file-system does
35863
+** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is
35864
+** closed (if it is open).
35865
+**
35866
+** If the pager is in ERROR state when this function is called, the
35867
+** contents of the pager cache are discarded before switching back to
35868
+** the OPEN state. Regardless of whether the pager is in exclusive-mode
35869
+** or not, any journal file left in the file-system will be treated
35870
+** as a hot-journal and rolled back the next time a read-transaction
35871
+** is opened (by this or by any other connection).
3526235872
*/
3526335873
static void pager_unlock(Pager *pPager){
35264
- if( !pPager->exclusiveMode ){
35265
- int rc = SQLITE_OK; /* Return code */
35874
+
35875
+ assert( pPager->eState==PAGER_READER
35876
+ || pPager->eState==PAGER_OPEN
35877
+ || pPager->eState==PAGER_ERROR
35878
+ );
35879
+
35880
+ sqlite3BitvecDestroy(pPager->pInJournal);
35881
+ pPager->pInJournal = 0;
35882
+ releaseAllSavepoints(pPager);
35883
+
35884
+ if( pagerUseWal(pPager) ){
35885
+ assert( !isOpen(pPager->jfd) );
35886
+ sqlite3WalEndReadTransaction(pPager->pWal);
35887
+ pPager->eState = PAGER_OPEN;
35888
+ }else if( !pPager->exclusiveMode ){
35889
+ int rc; /* Error code returned by pagerUnlockDb() */
3526635890
int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
3526735891
3526835892
/* If the operating system support deletion of open files, then
3526935893
** close the journal file when dropping the database lock. Otherwise
3527035894
** another connection with journal_mode=delete might delete the file
@@ -35280,62 +35904,60 @@
3528035904
|| 1!=(pPager->journalMode & 5)
3528135905
){
3528235906
sqlite3OsClose(pPager->jfd);
3528335907
}
3528435908
35285
- sqlite3BitvecDestroy(pPager->pInJournal);
35286
- pPager->pInJournal = 0;
35287
- releaseAllSavepoints(pPager);
35288
-
35289
- /* If the file is unlocked, somebody else might change it. The
35290
- ** values stored in Pager.dbSize etc. might become invalid if
35291
- ** this happens. One can argue that this doesn't need to be cleared
35292
- ** until the change-counter check fails in PagerSharedLock().
35293
- ** Clearing the page size cache here is being conservative.
35294
- */
35295
- pPager->dbSizeValid = 0;
35296
-
35297
- if( pagerUseWal(pPager) ){
35298
- sqlite3WalEndReadTransaction(pPager->pWal);
35299
- }else{
35300
- rc = osUnlock(pPager->fd, NO_LOCK);
35301
- }
35302
- if( rc ){
35303
- pPager->errCode = rc;
35304
- }
35305
- IOTRACE(("UNLOCK %p\n", pPager))
35306
-
35307
- /* If Pager.errCode is set, the contents of the pager cache cannot be
35308
- ** trusted. Now that the pager file is unlocked, the contents of the
35309
- ** cache can be discarded and the error code safely cleared.
35310
- */
35311
- if( pPager->errCode ){
35312
- if( rc==SQLITE_OK ){
35313
- pPager->errCode = SQLITE_OK;
35314
- }
35315
- pager_reset(pPager);
35316
- }
35317
-
35909
+ /* If the pager is in the ERROR state and the call to unlock the database
35910
+ ** file fails, set the current lock to UNKNOWN_LOCK. See the comment
35911
+ ** above the #define for UNKNOWN_LOCK for an explanation of why this
35912
+ ** is necessary.
35913
+ */
35914
+ rc = pagerUnlockDb(pPager, NO_LOCK);
35915
+ if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){
35916
+ pPager->eLock = UNKNOWN_LOCK;
35917
+ }
35918
+
35919
+ /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here
35920
+ ** without clearing the error code. This is intentional - the error
35921
+ ** code is cleared and the cache reset in the block below.
35922
+ */
35923
+ assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
3531835924
pPager->changeCountDone = 0;
35319
- pPager->state = PAGER_UNLOCK;
35320
- pPager->dbModified = 0;
35925
+ pPager->eState = PAGER_OPEN;
3532135926
}
35927
+
35928
+ /* If Pager.errCode is set, the contents of the pager cache cannot be
35929
+ ** trusted. Now that there are no outstanding references to the pager,
35930
+ ** it can safely move back to PAGER_OPEN state. This happens in both
35931
+ ** normal and exclusive-locking mode.
35932
+ */
35933
+ if( pPager->errCode ){
35934
+ assert( !MEMDB );
35935
+ pager_reset(pPager);
35936
+ pPager->changeCountDone = pPager->tempFile;
35937
+ pPager->eState = PAGER_OPEN;
35938
+ pPager->errCode = SQLITE_OK;
35939
+ }
35940
+
35941
+ pPager->journalOff = 0;
35942
+ pPager->journalHdr = 0;
35943
+ pPager->setMaster = 0;
3532235944
}
3532335945
3532435946
/*
35325
-** This function should be called when an IOERR, CORRUPT or FULL error
35326
-** may have occurred. The first argument is a pointer to the pager
35327
-** structure, the second the error-code about to be returned by a pager
35328
-** API function. The value returned is a copy of the second argument
35329
-** to this function.
35330
-**
35331
-** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL
35332
-** the error becomes persistent. Until the persistent error is cleared,
35333
-** subsequent API calls on this Pager will immediately return the same
35334
-** error code.
35335
-**
35336
-** A persistent error indicates that the contents of the pager-cache
35947
+** This function is called whenever an IOERR or FULL error that requires
35948
+** the pager to transition into the ERROR state may ahve occurred.
35949
+** The first argument is a pointer to the pager structure, the second
35950
+** the error-code about to be returned by a pager API function. The
35951
+** value returned is a copy of the second argument to this function.
35952
+**
35953
+** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the
35954
+** IOERR sub-codes, the pager enters the ERROR state and the error code
35955
+** is stored in Pager.errCode. While the pager remains in the ERROR state,
35956
+** all major API calls on the Pager will immediately return Pager.errCode.
35957
+**
35958
+** The ERROR state indicates that the contents of the pager-cache
3533735959
** cannot be trusted. This state can be cleared by completely discarding
3533835960
** the contents of the pager-cache. If a transaction was active when
3533935961
** the persistent error occurred, then the rollback journal may need
3534035962
** to be replayed to restore the contents of the database file (as if
3534135963
** it were a hot-journal).
@@ -35348,49 +35970,25 @@
3534835970
pPager->errCode==SQLITE_OK ||
3534935971
(pPager->errCode & 0xff)==SQLITE_IOERR
3535035972
);
3535135973
if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
3535235974
pPager->errCode = rc;
35975
+ pPager->eState = PAGER_ERROR;
3535335976
}
3535435977
return rc;
3535535978
}
3535635979
35357
-/*
35358
-** Execute a rollback if a transaction is active and unlock the
35359
-** database file.
35360
-**
35361
-** If the pager has already entered the error state, do not attempt
35362
-** the rollback at this time. Instead, pager_unlock() is called. The
35363
-** call to pager_unlock() will discard all in-memory pages, unlock
35364
-** the database file and clear the error state. If this means that
35365
-** there is a hot-journal left in the file-system, the next connection
35366
-** to obtain a shared lock on the pager (which may be this one) will
35367
-** roll it back.
35368
-**
35369
-** If the pager has not already entered the error state, but an IO or
35370
-** malloc error occurs during a rollback, then this will itself cause
35371
-** the pager to enter the error state. Which will be cleared by the
35372
-** call to pager_unlock(), as described above.
35373
-*/
35374
-static void pagerUnlockAndRollback(Pager *pPager){
35375
- if( pPager->errCode==SQLITE_OK && pPager->state>=PAGER_RESERVED ){
35376
- sqlite3BeginBenignMalloc();
35377
- sqlite3PagerRollback(pPager);
35378
- sqlite3EndBenignMalloc();
35379
- }
35380
- pager_unlock(pPager);
35381
-}
35382
-
3538335980
/*
3538435981
** This routine ends a transaction. A transaction is usually ended by
3538535982
** either a COMMIT or a ROLLBACK operation. This routine may be called
3538635983
** after rollback of a hot-journal, or if an error occurs while opening
3538735984
** the journal file or writing the very first journal-header of a
3538835985
** database transaction.
3538935986
**
35390
-** If the pager is in PAGER_SHARED or PAGER_UNLOCK state when this
35391
-** routine is called, it is a no-op (returns SQLITE_OK).
35987
+** This routine is never called in PAGER_ERROR state. If it is called
35988
+** in PAGER_NONE or PAGER_SHARED state and the lock held is less
35989
+** exclusive than a RESERVED lock, it is a no-op.
3539235990
**
3539335991
** Otherwise, any active savepoints are released.
3539435992
**
3539535993
** If the journal file is open, then it is "finalized". Once a journal
3539635994
** file has been finalized it is not possible to use it to roll back a
@@ -35417,17 +36015,13 @@
3541736015
** If the pager is running in exclusive mode, this method of finalizing
3541836016
** the journal file is never used. Instead, if the journalMode is
3541936017
** DELETE and the pager is in exclusive mode, the method described under
3542036018
** journalMode==PERSIST is used instead.
3542136019
**
35422
-** After the journal is finalized, if running in non-exclusive mode, the
35423
-** pager moves to PAGER_SHARED state (and downgrades the lock on the
35424
-** database file accordingly).
35425
-**
35426
-** If the pager is running in exclusive mode and is in PAGER_SYNCED state,
35427
-** it moves to PAGER_EXCLUSIVE. No locks are downgraded when running in
35428
-** exclusive mode.
36020
+** After the journal is finalized, the pager moves to PAGER_READER state.
36021
+** If running in non-exclusive rollback mode, the lock on the file is
36022
+** downgraded to a SHARED_LOCK.
3542936023
**
3543036024
** SQLITE_OK is returned if no error occurs. If an error occurs during
3543136025
** any of the IO operations to finalize the journal file or unlock the
3543236026
** database then the IO error code is returned to the user. If the
3543336027
** operation to finalize the journal file fails, then the code still
@@ -35438,15 +36032,30 @@
3543836032
*/
3543936033
static int pager_end_transaction(Pager *pPager, int hasMaster){
3544036034
int rc = SQLITE_OK; /* Error code from journal finalization operation */
3544136035
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
3544236036
35443
- if( pPager->state<PAGER_RESERVED ){
36037
+ /* Do nothing if the pager does not have an open write transaction
36038
+ ** or at least a RESERVED lock. This function may be called when there
36039
+ ** is no write-transaction active but a RESERVED or greater lock is
36040
+ ** held under two circumstances:
36041
+ **
36042
+ ** 1. After a successful hot-journal rollback, it is called with
36043
+ ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK.
36044
+ **
36045
+ ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE
36046
+ ** lock switches back to locking_mode=normal and then executes a
36047
+ ** read-transaction, this function is called with eState==PAGER_READER
36048
+ ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed.
36049
+ */
36050
+ assert( assert_pager_state(pPager) );
36051
+ assert( pPager->eState!=PAGER_ERROR );
36052
+ if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){
3544436053
return SQLITE_OK;
3544536054
}
36055
+
3544636056
releaseAllSavepoints(pPager);
35447
-
3544836057
assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
3544936058
if( isOpen(pPager->jfd) ){
3545036059
assert( !pagerUseWal(pPager) );
3545136060
3545236061
/* Finalize the journal file. */
@@ -35458,18 +36067,15 @@
3545836067
rc = SQLITE_OK;
3545936068
}else{
3546036069
rc = sqlite3OsTruncate(pPager->jfd, 0);
3546136070
}
3546236071
pPager->journalOff = 0;
35463
- pPager->journalStarted = 0;
3546436072
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
3546536073
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
3546636074
){
3546736075
rc = zeroJournalHdr(pPager, hasMaster);
35468
- pager_error(pPager, rc);
3546936076
pPager->journalOff = 0;
35470
- pPager->journalStarted = 0;
3547136077
}else{
3547236078
/* This branch may be executed with Pager.journalMode==MEMORY if
3547336079
** a hot-journal was just rolled back. In this case the journal
3547436080
** file should be closed and deleted. If this connection writes to
3547536081
** the database file, it will do so using an in-memory journal.
@@ -35481,52 +36087,80 @@
3548136087
sqlite3OsClose(pPager->jfd);
3548236088
if( !pPager->tempFile ){
3548336089
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
3548436090
}
3548536091
}
36092
+ }
3548636093
3548736094
#ifdef SQLITE_CHECK_PAGES
35488
- sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
36095
+ sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
36096
+ if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){
36097
+ PgHdr *p = pager_lookup(pPager, 1);
36098
+ if( p ){
36099
+ p->pageHash = 0;
36100
+ sqlite3PagerUnref(p);
36101
+ }
36102
+ }
3548936103
#endif
35490
- }
36104
+
3549136105
sqlite3BitvecDestroy(pPager->pInJournal);
3549236106
pPager->pInJournal = 0;
3549336107
pPager->nRec = 0;
3549436108
sqlite3PcacheCleanAll(pPager->pPCache);
36109
+ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
3549536110
3549636111
if( pagerUseWal(pPager) ){
36112
+ /* Drop the WAL write-lock, if any. Also, if the connection was in
36113
+ ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
36114
+ ** lock held on the database file.
36115
+ */
3549736116
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
3549836117
assert( rc2==SQLITE_OK );
35499
- pPager->state = PAGER_SHARED;
35500
-
35501
- /* If the connection was in locking_mode=exclusive mode but is no longer,
35502
- ** drop the EXCLUSIVE lock held on the database file.
35503
- */
35504
- if( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){
35505
- rc2 = osUnlock(pPager->fd, SHARED_LOCK);
35506
- }
35507
- }else if( !pPager->exclusiveMode ){
35508
- rc2 = osUnlock(pPager->fd, SHARED_LOCK);
35509
- pPager->state = PAGER_SHARED;
36118
+ }
36119
+ if( !pPager->exclusiveMode
36120
+ && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
36121
+ ){
36122
+ rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
3551036123
pPager->changeCountDone = 0;
35511
- }else if( pPager->state==PAGER_SYNCED ){
35512
- pPager->state = PAGER_EXCLUSIVE;
35513
- }
35514
- pPager->setMaster = 0;
35515
- pPager->needSync = 0;
35516
- pPager->dbModified = 0;
35517
-
35518
- /* TODO: Is this optimal? Why is the db size invalidated here
35519
- ** when the database file is not unlocked? */
35520
- pPager->dbOrigSize = 0;
35521
- sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
35522
- if( !MEMDB ){
35523
- pPager->dbSizeValid = 0;
35524
- }
36124
+ }
36125
+ pPager->eState = PAGER_READER;
36126
+ pPager->setMaster = 0;
3552536127
3552636128
return (rc==SQLITE_OK?rc2:rc);
3552736129
}
36130
+
36131
+/*
36132
+** Execute a rollback if a transaction is active and unlock the
36133
+** database file.
36134
+**
36135
+** If the pager has already entered the ERROR state, do not attempt
36136
+** the rollback at this time. Instead, pager_unlock() is called. The
36137
+** call to pager_unlock() will discard all in-memory pages, unlock
36138
+** the database file and move the pager back to OPEN state. If this
36139
+** means that there is a hot-journal left in the file-system, the next
36140
+** connection to obtain a shared lock on the pager (which may be this one)
36141
+** will roll it back.
36142
+**
36143
+** If the pager has not already entered the ERROR state, but an IO or
36144
+** malloc error occurs during a rollback, then this will itself cause
36145
+** the pager to enter the ERROR state. Which will be cleared by the
36146
+** call to pager_unlock(), as described above.
36147
+*/
36148
+static void pagerUnlockAndRollback(Pager *pPager){
36149
+ if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){
36150
+ assert( assert_pager_state(pPager) );
36151
+ if( pPager->eState>=PAGER_WRITER_LOCKED ){
36152
+ sqlite3BeginBenignMalloc();
36153
+ sqlite3PagerRollback(pPager);
36154
+ sqlite3EndBenignMalloc();
36155
+ }else if( !pPager->exclusiveMode ){
36156
+ assert( pPager->eState==PAGER_READER );
36157
+ pager_end_transaction(pPager, 0);
36158
+ }
36159
+ }
36160
+ pager_unlock(pPager);
36161
+}
3552836162
3552936163
/*
3553036164
** Parameter aData must point to a buffer of pPager->pageSize bytes
3553136165
** of data. Compute and return a checksum based ont the contents of the
3553236166
** page of data and the current value of pPager->cksumInit.
@@ -35574,13 +36208,12 @@
3557436208
** Read a single page from either the journal file (if isMainJrnl==1) or
3557536209
** from the sub-journal (if isMainJrnl==0) and playback that page.
3557636210
** The page begins at offset *pOffset into the file. The *pOffset
3557736211
** value is increased to the start of the next page in the journal.
3557836212
**
35579
-** The isMainJrnl flag is true if this is the main rollback journal and
35580
-** false for the statement journal. The main rollback journal uses
35581
-** checksums - the statement journal does not.
36213
+** The main rollback journal uses checksums - the statement journal does
36214
+** not.
3558236215
**
3558336216
** If the page number of the page record read from the (sub-)journal file
3558436217
** is greater than the current value of Pager.dbSize, then playback is
3558536218
** skipped and SQLITE_OK is returned.
3558636219
**
@@ -35629,10 +36262,21 @@
3562936262
assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */
3563036263
3563136264
aData = pPager->pTmpSpace;
3563236265
assert( aData ); /* Temp storage must have already been allocated */
3563336266
assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) );
36267
+
36268
+ /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction
36269
+ ** or savepoint rollback done at the request of the caller) or this is
36270
+ ** a hot-journal rollback. If it is a hot-journal rollback, the pager
36271
+ ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback
36272
+ ** only reads from the main journal, not the sub-journal.
36273
+ */
36274
+ assert( pPager->eState>=PAGER_WRITER_CACHEMOD
36275
+ || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK)
36276
+ );
36277
+ assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl );
3563436278
3563536279
/* Read the page number and page data from the journal or sub-journal
3563636280
** file. Return an error code to the caller if an IO error occurs.
3563736281
*/
3563836282
jfd = isMainJrnl ? pPager->jfd : pPager->sjfd;
@@ -35666,20 +36310,19 @@
3566636310
** rollback, then don't bother to play it back again.
3566736311
*/
3566836312
if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
3566936313
return rc;
3567036314
}
35671
- assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
3567236315
3567336316
/* When playing back page 1, restore the nReserve setting
3567436317
*/
3567536318
if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){
3567636319
pPager->nReserve = ((u8*)aData)[20];
3567736320
pagerReportSize(pPager);
3567836321
}
3567936322
35680
- /* If the pager is in RESERVED state, then there must be a copy of this
36323
+ /* If the pager is in CACHEMOD state, then there must be a copy of this
3568136324
** page in the pager cache. In this case just update the pager cache,
3568236325
** not the database file. The page is left marked dirty in this case.
3568336326
**
3568436327
** An exception to the above rule: If the database is in no-sync mode
3568536328
** and a page is moved during an incremental vacuum then the page may
@@ -35686,12 +36329,15 @@
3568636329
** not be in the pager cache. Later: if a malloc() or IO error occurs
3568736330
** during a Movepage() call, then the page may not be in the cache
3568836331
** either. So the condition described in the above paragraph is not
3568936332
** assert()able.
3569036333
**
35691
- ** If in EXCLUSIVE state, then we update the pager cache if it exists
35692
- ** and the main file. The page is then marked not dirty.
36334
+ ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the
36335
+ ** pager cache if it exists and the main file. The page is then marked
36336
+ ** not dirty. Since this code is only executed in PAGER_OPEN state for
36337
+ ** a hot-journal rollback, it is guaranteed that the page-cache is empty
36338
+ ** if the pager is in OPEN state.
3569336339
**
3569436340
** Ticket #1171: The statement journal might contain page content that is
3569536341
** different from the page content at the start of the transaction.
3569636342
** This occurs when a page is changed prior to the start of a statement
3569736343
** then changed again within the statement. When rolling back such a
@@ -35713,21 +36359,22 @@
3571336359
pPg = 0;
3571436360
}else{
3571536361
pPg = pager_lookup(pPager, pgno);
3571636362
}
3571736363
assert( pPg || !MEMDB );
36364
+ assert( pPager->eState!=PAGER_OPEN || pPg==0 );
3571836365
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
3571936366
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
3572036367
(isMainJrnl?"main-journal":"sub-journal")
3572136368
));
3572236369
if( isMainJrnl ){
3572336370
isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr);
3572436371
}else{
3572536372
isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
3572636373
}
35727
- if( (pPager->state>=PAGER_EXCLUSIVE)
35728
- && isOpen(pPager->fd)
36374
+ if( isOpen(pPager->fd)
36375
+ && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
3572936376
&& isSynced
3573036377
){
3573136378
i64 ofst = (pgno-1)*(i64)pPager->pageSize;
3573236379
testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
3573336380
assert( !pagerUseWal(pPager) );
@@ -35799,13 +36446,12 @@
3579936446
** database corruption may ensue.
3580036447
*/
3580136448
assert( !pagerUseWal(pPager) );
3580236449
sqlite3PcacheMakeClean(pPg);
3580336450
}
35804
-#ifdef SQLITE_CHECK_PAGES
35805
- pPg->pageHash = pager_pagehash(pPg);
35806
-#endif
36451
+ pager_set_pagehash(pPg);
36452
+
3580736453
/* If this was page 1, then restore the value of Pager.dbFileVers.
3580836454
** Do this before any decoding. */
3580936455
if( pgno==1 ){
3581036456
memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
3581136457
}
@@ -35953,14 +36599,14 @@
3595336599
/*
3595436600
** This function is used to change the actual size of the database
3595536601
** file in the file-system. This only happens when committing a transaction,
3595636602
** or rolling back a transaction (including rolling back a hot-journal).
3595736603
**
35958
-** If the main database file is not open, or an exclusive lock is not
35959
-** held, this function is a no-op. Otherwise, the size of the file is
35960
-** changed to nPage pages (nPage*pPager->pageSize bytes). If the file
35961
-** on disk is currently larger than nPage pages, then use the VFS
36604
+** If the main database file is not open, or the pager is not in either
36605
+** DBMOD or OPEN state, this function is a no-op. Otherwise, the size
36606
+** of the file is changed to nPage pages (nPage*pPager->pageSize bytes).
36607
+** If the file on disk is currently larger than nPage pages, then use the VFS
3596236608
** xTruncate() method to truncate it.
3596336609
**
3596436610
** Or, it might might be the case that the file on disk is smaller than
3596536611
** nPage pages. Some operating system implementations can get confused if
3596636612
** you try to truncate a file to some size that is larger than it
@@ -35970,12 +36616,18 @@
3597036616
** If successful, return SQLITE_OK. If an IO error occurs while modifying
3597136617
** the database file, return the error code to the caller.
3597236618
*/
3597336619
static int pager_truncate(Pager *pPager, Pgno nPage){
3597436620
int rc = SQLITE_OK;
35975
- if( pPager->state>=PAGER_EXCLUSIVE && isOpen(pPager->fd) ){
36621
+ assert( pPager->eState!=PAGER_ERROR );
36622
+ assert( pPager->eState!=PAGER_READER );
36623
+
36624
+ if( isOpen(pPager->fd)
36625
+ && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
36626
+ ){
3597636627
i64 currentSize, newSize;
36628
+ assert( pPager->eLock==EXCLUSIVE_LOCK );
3597736629
/* TODO: Is it safe to use Pager.dbFileSize here? */
3597836630
rc = sqlite3OsFileSize(pPager->fd, &currentSize);
3597936631
newSize = pPager->pageSize*(i64)nPage;
3598036632
if( rc==SQLITE_OK && currentSize!=newSize ){
3598136633
if( currentSize>newSize ){
@@ -36095,11 +36747,11 @@
3609536747
/* Figure out how many records are in the journal. Abort early if
3609636748
** the journal is empty.
3609736749
*/
3609836750
assert( isOpen(pPager->jfd) );
3609936751
rc = sqlite3OsFileSize(pPager->jfd, &szJ);
36100
- if( rc!=SQLITE_OK || szJ==0 ){
36752
+ if( rc!=SQLITE_OK ){
3610136753
goto end_playback;
3610236754
}
3610336755
3610436756
/* Read the master journal name from the journal, if it is present.
3610536757
** If a master journal file name is specified, but the file is not
@@ -36129,11 +36781,11 @@
3612936781
** occurs.
3613036782
*/
3613136783
while( 1 ){
3613236784
/* Read the next journal header from the journal file. If there are
3613336785
** not enough bytes left in the journal file for a complete header, or
36134
- ** it is corrupted, then a process must of failed while writing it.
36786
+ ** it is corrupted, then a process must have failed while writing it.
3613536787
** This indicates nothing more needs to be rolled back.
3613636788
*/
3613736789
rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
3613836790
if( rc!=SQLITE_OK ){
3613936791
if( rc==SQLITE_DONE ){
@@ -36243,14 +36895,13 @@
3624336895
if( rc==SQLITE_OK ){
3624436896
zMaster = pPager->pTmpSpace;
3624536897
rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
3624636898
testcase( rc!=SQLITE_OK );
3624736899
}
36248
- if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
36249
- rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
36250
- }
36251
- if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
36900
+ if( rc==SQLITE_OK && !pPager->noSync
36901
+ && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
36902
+ ){
3625236903
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
3625336904
}
3625436905
if( rc==SQLITE_OK ){
3625536906
rc = pager_end_transaction(pPager, zMaster[0]!='\0');
3625636907
testcase( rc!=SQLITE_OK );
@@ -36288,11 +36939,11 @@
3628836939
Pgno pgno = pPg->pgno; /* Page number to read */
3628936940
int rc = SQLITE_OK; /* Return code */
3629036941
int isInWal = 0; /* True if page is in log file */
3629136942
int pgsz = pPager->pageSize; /* Number of bytes to read */
3629236943
36293
- assert( pPager->state>=PAGER_SHARED && !MEMDB );
36944
+ assert( pPager->eState>=PAGER_READER && !MEMDB );
3629436945
assert( isOpen(pPager->fd) );
3629536946
3629636947
if( NEVER(!isOpen(pPager->fd)) ){
3629736948
assert( pPager->tempFile );
3629836949
memset(pPg->pData, 0, pPager->pageSize);
@@ -36435,10 +37086,18 @@
3643537086
PgHdr *p;
3643637087
for(p=pList; p; p=p->pDirty){
3643737088
sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
3643837089
}
3643937090
}
37091
+
37092
+#ifdef SQLITE_CHECK_PAGES
37093
+ {
37094
+ PgHdr *p;
37095
+ for(p=pList; p; p=p->pDirty) pager_set_pagehash(p);
37096
+ }
37097
+#endif
37098
+
3644037099
return rc;
3644137100
}
3644237101
3644337102
/*
3644437103
** Begin a read transaction on the WAL.
@@ -36451,31 +37110,82 @@
3645137110
static int pagerBeginReadTransaction(Pager *pPager){
3645237111
int rc; /* Return code */
3645337112
int changed = 0; /* True if cache must be reset */
3645437113
3645537114
assert( pagerUseWal(pPager) );
37115
+ assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
3645637116
3645737117
/* sqlite3WalEndReadTransaction() was not called for the previous
3645837118
** transaction in locking_mode=EXCLUSIVE. So call it now. If we
3645937119
** are in locking_mode=NORMAL and EndRead() was previously called,
3646037120
** the duplicate call is harmless.
3646137121
*/
3646237122
sqlite3WalEndReadTransaction(pPager->pWal);
3646337123
3646437124
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
36465
- if( rc==SQLITE_OK ){
36466
- int dummy;
36467
- if( changed ){
36468
- pager_reset(pPager);
36469
- assert( pPager->errCode || pPager->dbSizeValid==0 );
36470
- }
36471
- rc = sqlite3PagerPagecount(pPager, &dummy);
36472
- }
36473
- pPager->state = PAGER_SHARED;
37125
+ if( rc==SQLITE_OK && changed ){
37126
+ pager_reset(pPager);
37127
+ }
3647437128
3647537129
return rc;
3647637130
}
37131
+
37132
+/*
37133
+** This function is called as part of the transition from PAGER_OPEN
37134
+** to PAGER_READER state to determine the size of the database file
37135
+** in pages (assuming the page size currently stored in Pager.pageSize).
37136
+**
37137
+** If no error occurs, SQLITE_OK is returned and the size of the database
37138
+** in pages is stored in *pnPage. Otherwise, an error code (perhaps
37139
+** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified.
37140
+*/
37141
+static int pagerPagecount(Pager *pPager, Pgno *pnPage){
37142
+ Pgno nPage; /* Value to return via *pnPage */
37143
+
37144
+ /* Query the WAL sub-system for the database size. The WalDbsize()
37145
+ ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or
37146
+ ** if the database size is not available. The database size is not
37147
+ ** available from the WAL sub-system if the log file is empty or
37148
+ ** contains no valid committed transactions.
37149
+ */
37150
+ assert( pPager->eState==PAGER_OPEN );
37151
+ assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
37152
+ nPage = sqlite3WalDbsize(pPager->pWal);
37153
+
37154
+ /* If the database size was not available from the WAL sub-system,
37155
+ ** determine it based on the size of the database file. If the size
37156
+ ** of the database file is not an integer multiple of the page-size,
37157
+ ** round down to the nearest page. Except, any file larger than 0
37158
+ ** bytes in size is considered to contain at least one page.
37159
+ */
37160
+ if( nPage==0 ){
37161
+ i64 n = 0; /* Size of db file in bytes */
37162
+ assert( isOpen(pPager->fd) || pPager->tempFile );
37163
+ if( isOpen(pPager->fd) ){
37164
+ int rc = sqlite3OsFileSize(pPager->fd, &n);
37165
+ if( rc!=SQLITE_OK ){
37166
+ return rc;
37167
+ }
37168
+ }
37169
+ nPage = (Pgno)(n / pPager->pageSize);
37170
+ if( nPage==0 && n>0 ){
37171
+ nPage = 1;
37172
+ }
37173
+ }
37174
+
37175
+ /* If the current number of pages in the file is greater than the
37176
+ ** configured maximum pager number, increase the allowed limit so
37177
+ ** that the file can be read.
37178
+ */
37179
+ if( nPage>pPager->mxPgno ){
37180
+ pPager->mxPgno = (Pgno)nPage;
37181
+ }
37182
+
37183
+ *pnPage = nPage;
37184
+ return SQLITE_OK;
37185
+}
37186
+
3647737187
3647837188
/*
3647937189
** Check if the *-wal file that corresponds to the database opened by pPager
3648037190
** exists if the database is not empy, or verify that the *-wal file does
3648137191
** not exist (by deleting it) if the database file is empty.
@@ -36485,25 +37195,26 @@
3648537195
** if no error occurs, make sure Pager.journalMode is not set to
3648637196
** PAGER_JOURNALMODE_WAL.
3648737197
**
3648837198
** Return SQLITE_OK or an error code.
3648937199
**
36490
-** If the WAL file is opened, also open a snapshot (read transaction).
36491
-**
3649237200
** The caller must hold a SHARED lock on the database file to call this
3649337201
** function. Because an EXCLUSIVE lock on the db file is required to delete
3649437202
** a WAL on a none-empty database, this ensures there is no race condition
3649537203
** between the xAccess() below and an xDelete() being executed by some
3649637204
** other connection.
3649737205
*/
3649837206
static int pagerOpenWalIfPresent(Pager *pPager){
3649937207
int rc = SQLITE_OK;
37208
+ assert( pPager->eState==PAGER_OPEN );
37209
+ assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
37210
+
3650037211
if( !pPager->tempFile ){
3650137212
int isWal; /* True if WAL file exists */
36502
- int nPage; /* Size of the database file */
36503
- assert( pPager->state>=SHARED_LOCK );
36504
- rc = sqlite3PagerPagecount(pPager, &nPage);
37213
+ Pgno nPage; /* Size of the database file */
37214
+
37215
+ rc = pagerPagecount(pPager, &nPage);
3650537216
if( rc ) return rc;
3650637217
if( nPage==0 ){
3650737218
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
3650837219
isWal = 0;
3650937220
}else{
@@ -36511,15 +37222,12 @@
3651137222
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal
3651237223
);
3651337224
}
3651437225
if( rc==SQLITE_OK ){
3651537226
if( isWal ){
36516
- pager_reset(pPager);
37227
+ testcase( sqlite3PcachePagecount(pPager->pPCache)==0 );
3651737228
rc = sqlite3PagerOpenWal(pPager, 0);
36518
- if( rc==SQLITE_OK ){
36519
- rc = pagerBeginReadTransaction(pPager);
36520
- }
3652137229
}else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
3652237230
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
3652337231
}
3652437232
}
3652537233
}
@@ -36567,11 +37275,12 @@
3656737275
i64 szJ; /* Effective size of the main journal */
3656837276
i64 iHdrOff; /* End of first segment of main-journal records */
3656937277
int rc = SQLITE_OK; /* Return code */
3657037278
Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */
3657137279
36572
- assert( pPager->state>=PAGER_SHARED );
37280
+ assert( pPager->eState!=PAGER_ERROR );
37281
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
3657337282
3657437283
/* Allocate a bitvec to use to store the set of pages rolled back */
3657537284
if( pSavepoint ){
3657637285
pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
3657737286
if( !pDone ){
@@ -36706,11 +37415,10 @@
3670637415
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
3670737416
SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
3670837417
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
3670937418
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
3671037419
pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
36711
- if( pPager->noSync ) pPager->needSync = 0;
3671237420
}
3671337421
#endif
3671437422
3671537423
/*
3671637424
** The following global variable is incremented whenever the library
@@ -36788,11 +37496,11 @@
3678837496
** Change the page size used by the Pager object. The new page size
3678937497
** is passed in *pPageSize.
3679037498
**
3679137499
** If the pager is in the error state when this function is called, it
3679237500
** is a no-op. The value returned is the error state error code (i.e.
36793
-** one of SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_FULL).
37501
+** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL).
3679437502
**
3679537503
** Otherwise, if all of the following are true:
3679637504
**
3679737505
** * the new page size (value of *pPageSize) is valid (a power
3679837506
** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and
@@ -36812,32 +37520,52 @@
3681237520
** If the page size is not changed, either because one of the enumerated
3681337521
** conditions above is not true, the pager was in error state when this
3681437522
** function was called, or because the memory allocation attempt failed,
3681537523
** then *pPageSize is set to the old, retained page size before returning.
3681637524
*/
36817
-SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){
36818
- int rc = pPager->errCode;
36819
-
36820
- if( rc==SQLITE_OK ){
36821
- u16 pageSize = *pPageSize;
36822
- assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
36823
- if( (pPager->memDb==0 || pPager->dbSize==0)
36824
- && sqlite3PcacheRefCount(pPager->pPCache)==0
36825
- && pageSize && pageSize!=pPager->pageSize
36826
- ){
36827
- char *pNew = (char *)sqlite3PageMalloc(pageSize);
36828
- if( !pNew ){
36829
- rc = SQLITE_NOMEM;
36830
- }else{
36831
- pager_reset(pPager);
36832
- pPager->pageSize = pageSize;
36833
- sqlite3PageFree(pPager->pTmpSpace);
36834
- pPager->pTmpSpace = pNew;
36835
- sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
36836
- }
36837
- }
36838
- *pPageSize = (u16)pPager->pageSize;
37525
+SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
37526
+ int rc = SQLITE_OK;
37527
+
37528
+ /* It is not possible to do a full assert_pager_state() here, as this
37529
+ ** function may be called from within PagerOpen(), before the state
37530
+ ** of the Pager object is internally consistent.
37531
+ **
37532
+ ** At one point this function returned an error if the pager was in
37533
+ ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that
37534
+ ** there is at least one outstanding page reference, this function
37535
+ ** is a no-op for that case anyhow.
37536
+ */
37537
+
37538
+ u32 pageSize = *pPageSize;
37539
+ assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
37540
+ if( (pPager->memDb==0 || pPager->dbSize==0)
37541
+ && sqlite3PcacheRefCount(pPager->pPCache)==0
37542
+ && pageSize && pageSize!=(u32)pPager->pageSize
37543
+ ){
37544
+ char *pNew = NULL; /* New temp space */
37545
+ i64 nByte = 0;
37546
+
37547
+ if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){
37548
+ rc = sqlite3OsFileSize(pPager->fd, &nByte);
37549
+ }
37550
+ if( rc==SQLITE_OK ){
37551
+ pNew = (char *)sqlite3PageMalloc(pageSize);
37552
+ if( !pNew ) rc = SQLITE_NOMEM;
37553
+ }
37554
+
37555
+ if( rc==SQLITE_OK ){
37556
+ pager_reset(pPager);
37557
+ pPager->dbSize = (Pgno)(nByte/pageSize);
37558
+ pPager->pageSize = pageSize;
37559
+ sqlite3PageFree(pPager->pTmpSpace);
37560
+ pPager->pTmpSpace = pNew;
37561
+ sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
37562
+ }
37563
+ }
37564
+
37565
+ *pPageSize = pPager->pageSize;
37566
+ if( rc==SQLITE_OK ){
3683937567
if( nReserve<0 ) nReserve = pPager->nReserve;
3684037568
assert( nReserve>=0 && nReserve<1000 );
3684137569
pPager->nReserve = (i16)nReserve;
3684237570
pagerReportSize(pPager);
3684337571
}
@@ -36862,17 +37590,15 @@
3686237590
** maximum page count below the current size of the database.
3686337591
**
3686437592
** Regardless of mxPage, return the current maximum page count.
3686537593
*/
3686637594
SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
36867
- int nPage;
3686837595
if( mxPage>0 ){
3686937596
pPager->mxPgno = mxPage;
3687037597
}
36871
- if( pPager->state!=PAGER_UNLOCK ){
36872
- sqlite3PagerPagecount(pPager, &nPage);
36873
- assert( (int)pPager->mxPgno>=nPage );
37598
+ if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){
37599
+ pPager->mxPgno = pPager->dbSize;
3687437600
}
3687537601
return pPager->mxPgno;
3687637602
}
3687737603
3687837604
/*
@@ -36933,70 +37659,20 @@
3693337659
}
3693437660
return rc;
3693537661
}
3693637662
3693737663
/*
36938
-** Return the total number of pages in the database file associated
36939
-** with pPager. Normally, this is calculated as (<db file size>/<page-size>).
37664
+** This function may only be called when a read-transaction is open on
37665
+** the pager. It returns the total number of pages in the database.
37666
+**
3694037667
** However, if the file is between 1 and <page-size> bytes in size, then
3694137668
** this is considered a 1 page file.
36942
-**
36943
-** If the pager is in error state when this function is called, then the
36944
-** error state error code is returned and *pnPage left unchanged. Or,
36945
-** if the file system has to be queried for the size of the file and
36946
-** the query attempt returns an IO error, the IO error code is returned
36947
-** and *pnPage is left unchanged.
36948
-**
36949
-** Otherwise, if everything is successful, then SQLITE_OK is returned
36950
-** and *pnPage is set to the number of pages in the database.
36951
-*/
36952
-SQLITE_PRIVATE int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
36953
- Pgno nPage = 0; /* Value to return via *pnPage */
36954
-
36955
- /* Determine the number of pages in the file. Store this in nPage. */
36956
- if( pPager->dbSizeValid ){
36957
- nPage = pPager->dbSize;
36958
- }else{
36959
- int rc; /* Error returned by OsFileSize() */
36960
- i64 n = 0; /* File size in bytes returned by OsFileSize() */
36961
-
36962
- if( pagerUseWal(pPager) && pPager->state!=PAGER_UNLOCK ){
36963
- sqlite3WalDbsize(pPager->pWal, &nPage);
36964
- }
36965
-
36966
- if( nPage==0 ){
36967
- assert( isOpen(pPager->fd) || pPager->tempFile );
36968
- if( isOpen(pPager->fd) ){
36969
- if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){
36970
- pager_error(pPager, rc);
36971
- return rc;
36972
- }
36973
- }
36974
- if( n>0 && n<pPager->pageSize ){
36975
- nPage = 1;
36976
- }else{
36977
- nPage = (Pgno)(n / pPager->pageSize);
36978
- }
36979
- }
36980
- if( pPager->state!=PAGER_UNLOCK ){
36981
- pPager->dbSize = nPage;
36982
- pPager->dbFileSize = nPage;
36983
- pPager->dbSizeValid = 1;
36984
- }
36985
- }
36986
-
36987
- /* If the current number of pages in the file is greater than the
36988
- ** configured maximum pager number, increase the allowed limit so
36989
- ** that the file can be read.
36990
- */
36991
- if( nPage>pPager->mxPgno ){
36992
- pPager->mxPgno = (Pgno)nPage;
36993
- }
36994
-
36995
- /* Set the output variable and return SQLITE_OK */
36996
- *pnPage = nPage;
36997
- return SQLITE_OK;
37669
+*/
37670
+SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){
37671
+ assert( pPager->eState>=PAGER_READER );
37672
+ assert( pPager->eState!=PAGER_WRITER_FINISHED );
37673
+ *pnPage = (int)pPager->dbSize;
3699837674
}
3699937675
3700037676
3700137677
/*
3700237678
** Try to obtain a lock of type locktype on the database file. If
@@ -37013,42 +37689,23 @@
3701337689
** variable to locktype before returning.
3701437690
*/
3701537691
static int pager_wait_on_lock(Pager *pPager, int locktype){
3701637692
int rc; /* Return code */
3701737693
37018
- /* The OS lock values must be the same as the Pager lock values */
37019
- assert( PAGER_SHARED==SHARED_LOCK );
37020
- assert( PAGER_RESERVED==RESERVED_LOCK );
37021
- assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
37022
-
37023
- /* If the file is currently unlocked then the size must be unknown. It
37024
- ** must not have been modified at this point.
37025
- */
37026
- assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 );
37027
- assert( pPager->state>=PAGER_SHARED || pPager->dbModified==0 );
37028
-
3702937694
/* Check that this is either a no-op (because the requested lock is
3703037695
** already held, or one of the transistions that the busy-handler
3703137696
** may be invoked during, according to the comment above
3703237697
** sqlite3PagerSetBusyhandler().
3703337698
*/
37034
- assert( (pPager->state>=locktype)
37035
- || (pPager->state==PAGER_UNLOCK && locktype==PAGER_SHARED)
37036
- || (pPager->state==PAGER_RESERVED && locktype==PAGER_EXCLUSIVE)
37699
+ assert( (pPager->eLock>=locktype)
37700
+ || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK)
37701
+ || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
3703737702
);
3703837703
37039
- if( pPager->state>=locktype ){
37040
- rc = SQLITE_OK;
37041
- }else{
37042
- do {
37043
- rc = sqlite3OsLock(pPager->fd, locktype);
37044
- }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
37045
- if( rc==SQLITE_OK ){
37046
- pPager->state = (u8)locktype;
37047
- IOTRACE(("LOCK %p %d\n", pPager, locktype))
37048
- }
37049
- }
37704
+ do {
37705
+ rc = pagerLockDb(pPager, locktype);
37706
+ }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
3705037707
return rc;
3705137708
}
3705237709
3705337710
/*
3705437711
** Function assertTruncateConstraint(pPager) checks that one of the
@@ -37089,13 +37746,12 @@
3708937746
** function does not actually modify the database file on disk. It
3709037747
** just sets the internal state of the pager object so that the
3709137748
** truncation will be done when the current transaction is committed.
3709237749
*/
3709337750
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
37094
- assert( pPager->dbSizeValid );
3709537751
assert( pPager->dbSize>=nPage );
37096
- assert( pPager->state>=PAGER_RESERVED );
37752
+ assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
3709737753
pPager->dbSize = nPage;
3709837754
assertTruncateConstraint(pPager);
3709937755
}
3710037756
3710137757
@@ -37141,11 +37797,11 @@
3714137797
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
3714237798
u8 *pTmp = (u8 *)pPager->pTmpSpace;
3714337799
3714437800
disable_simulated_io_errors();
3714537801
sqlite3BeginBenignMalloc();
37146
- pPager->errCode = 0;
37802
+ /* pPager->errCode = 0; */
3714737803
pPager->exclusiveMode = 0;
3714837804
#ifndef SQLITE_OMIT_WAL
3714937805
sqlite3WalClose(pPager->pWal,
3715037806
(pPager->noSync ? 0 : pPager->sync_flags),
3715137807
pPager->pageSize, pTmp
@@ -37154,18 +37810,23 @@
3715437810
#endif
3715537811
pager_reset(pPager);
3715637812
if( MEMDB ){
3715737813
pager_unlock(pPager);
3715837814
}else{
37159
- /* Set Pager.journalHdr to -1 for the benefit of the pager_playback()
37160
- ** call which may be made from within pagerUnlockAndRollback(). If it
37161
- ** is not -1, then the unsynced portion of an open journal file may
37162
- ** be played back into the database. If a power failure occurs while
37163
- ** this is happening, the database may become corrupt.
37815
+ /* If it is open, sync the journal file before calling UnlockAndRollback.
37816
+ ** If this is not done, then an unsynced portion of the open journal
37817
+ ** file may be played back into the database. If a power failure occurs
37818
+ ** while this is happening, the database could become corrupt.
37819
+ **
37820
+ ** If an error occurs while trying to sync the journal, shift the pager
37821
+ ** into the ERROR state. This causes UnlockAndRollback to unlock the
37822
+ ** database and close the journal file without attempting to roll it
37823
+ ** back or finalize it. The next database user will have to do hot-journal
37824
+ ** rollback before accessing the database file.
3716437825
*/
3716537826
if( isOpen(pPager->jfd) ){
37166
- pPager->errCode = pagerSyncHotJournal(pPager);
37827
+ pager_error(pPager, pagerSyncHotJournal(pPager));
3716737828
}
3716837829
pagerUnlockAndRollback(pPager);
3716937830
}
3717037831
sqlite3EndBenignMalloc();
3717137832
enable_simulated_io_errors();
@@ -37206,13 +37867,13 @@
3720637867
/*
3720737868
** Sync the journal. In other words, make sure all the pages that have
3720837869
** been written to the journal have actually reached the surface of the
3720937870
** disk and can be restored in the event of a hot-journal rollback.
3721037871
**
37211
-** If the Pager.needSync flag is not set, then this function is a
37212
-** no-op. Otherwise, the actions required depend on the journal-mode
37213
-** and the device characteristics of the the file-system, as follows:
37872
+** If the Pager.noSync flag is set, then this function is a no-op.
37873
+** Otherwise, the actions required depend on the journal-mode and the
37874
+** device characteristics of the the file-system, as follows:
3721437875
**
3721537876
** * If the journal file is an in-memory journal file, no action need
3721637877
** be taken.
3721737878
**
3721837879
** * Otherwise, if the device does not support the SAFE_APPEND property,
@@ -37232,22 +37893,29 @@
3723237893
** <update nRec field>
3723337894
** }
3723437895
** if( NOT SEQUENTIAL ) xSync(<journal file>);
3723537896
** }
3723637897
**
37237
-** The Pager.needSync flag is never be set for temporary files, or any
37238
-** file operating in no-sync mode (Pager.noSync set to non-zero).
37239
-**
3724037898
** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
3724137899
** page currently held in memory before returning SQLITE_OK. If an IO
3724237900
** error is encountered, then the IO error code is returned to the caller.
3724337901
*/
37244
-static int syncJournal(Pager *pPager){
37245
- if( pPager->needSync ){
37902
+static int syncJournal(Pager *pPager, int newHdr){
37903
+ int rc; /* Return code */
37904
+
37905
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
37906
+ || pPager->eState==PAGER_WRITER_DBMOD
37907
+ );
37908
+ assert( assert_pager_state(pPager) );
37909
+ assert( !pagerUseWal(pPager) );
37910
+
37911
+ rc = sqlite3PagerExclusiveLock(pPager);
37912
+ if( rc!=SQLITE_OK ) return rc;
37913
+
37914
+ if( !pPager->noSync ){
3724637915
assert( !pPager->tempFile );
37247
- if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
37248
- int rc; /* Return code */
37916
+ if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
3724937917
const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
3725037918
assert( isOpen(pPager->jfd) );
3725137919
3725237920
if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
3725337921
/* This block deals with an obscure problem. If the last connection
@@ -37318,21 +37986,29 @@
3731837986
rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
3731937987
(pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
3732037988
);
3732137989
if( rc!=SQLITE_OK ) return rc;
3732237990
}
37323
- }
37324
-
37325
- /* The journal file was just successfully synced. Set Pager.needSync
37326
- ** to zero and clear the PGHDR_NEED_SYNC flag on all pagess.
37327
- */
37328
- pPager->needSync = 0;
37329
- pPager->journalStarted = 1;
37330
- pPager->journalHdr = pPager->journalOff;
37331
- sqlite3PcacheClearSyncFlags(pPager->pPCache);
37991
+
37992
+ pPager->journalHdr = pPager->journalOff;
37993
+ if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
37994
+ pPager->nRec = 0;
37995
+ rc = writeJournalHdr(pPager);
37996
+ if( rc!=SQLITE_OK ) return rc;
37997
+ }
37998
+ }else{
37999
+ pPager->journalHdr = pPager->journalOff;
38000
+ }
3733238001
}
3733338002
38003
+ /* Unless the pager is in noSync mode, the journal file was just
38004
+ ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on
38005
+ ** all pages.
38006
+ */
38007
+ sqlite3PcacheClearSyncFlags(pPager->pPCache);
38008
+ pPager->eState = PAGER_WRITER_DBMOD;
38009
+ assert( assert_pager_state(pPager) );
3733438010
return SQLITE_OK;
3733538011
}
3733638012
3733738013
/*
3733838014
** The argument is the first in a linked list of dirty pages connected
@@ -37365,31 +38041,16 @@
3736538041
** If everything is successful, SQLITE_OK is returned. If an IO error
3736638042
** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
3736738043
** be obtained, SQLITE_BUSY is returned.
3736838044
*/
3736938045
static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
37370
- int rc; /* Return code */
37371
-
37372
- /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
37373
- ** database file. If there is already an EXCLUSIVE lock, the following
37374
- ** call is a no-op.
37375
- **
37376
- ** Moving the lock from RESERVED to EXCLUSIVE actually involves going
37377
- ** through an intermediate state PENDING. A PENDING lock prevents new
37378
- ** readers from attaching to the database but is unsufficient for us to
37379
- ** write. The idea of a PENDING lock is to prevent new readers from
37380
- ** coming in while we wait for existing readers to clear.
37381
- **
37382
- ** While the pager is in the RESERVED state, the original database file
37383
- ** is unchanged and we can rollback without having to playback the
37384
- ** journal into the original database file. Once we transition to
37385
- ** EXCLUSIVE, it means the database file has been changed and any rollback
37386
- ** will require a journal playback.
37387
- */
38046
+ int rc = SQLITE_OK; /* Return code */
38047
+
38048
+ /* This function is only called for rollback pagers in WRITER_DBMOD state. */
3738838049
assert( !pagerUseWal(pPager) );
37389
- assert( pPager->state>=PAGER_RESERVED );
37390
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
38050
+ assert( pPager->eState==PAGER_WRITER_DBMOD );
38051
+ assert( pPager->eLock==EXCLUSIVE_LOCK );
3739138052
3739238053
/* If the file is a temp-file has not yet been opened, open it now. It
3739338054
** is not possible for rc to be other than SQLITE_OK if this branch
3739438055
** is taken, as pager_wait_on_lock() is a no-op for temp-files.
3739538056
*/
@@ -37400,13 +38061,14 @@
3740038061
3740138062
/* Before the first write, give the VFS a hint of what the final
3740238063
** file size will be.
3740338064
*/
3740438065
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
37405
- if( rc==SQLITE_OK && pPager->dbSize>(pPager->dbOrigSize+1) ){
38066
+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
3740638067
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
3740738068
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
38069
+ pPager->dbHintSize = pPager->dbSize;
3740838070
}
3740938071
3741038072
while( rc==SQLITE_OK && pList ){
3741138073
Pgno pgno = pList->pgno;
3741238074
@@ -37419,10 +38081,12 @@
3741938081
** set (set by sqlite3PagerDontWrite()).
3742038082
*/
3742138083
if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
3742238084
i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */
3742338085
char *pData; /* Data to write */
38086
+
38087
+ assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
3742438088
3742538089
/* Encode the database */
3742638090
CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
3742738091
3742838092
/* Write out the page data. */
@@ -37448,13 +38112,11 @@
3744838112
PAGER_INCR(sqlite3_pager_writedb_count);
3744938113
PAGER_INCR(pPager->nWrite);
3745038114
}else{
3745138115
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
3745238116
}
37453
-#ifdef SQLITE_CHECK_PAGES
37454
- pList->pageHash = pager_pagehash(pList);
37455
-#endif
38117
+ pager_set_pagehash(pList);
3745638118
pList = pList->pDirty;
3745738119
}
3745838120
3745938121
return rc;
3746038122
}
@@ -37562,13 +38224,18 @@
3756238224
** pages belonging to the same sector.
3756338225
**
3756438226
** The doNotSpill flag inhibits all cache spilling regardless of whether
3756538227
** or not a sync is required. This is set during a rollback.
3756638228
**
37567
- ** Spilling is also inhibited when in an error state.
38229
+ ** Spilling is also prohibited when in an error state since that could
38230
+ ** lead to database corruption. In the current implementaton it
38231
+ ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
38232
+ ** while in the error state, hence it is impossible for this routine to
38233
+ ** be called in the error state. Nevertheless, we include a NEVER()
38234
+ ** test for the error state as a safeguard against future changes.
3756838235
*/
37569
- if( pPager->errCode ) return SQLITE_OK;
38236
+ if( NEVER(pPager->errCode) ) return SQLITE_OK;
3757038237
if( pPager->doNotSpill ) return SQLITE_OK;
3757138238
if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
3757238239
return SQLITE_OK;
3757338240
}
3757438241
@@ -37582,20 +38249,14 @@
3758238249
rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
3758338250
}
3758438251
}else{
3758538252
3758638253
/* Sync the journal file if required. */
37587
- if( pPg->flags&PGHDR_NEED_SYNC ){
37588
- assert( !pPager->noSync );
37589
- rc = syncJournal(pPager);
37590
- if( rc==SQLITE_OK &&
37591
- !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) &&
37592
- !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
37593
- ){
37594
- pPager->nRec = 0;
37595
- rc = writeJournalHdr(pPager);
37596
- }
38254
+ if( pPg->flags&PGHDR_NEED_SYNC
38255
+ || pPager->eState==PAGER_WRITER_CACHEMOD
38256
+ ){
38257
+ rc = syncJournal(pPager, 1);
3759738258
}
3759838259
3759938260
/* If the page number of this page is larger than the current size of
3760038261
** the database image, it may need to be written to the sub-journal.
3760138262
** This is because the call to pager_write_pagelist() below will not
@@ -37629,10 +38290,11 @@
3762938290
rc = subjournalPage(pPg);
3763038291
}
3763138292
3763238293
/* Write the contents of the page out to the database file. */
3763338294
if( rc==SQLITE_OK ){
38295
+ assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
3763438296
rc = pager_write_pagelist(pPager, pPg);
3763538297
}
3763638298
}
3763738299
3763838300
/* Mark the page as clean. */
@@ -37639,11 +38301,11 @@
3763938301
if( rc==SQLITE_OK ){
3764038302
PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
3764138303
sqlite3PcacheMakeClean(pPg);
3764238304
}
3764338305
37644
- return pager_error(pPager, rc);
38306
+ return pager_error(pPager, rc);
3764538307
}
3764638308
3764738309
3764838310
/*
3764938311
** Allocate and initialize a new Pager object and put a pointer to it
@@ -37694,11 +38356,11 @@
3769438356
char *zPathname = 0; /* Full path to database file */
3769538357
int nPathname = 0; /* Number of bytes in zPathname */
3769638358
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
3769738359
int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
3769838360
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
37699
- u16 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
38361
+ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
3770038362
3770138363
/* Figure out how much space is required for each journal file-handle
3770238364
** (there are two of them, the main journal and the sub-journal). This
3770338365
** is the maximum space required for an in-memory journal file handle
3770438366
** and a regular journal file-handle. Note that a "regular journal-handle"
@@ -37829,11 +38491,11 @@
3782938491
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
3783038492
if( szPageDflt<pPager->sectorSize ){
3783138493
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
3783238494
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
3783338495
}else{
37834
- szPageDflt = (u16)pPager->sectorSize;
38496
+ szPageDflt = (u32)pPager->sectorSize;
3783538497
}
3783638498
}
3783738499
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
3783838500
{
3783938501
int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
@@ -37857,11 +38519,12 @@
3785738519
** This branch is also run for an in-memory database. An in-memory
3785838520
** database is the same as a temp-file that is never written out to
3785938521
** disk and uses an in-memory rollback journal.
3786038522
*/
3786138523
tempFile = 1;
37862
- pPager->state = PAGER_EXCLUSIVE;
38524
+ pPager->eState = PAGER_READER;
38525
+ pPager->eLock = EXCLUSIVE_LOCK;
3786338526
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
3786438527
}
3786538528
3786638529
/* The following call to PagerSetPagesize() serves to set the value of
3786738530
** Pager.pageSize and to allocate the Pager.pTmpSpace buffer.
@@ -37894,27 +38557,27 @@
3789438557
pPager->useJournal = (u8)useJournal;
3789538558
pPager->noReadlock = (noReadlock && readOnly) ?1:0;
3789638559
/* pPager->stmtOpen = 0; */
3789738560
/* pPager->stmtInUse = 0; */
3789838561
/* pPager->nRef = 0; */
37899
- pPager->dbSizeValid = (u8)memDb;
3790038562
/* pPager->stmtSize = 0; */
3790138563
/* pPager->stmtJSize = 0; */
3790238564
/* pPager->nPage = 0; */
3790338565
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
3790438566
/* pPager->state = PAGER_UNLOCK; */
38567
+#if 0
3790538568
assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
38569
+#endif
3790638570
/* pPager->errMask = 0; */
3790738571
pPager->tempFile = (u8)tempFile;
3790838572
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
3790938573
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
3791038574
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
3791138575
pPager->exclusiveMode = (u8)tempFile;
3791238576
pPager->changeCountDone = pPager->tempFile;
3791338577
pPager->memDb = (u8)memDb;
3791438578
pPager->readOnly = (u8)readOnly;
37915
- /* pPager->needSync = 0; */
3791638579
assert( useJournal || pPager->tempFile );
3791738580
pPager->noSync = pPager->tempFile;
3791838581
pPager->fullSync = pPager->noSync ?0:1;
3791938582
pPager->sync_flags = SQLITE_SYNC_NORMAL;
3792038583
/* pPager->pFirst = 0; */
@@ -37975,24 +38638,24 @@
3797538638
sqlite3_vfs * const pVfs = pPager->pVfs;
3797638639
int rc = SQLITE_OK; /* Return code */
3797738640
int exists = 1; /* True if a journal file is present */
3797838641
int jrnlOpen = !!isOpen(pPager->jfd);
3797938642
37980
- assert( pPager!=0 );
3798138643
assert( pPager->useJournal );
3798238644
assert( isOpen(pPager->fd) );
37983
- assert( pPager->state <= PAGER_SHARED );
38645
+ assert( pPager->eState==PAGER_OPEN );
38646
+
3798438647
assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
3798538648
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
3798638649
));
3798738650
3798838651
*pExists = 0;
3798938652
if( !jrnlOpen ){
3799038653
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
3799138654
}
3799238655
if( rc==SQLITE_OK && exists ){
37993
- int locked; /* True if some process holds a RESERVED lock */
38656
+ int locked = 0; /* True if some process holds a RESERVED lock */
3799438657
3799538658
/* Race condition here: Another process might have been holding the
3799638659
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
3799738660
** call above, but then delete the journal and drop the lock before
3799838661
** we get to the following sqlite3OsCheckReservedLock() call. If that
@@ -38000,25 +38663,25 @@
3800038663
** in fact there is none. This results in a false-positive which will
3800138664
** be dealt with by the playback routine. Ticket #3883.
3800238665
*/
3800338666
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
3800438667
if( rc==SQLITE_OK && !locked ){
38005
- int nPage;
38668
+ Pgno nPage; /* Number of pages in database file */
3800638669
3800738670
/* Check the size of the database file. If it consists of 0 pages,
3800838671
** then delete the journal file. See the header comment above for
3800938672
** the reasoning here. Delete the obsolete journal file under
3801038673
** a RESERVED lock to avoid race conditions and to avoid violating
3801138674
** [H33020].
3801238675
*/
38013
- rc = sqlite3PagerPagecount(pPager, &nPage);
38676
+ rc = pagerPagecount(pPager, &nPage);
3801438677
if( rc==SQLITE_OK ){
3801538678
if( nPage==0 ){
3801638679
sqlite3BeginBenignMalloc();
38017
- if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){
38680
+ if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
3801838681
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
38019
- sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
38682
+ pagerUnlockDb(pPager, SHARED_LOCK);
3802038683
}
3802138684
sqlite3EndBenignMalloc();
3802238685
}else{
3802338686
/* The journal file exists and no other connection has a reserved
3802438687
** or greater lock on the database file. Now check that there is
@@ -38067,11 +38730,11 @@
3806738730
** has been successfully called. If a shared-lock is already held when
3806838731
** this function is called, it is a no-op.
3806938732
**
3807038733
** The following operations are also performed by this function.
3807138734
**
38072
-** 1) If the pager is currently in PAGER_UNLOCK state (no lock held
38735
+** 1) If the pager is currently in PAGER_OPEN state (no lock held
3807338736
** on the database file), then an attempt is made to obtain a
3807438737
** SHARED lock on the database file. Immediately after obtaining
3807538738
** the SHARED lock, the file-system is checked for a hot-journal,
3807638739
** which is played back if present. Following any hot-journal
3807738740
** rollback, the contents of the cache are validated by checking
@@ -38082,70 +38745,51 @@
3808238745
** no outstanding references to any pages, and is in the error state,
3808338746
** then an attempt is made to clear the error state by discarding
3808438747
** the contents of the page cache and rolling back any open journal
3808538748
** file.
3808638749
**
38087
-** If the operation described by (2) above is not attempted, and if the
38088
-** pager is in an error state other than SQLITE_FULL when this is called,
38089
-** the error state error code is returned. It is permitted to read the
38090
-** database when in SQLITE_FULL error state.
38091
-**
38092
-** Otherwise, if everything is successful, SQLITE_OK is returned. If an
38093
-** IO error occurs while locking the database, checking for a hot-journal
38094
-** file or rolling back a journal file, the IO error code is returned.
38750
+** If everything is successful, SQLITE_OK is returned. If an IO error
38751
+** occurs while locking the database, checking for a hot-journal file or
38752
+** rolling back a journal file, the IO error code is returned.
3809538753
*/
3809638754
SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
3809738755
int rc = SQLITE_OK; /* Return code */
38098
- int isErrorReset = 0; /* True if recovering from error state */
3809938756
3810038757
/* This routine is only called from b-tree and only when there are no
38101
- ** outstanding pages */
38758
+ ** outstanding pages. This implies that the pager state should either
38759
+ ** be OPEN or READER. READER is only possible if the pager is or was in
38760
+ ** exclusive access mode.
38761
+ */
3810238762
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
38763
+ assert( assert_pager_state(pPager) );
38764
+ assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
3810338765
if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
3810438766
38105
- /* If this database is in an error-state, now is a chance to clear
38106
- ** the error. Discard the contents of the pager-cache and rollback
38107
- ** any hot journal in the file-system.
38108
- */
38109
- if( pPager->errCode ){
38110
- if( isOpen(pPager->jfd) || pPager->zJournal ){
38111
- isErrorReset = 1;
38112
- }
38113
- pPager->errCode = SQLITE_OK;
38114
- pager_reset(pPager);
38115
- }
38116
-
38117
- if( pagerUseWal(pPager) ){
38118
- rc = pagerBeginReadTransaction(pPager);
38119
- }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
38120
- sqlite3_vfs * const pVfs = pPager->pVfs;
38121
- int isHotJournal = 0;
38767
+ if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
38768
+ int bHotJournal = 1; /* True if there exists a hot journal-file */
38769
+
3812238770
assert( !MEMDB );
38123
- assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
38124
- if( pPager->noReadlock ){
38125
- assert( pPager->readOnly );
38126
- pPager->state = PAGER_SHARED;
38127
- }else{
38771
+ assert( pPager->noReadlock==0 || pPager->readOnly );
38772
+
38773
+ if( pPager->noReadlock==0 ){
3812838774
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
3812938775
if( rc!=SQLITE_OK ){
38130
- assert( pPager->state==PAGER_UNLOCK );
38131
- return pager_error(pPager, rc);
38776
+ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
38777
+ goto failed;
3813238778
}
3813338779
}
38134
- assert( pPager->state>=SHARED_LOCK );
3813538780
3813638781
/* If a journal file exists, and there is no RESERVED lock on the
3813738782
** database file, then it either needs to be played back or deleted.
3813838783
*/
38139
- if( !isErrorReset ){
38140
- assert( pPager->state <= PAGER_SHARED );
38141
- rc = hasHotJournal(pPager, &isHotJournal);
38142
- if( rc!=SQLITE_OK ){
38143
- goto failed;
38144
- }
38145
- }
38146
- if( isErrorReset || isHotJournal ){
38784
+ if( pPager->eLock<=SHARED_LOCK ){
38785
+ rc = hasHotJournal(pPager, &bHotJournal);
38786
+ }
38787
+ if( rc!=SQLITE_OK ){
38788
+ goto failed;
38789
+ }
38790
+ if( bHotJournal ){
3814738791
/* Get an EXCLUSIVE lock on the database file. At this point it is
3814838792
** important that a RESERVED lock is not obtained on the way to the
3814938793
** EXCLUSIVE lock. If it were, another process might open the
3815038794
** database file, detect the RESERVED lock, and conclude that the
3815138795
** database is safe to read while this process is still rolling the
@@ -38153,62 +38797,49 @@
3815338797
**
3815438798
** Because the intermediate RESERVED lock is not requested, any
3815538799
** other process attempting to access the database file will get to
3815638800
** this point in the code and fail to obtain its own EXCLUSIVE lock
3815738801
** on the database file.
38158
- */
38159
- if( pPager->state<EXCLUSIVE_LOCK ){
38160
- rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
38161
- if( rc!=SQLITE_OK ){
38162
- rc = pager_error(pPager, rc);
38163
- goto failed;
38164
- }
38165
- pPager->state = PAGER_EXCLUSIVE;
38166
- }
38167
-
38168
- /* Open the journal for read/write access. This is because in
38169
- ** exclusive-access mode the file descriptor will be kept open and
38170
- ** possibly used for a transaction later on. On some systems, the
38171
- ** OsTruncate() call used in exclusive-access mode also requires
38172
- ** a read/write file handle.
38173
- */
38174
- if( !isOpen(pPager->jfd) ){
38175
- int res;
38176
- rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res);
38177
- if( rc==SQLITE_OK ){
38178
- if( res ){
38179
- int fout = 0;
38180
- int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
38181
- assert( !pPager->tempFile );
38182
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
38183
- assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38184
- if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
38185
- rc = SQLITE_CANTOPEN_BKPT;
38186
- sqlite3OsClose(pPager->jfd);
38187
- }
38188
- }else{
38189
- /* If the journal does not exist, it usually means that some
38190
- ** other connection managed to get in and roll it back before
38191
- ** this connection obtained the exclusive lock above. Or, it
38192
- ** may mean that the pager was in the error-state when this
38193
- ** function was called and the journal file does not exist. */
38194
- rc = pager_end_transaction(pPager, 0);
38195
- }
38196
- }
38197
- }
38198
- if( rc!=SQLITE_OK ){
38199
- goto failed;
38200
- }
38201
-
38202
- /* Reset the journal status fields to indicates that we have no
38203
- ** rollback journal at this time. */
38204
- pPager->journalStarted = 0;
38205
- pPager->journalOff = 0;
38206
- pPager->setMaster = 0;
38207
- pPager->journalHdr = 0;
38208
-
38209
- /* Make sure the journal file has been synced to disk. */
38802
+ **
38803
+ ** Unless the pager is in locking_mode=exclusive mode, the lock is
38804
+ ** downgraded to SHARED_LOCK before this function returns.
38805
+ */
38806
+ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
38807
+ if( rc!=SQLITE_OK ){
38808
+ goto failed;
38809
+ }
38810
+
38811
+ /* If it is not already open and the file exists on disk, open the
38812
+ ** journal for read/write access. Write access is required because
38813
+ ** in exclusive-access mode the file descriptor will be kept open
38814
+ ** and possibly used for a transaction later on. Also, write-access
38815
+ ** is usually required to finalize the journal in journal_mode=persist
38816
+ ** mode (and also for journal_mode=truncate on some systems).
38817
+ **
38818
+ ** If the journal does not exist, it usually means that some
38819
+ ** other connection managed to get in and roll it back before
38820
+ ** this connection obtained the exclusive lock above. Or, it
38821
+ ** may mean that the pager was in the error-state when this
38822
+ ** function was called and the journal file does not exist.
38823
+ */
38824
+ if( !isOpen(pPager->jfd) ){
38825
+ sqlite3_vfs * const pVfs = pPager->pVfs;
38826
+ int bExists; /* True if journal file exists */
38827
+ rc = sqlite3OsAccess(
38828
+ pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
38829
+ if( rc==SQLITE_OK && bExists ){
38830
+ int fout = 0;
38831
+ int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
38832
+ assert( !pPager->tempFile );
38833
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
38834
+ assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38835
+ if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
38836
+ rc = SQLITE_CANTOPEN_BKPT;
38837
+ sqlite3OsClose(pPager->jfd);
38838
+ }
38839
+ }
38840
+ }
3821038841
3821138842
/* Playback and delete the journal. Drop the database write
3821238843
** lock and reacquire the read lock. Purge the cache before
3821338844
** playing back the hot-journal so that we don't end up with
3821438845
** an inconsistent cache. Sync the hot journal before playing
@@ -38215,25 +38846,50 @@
3821538846
** it back since the process that crashed and left the hot journal
3821638847
** probably did not sync it and we are required to always sync
3821738848
** the journal before playing it back.
3821838849
*/
3821938850
if( isOpen(pPager->jfd) ){
38851
+ assert( rc==SQLITE_OK );
3822038852
rc = pagerSyncHotJournal(pPager);
3822138853
if( rc==SQLITE_OK ){
3822238854
rc = pager_playback(pPager, 1);
38223
- }
38224
- if( rc!=SQLITE_OK ){
38225
- rc = pager_error(pPager, rc);
38226
- goto failed;
38227
- }
38228
- }
38229
- assert( (pPager->state==PAGER_SHARED)
38230
- || (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
38855
+ pPager->eState = PAGER_OPEN;
38856
+ }
38857
+ }else if( !pPager->exclusiveMode ){
38858
+ pagerUnlockDb(pPager, SHARED_LOCK);
38859
+ }
38860
+
38861
+ if( rc!=SQLITE_OK ){
38862
+ /* This branch is taken if an error occurs while trying to open
38863
+ ** or roll back a hot-journal while holding an EXCLUSIVE lock. The
38864
+ ** pager_unlock() routine will be called before returning to unlock
38865
+ ** the file. If the unlock attempt fails, then Pager.eLock must be
38866
+ ** set to UNKNOWN_LOCK (see the comment above the #define for
38867
+ ** UNKNOWN_LOCK above for an explanation).
38868
+ **
38869
+ ** In order to get pager_unlock() to do this, set Pager.eState to
38870
+ ** PAGER_ERROR now. This is not actually counted as a transition
38871
+ ** to ERROR state in the state diagram at the top of this file,
38872
+ ** since we know that the same call to pager_unlock() will very
38873
+ ** shortly transition the pager object to the OPEN state. Calling
38874
+ ** assert_pager_state() would fail now, as it should not be possible
38875
+ ** to be in ERROR state when there are zero outstanding page
38876
+ ** references.
38877
+ */
38878
+ pager_error(pPager, rc);
38879
+ goto failed;
38880
+ }
38881
+
38882
+ assert( pPager->eState==PAGER_OPEN );
38883
+ assert( (pPager->eLock==SHARED_LOCK)
38884
+ || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
3823138885
);
3823238886
}
3823338887
38234
- if( pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0 ){
38888
+ if( !pPager->tempFile
38889
+ && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0)
38890
+ ){
3823538891
/* The shared-lock has just been acquired on the database file
3823638892
** and there are already pages in the cache (from a previous
3823738893
** read or write transaction). Check to see if the database
3823838894
** has been modified. If the database has changed, flush the
3823938895
** cache.
@@ -38246,18 +38902,15 @@
3824638902
**
3824738903
** There is a vanishingly small chance that a change will not be
3824838904
** detected. The chance of an undetected change is so small that
3824938905
** it can be neglected.
3825038906
*/
38251
- int nPage = 0;
38907
+ Pgno nPage = 0;
3825238908
char dbFileVers[sizeof(pPager->dbFileVers)];
38253
- sqlite3PagerPagecount(pPager, &nPage);
3825438909
38255
- if( pPager->errCode ){
38256
- rc = pPager->errCode;
38257
- goto failed;
38258
- }
38910
+ rc = pagerPagecount(pPager, &nPage);
38911
+ if( rc ) goto failed;
3825938912
3826038913
if( nPage>0 ){
3826138914
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
3826238915
rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
3826338916
if( rc!=SQLITE_OK ){
@@ -38269,22 +38922,34 @@
3826938922
3827038923
if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
3827138924
pager_reset(pPager);
3827238925
}
3827338926
}
38274
- assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );
3827538927
3827638928
/* If there is a WAL file in the file-system, open this database in WAL
3827738929
** mode. Otherwise, the following function call is a no-op.
3827838930
*/
3827938931
rc = pagerOpenWalIfPresent(pPager);
38932
+ assert( pPager->pWal==0 || rc==SQLITE_OK );
38933
+ }
38934
+
38935
+ if( pagerUseWal(pPager) ){
38936
+ assert( rc==SQLITE_OK );
38937
+ rc = pagerBeginReadTransaction(pPager);
38938
+ }
38939
+
38940
+ if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
38941
+ rc = pagerPagecount(pPager, &pPager->dbSize);
3828038942
}
3828138943
3828238944
failed:
3828338945
if( rc!=SQLITE_OK ){
38284
- /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
38946
+ assert( !MEMDB );
3828538947
pager_unlock(pPager);
38948
+ assert( pPager->eState==PAGER_OPEN );
38949
+ }else{
38950
+ pPager->eState = PAGER_READER;
3828638951
}
3828738952
return rc;
3828838953
}
3828938954
3829038955
/*
@@ -38294,13 +38959,11 @@
3829438959
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
3829538960
** the rollback journal, the unlock is not performed and there is
3829638961
** nothing to rollback, so this routine is a no-op.
3829738962
*/
3829838963
static void pagerUnlockIfUnused(Pager *pPager){
38299
- if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
38300
- && (!pPager->exclusiveMode || pPager->journalOff>0)
38301
- ){
38964
+ if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
3830238965
pagerUnlockAndRollback(pPager);
3830338966
}
3830438967
}
3830538968
3830638969
/*
@@ -38360,20 +39023,20 @@
3836039023
int noContent /* Do not bother reading content from disk if true */
3836139024
){
3836239025
int rc;
3836339026
PgHdr *pPg;
3836439027
39028
+ assert( pPager->eState>=PAGER_READER );
3836539029
assert( assert_pager_state(pPager) );
38366
- assert( pPager->state>PAGER_UNLOCK );
3836739030
3836839031
if( pgno==0 ){
3836939032
return SQLITE_CORRUPT_BKPT;
3837039033
}
3837139034
3837239035
/* If the pager is in the error state, return an error immediately.
3837339036
** Otherwise, request the page from the PCache layer. */
38374
- if( pPager->errCode!=SQLITE_OK && pPager->errCode!=SQLITE_FULL ){
39037
+ if( pPager->errCode!=SQLITE_OK ){
3837539038
rc = pPager->errCode;
3837639039
}else{
3837739040
rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
3837839041
}
3837939042
@@ -38395,11 +39058,10 @@
3839539058
return SQLITE_OK;
3839639059
3839739060
}else{
3839839061
/* The pager cache has created a new page. Its content needs to
3839939062
** be initialized. */
38400
- int nMax;
3840139063
3840239064
PAGER_INCR(pPager->nMiss);
3840339065
pPg = *ppPage;
3840439066
pPg->pPager = pPager;
3840539067
@@ -38408,16 +39070,11 @@
3840839070
if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
3840939071
rc = SQLITE_CORRUPT_BKPT;
3841039072
goto pager_acquire_err;
3841139073
}
3841239074
38413
- rc = sqlite3PagerPagecount(pPager, &nMax);
38414
- if( rc!=SQLITE_OK ){
38415
- goto pager_acquire_err;
38416
- }
38417
-
38418
- if( MEMDB || nMax<(int)pgno || noContent || !isOpen(pPager->fd) ){
39075
+ if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
3841939076
if( pgno>pPager->mxPgno ){
3842039077
rc = SQLITE_FULL;
3842139078
goto pager_acquire_err;
3842239079
}
3842339080
if( noContent ){
@@ -38443,13 +39100,11 @@
3844339100
rc = readDbPage(pPg);
3844439101
if( rc!=SQLITE_OK ){
3844539102
goto pager_acquire_err;
3844639103
}
3844739104
}
38448
-#ifdef SQLITE_CHECK_PAGES
38449
- pPg->pageHash = pager_pagehash(pPg);
38450
-#endif
39105
+ pager_set_pagehash(pPg);
3845139106
}
3845239107
3845339108
return SQLITE_OK;
3845439109
3845539110
pager_acquire_err:
@@ -38464,13 +39119,11 @@
3846439119
}
3846539120
3846639121
/*
3846739122
** Acquire a page if it is already in the in-memory cache. Do
3846839123
** not read the page from disk. Return a pointer to the page,
38469
-** or 0 if the page is not in cache. Also, return 0 if the
38470
-** pager is in PAGER_UNLOCK state when this function is called,
38471
-** or if the pager is in an error state other than SQLITE_FULL.
39124
+** or 0 if the page is not in cache.
3847239125
**
3847339126
** See also sqlite3PagerGet(). The difference between this routine
3847439127
** and sqlite3PagerGet() is that _get() will go to the disk and read
3847539128
** in the page if the page is not already in cache. This routine
3847639129
** returns NULL if the page is not in cache or if a disk I/O error
@@ -38479,11 +39132,11 @@
3847939132
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
3848039133
PgHdr *pPg = 0;
3848139134
assert( pPager!=0 );
3848239135
assert( pgno!=0 );
3848339136
assert( pPager->pPCache!=0 );
38484
- assert( pPager->state > PAGER_UNLOCK );
39137
+ assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR );
3848539138
sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
3848639139
return pPg;
3848739140
}
3848839141
3848939142
/*
@@ -38524,73 +39177,71 @@
3852439177
** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
3852539178
** an IO error code if opening or writing the journal file fails.
3852639179
*/
3852739180
static int pager_open_journal(Pager *pPager){
3852839181
int rc = SQLITE_OK; /* Return code */
38529
- int nPage; /* Size of database file */
3853039182
sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */
3853139183
38532
- assert( pPager->state>=PAGER_RESERVED );
38533
- assert( pPager->useJournal );
38534
- assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF );
39184
+ assert( pPager->eState==PAGER_WRITER_LOCKED );
39185
+ assert( assert_pager_state(pPager) );
3853539186
assert( pPager->pInJournal==0 );
3853639187
3853739188
/* If already in the error state, this function is a no-op. But on
3853839189
** the other hand, this routine is never called if we are already in
3853939190
** an error state. */
3854039191
if( NEVER(pPager->errCode) ) return pPager->errCode;
3854139192
38542
- testcase( pPager->dbSizeValid==0 );
38543
- rc = sqlite3PagerPagecount(pPager, &nPage);
38544
- if( rc ) return rc;
38545
- pPager->pInJournal = sqlite3BitvecCreate(nPage);
38546
- if( pPager->pInJournal==0 ){
38547
- return SQLITE_NOMEM;
38548
- }
38549
-
38550
- /* Open the journal file if it is not already open. */
38551
- if( !isOpen(pPager->jfd) ){
38552
- if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
38553
- sqlite3MemJournalOpen(pPager->jfd);
38554
- }else{
38555
- const int flags = /* VFS flags to open journal file */
38556
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
38557
- (pPager->tempFile ?
38558
- (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
38559
- (SQLITE_OPEN_MAIN_JOURNAL)
38560
- );
38561
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
38562
- rc = sqlite3JournalOpen(
38563
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
38564
- );
38565
-#else
38566
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
38567
-#endif
38568
- }
38569
- assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38570
- }
38571
-
38572
-
38573
- /* Write the first journal header to the journal file and open
38574
- ** the sub-journal if necessary.
38575
- */
38576
- if( rc==SQLITE_OK ){
38577
- /* TODO: Check if all of these are really required. */
38578
- pPager->dbOrigSize = pPager->dbSize;
38579
- pPager->journalStarted = 0;
38580
- pPager->needSync = 0;
38581
- pPager->nRec = 0;
38582
- pPager->journalOff = 0;
38583
- pPager->setMaster = 0;
38584
- pPager->journalHdr = 0;
38585
- rc = writeJournalHdr(pPager);
39193
+ if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
39194
+ pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
39195
+ if( pPager->pInJournal==0 ){
39196
+ return SQLITE_NOMEM;
39197
+ }
39198
+
39199
+ /* Open the journal file if it is not already open. */
39200
+ if( !isOpen(pPager->jfd) ){
39201
+ if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
39202
+ sqlite3MemJournalOpen(pPager->jfd);
39203
+ }else{
39204
+ const int flags = /* VFS flags to open journal file */
39205
+ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
39206
+ (pPager->tempFile ?
39207
+ (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
39208
+ (SQLITE_OPEN_MAIN_JOURNAL)
39209
+ );
39210
+ #ifdef SQLITE_ENABLE_ATOMIC_WRITE
39211
+ rc = sqlite3JournalOpen(
39212
+ pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
39213
+ );
39214
+ #else
39215
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
39216
+ #endif
39217
+ }
39218
+ assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
39219
+ }
39220
+
39221
+
39222
+ /* Write the first journal header to the journal file and open
39223
+ ** the sub-journal if necessary.
39224
+ */
39225
+ if( rc==SQLITE_OK ){
39226
+ /* TODO: Check if all of these are really required. */
39227
+ pPager->nRec = 0;
39228
+ pPager->journalOff = 0;
39229
+ pPager->setMaster = 0;
39230
+ pPager->journalHdr = 0;
39231
+ rc = writeJournalHdr(pPager);
39232
+ }
3858639233
}
3858739234
3858839235
if( rc!=SQLITE_OK ){
3858939236
sqlite3BitvecDestroy(pPager->pInJournal);
3859039237
pPager->pInJournal = 0;
39238
+ }else{
39239
+ assert( pPager->eState==PAGER_WRITER_LOCKED );
39240
+ pPager->eState = PAGER_WRITER_CACHEMOD;
3859139241
}
39242
+
3859239243
return rc;
3859339244
}
3859439245
3859539246
/*
3859639247
** Begin a write-transaction on the specified pager object. If a
@@ -38599,18 +39250,10 @@
3859939250
** If the exFlag argument is false, then acquire at least a RESERVED
3860039251
** lock on the database file. If exFlag is true, then acquire at least
3860139252
** an EXCLUSIVE lock. If such a lock is already held, no locking
3860239253
** functions need be called.
3860339254
**
38604
-** If this is not a temporary or in-memory file and, the journal file is
38605
-** opened if it has not been already. For a temporary file, the opening
38606
-** of the journal file is deferred until there is an actual need to
38607
-** write to the journal. TODO: Why handle temporary files differently?
38608
-**
38609
-** If the journal file is opened (or if it is already open), then a
38610
-** journal-header is written to the start of it.
38611
-**
3861239255
** If the subjInMemory argument is non-zero, then any sub-journal opened
3861339256
** within this transaction will be opened as an in-memory file. This
3861439257
** has no effect if the sub-journal is already opened (as it may be when
3861539258
** running in exclusive mode) or if the transaction does not require a
3861639259
** sub-journal. If the subjInMemory argument is zero, then any required
@@ -38617,24 +39260,24 @@
3861739260
** sub-journal is implemented in-memory if pPager is an in-memory database,
3861839261
** or using a temporary file otherwise.
3861939262
*/
3862039263
SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
3862139264
int rc = SQLITE_OK;
38622
- assert( pPager->state!=PAGER_UNLOCK );
39265
+
39266
+ if( pPager->errCode ) return pPager->errCode;
39267
+ assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
3862339268
pPager->subjInMemory = (u8)subjInMemory;
3862439269
38625
- if( pPager->state==PAGER_SHARED ){
39270
+ if( ALWAYS(pPager->eState==PAGER_READER) ){
3862639271
assert( pPager->pInJournal==0 );
38627
- assert( !MEMDB && !pPager->tempFile );
3862839272
3862939273
if( pagerUseWal(pPager) ){
3863039274
/* If the pager is configured to use locking_mode=exclusive, and an
3863139275
** exclusive lock on the database is not already held, obtain it now.
3863239276
*/
3863339277
if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
38634
- rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
38635
- pPager->state = PAGER_SHARED;
39278
+ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
3863639279
if( rc!=SQLITE_OK ){
3863739280
return rc;
3863839281
}
3863939282
sqlite3WalExclusiveMode(pPager->pWal, 1);
3864039283
}
@@ -38641,56 +39284,44 @@
3864139284
3864239285
/* Grab the write lock on the log file. If successful, upgrade to
3864339286
** PAGER_RESERVED state. Otherwise, return an error code to the caller.
3864439287
** The busy-handler is not invoked if another connection already
3864539288
** holds the write-lock. If possible, the upper layer will call it.
38646
- **
38647
- ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open
38648
- ** transaction, but never to PAGER_EXCLUSIVE. This is because in
38649
- ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions
38650
- ** may copy data from the sub-journal into the database file as well
38651
- ** as into the page cache. Which would be incorrect in WAL mode.
3865239289
*/
3865339290
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
38654
- if( rc==SQLITE_OK ){
38655
- pPager->dbOrigSize = pPager->dbSize;
38656
- pPager->state = PAGER_RESERVED;
38657
- pPager->journalOff = 0;
38658
- }
38659
-
38660
- assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED );
38661
- assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED );
3866239291
}else{
3866339292
/* Obtain a RESERVED lock on the database file. If the exFlag parameter
3866439293
** is true, then immediately upgrade this to an EXCLUSIVE lock. The
3866539294
** busy-handler callback can be used when upgrading to the EXCLUSIVE
3866639295
** lock, but not when obtaining the RESERVED lock.
3866739296
*/
38668
- rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
38669
- if( rc==SQLITE_OK ){
38670
- pPager->state = PAGER_RESERVED;
38671
- if( exFlag ){
38672
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
38673
- }
38674
- }
38675
- }
38676
-
38677
- /* No need to open the journal file at this time. It will be
38678
- ** opened before it is written to. If we defer opening the journal,
38679
- ** we might save the work of creating a file if the transaction
38680
- ** ends up being a no-op.
38681
- */
38682
-
38683
- if( rc!=SQLITE_OK ){
38684
- assert( !pPager->dbModified );
38685
- /* Ignore any IO error that occurs within pager_end_transaction(). The
38686
- ** purpose of this call is to reset the internal state of the pager
38687
- ** sub-system. It doesn't matter if the journal-file is not properly
38688
- ** finalized at this point (since it is not a valid journal file anyway).
38689
- */
38690
- pager_end_transaction(pPager, 0);
38691
- }
39297
+ rc = pagerLockDb(pPager, RESERVED_LOCK);
39298
+ if( rc==SQLITE_OK && exFlag ){
39299
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
39300
+ }
39301
+ }
39302
+
39303
+ if( rc==SQLITE_OK ){
39304
+ /* Change to WRITER_LOCKED state.
39305
+ **
39306
+ ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD
39307
+ ** when it has an open transaction, but never to DBMOD or FINISHED.
39308
+ ** This is because in those states the code to roll back savepoint
39309
+ ** transactions may copy data from the sub-journal into the database
39310
+ ** file as well as into the page cache. Which would be incorrect in
39311
+ ** WAL mode.
39312
+ */
39313
+ pPager->eState = PAGER_WRITER_LOCKED;
39314
+ pPager->dbHintSize = pPager->dbSize;
39315
+ pPager->dbFileSize = pPager->dbSize;
39316
+ pPager->dbOrigSize = pPager->dbSize;
39317
+ pPager->journalOff = 0;
39318
+ }
39319
+
39320
+ assert( rc==SQLITE_OK || pPager->eState==PAGER_READER );
39321
+ assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED );
39322
+ assert( assert_pager_state(pPager) );
3869239323
}
3869339324
3869439325
PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
3869539326
return rc;
3869639327
}
@@ -38705,110 +39336,98 @@
3870539336
static int pager_write(PgHdr *pPg){
3870639337
void *pData = pPg->pData;
3870739338
Pager *pPager = pPg->pPager;
3870839339
int rc = SQLITE_OK;
3870939340
38710
- /* This routine is not called unless a transaction has already been
38711
- ** started.
39341
+ /* This routine is not called unless a write-transaction has already
39342
+ ** been started. The journal file may or may not be open at this point.
39343
+ ** It is never called in the ERROR state.
3871239344
*/
38713
- assert( pPager->state>=PAGER_RESERVED );
39345
+ assert( pPager->eState==PAGER_WRITER_LOCKED
39346
+ || pPager->eState==PAGER_WRITER_CACHEMOD
39347
+ || pPager->eState==PAGER_WRITER_DBMOD
39348
+ );
39349
+ assert( assert_pager_state(pPager) );
3871439350
3871539351
/* If an error has been previously detected, report the same error
38716
- ** again.
38717
- */
39352
+ ** again. This should not happen, but the check provides robustness. */
3871839353
if( NEVER(pPager->errCode) ) return pPager->errCode;
3871939354
3872039355
/* Higher-level routines never call this function if database is not
3872139356
** writable. But check anyway, just for robustness. */
3872239357
if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
3872339358
38724
- assert( !pPager->setMaster );
38725
-
3872639359
CHECK_PAGE(pPg);
3872739360
3872839361
/* Mark the page as dirty. If the page has already been written
3872939362
** to the journal then we can return right away.
3873039363
*/
3873139364
sqlite3PcacheMakeDirty(pPg);
3873239365
if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
3873339366
assert( !pagerUseWal(pPager) );
38734
- pPager->dbModified = 1;
39367
+ assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
3873539368
}else{
3873639369
3873739370
/* If we get this far, it means that the page needs to be
38738
- ** written to the transaction journal or the ckeckpoint journal
39371
+ ** written to the transaction journal or the checkpoint journal
3873939372
** or both.
3874039373
**
38741
- ** Higher level routines should have already started a transaction,
38742
- ** which means they have acquired the necessary locks but the rollback
38743
- ** journal might not yet be open.
39374
+ ** Higher level routines have already obtained the necessary locks
39375
+ ** to begin the write-transaction, but the rollback journal might not
39376
+ ** yet be open. Open it now if this is the case.
3874439377
*/
38745
- assert( pPager->state>=RESERVED_LOCK );
38746
- if( pPager->pInJournal==0
38747
- && pPager->journalMode!=PAGER_JOURNALMODE_OFF
38748
- && !pagerUseWal(pPager)
38749
- ){
38750
- assert( pPager->useJournal );
39378
+ if( pPager->eState==PAGER_WRITER_LOCKED ){
3875139379
rc = pager_open_journal(pPager);
3875239380
if( rc!=SQLITE_OK ) return rc;
3875339381
}
38754
- pPager->dbModified = 1;
39382
+ assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
39383
+ assert( assert_pager_state(pPager) );
3875539384
3875639385
/* The transaction journal now exists and we have a RESERVED or an
3875739386
** EXCLUSIVE lock on the main database file. Write the current page to
3875839387
** the transaction journal if it is not there already.
3875939388
*/
38760
- if( !pageInJournal(pPg) && isOpen(pPager->jfd) ){
38761
- assert( !pagerUseWal(pPager) );
38762
- if( pPg->pgno<=pPager->dbOrigSize ){
39389
+ if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
39390
+ assert( pagerUseWal(pPager)==0 );
39391
+ if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
3876339392
u32 cksum;
3876439393
char *pData2;
39394
+ i64 iOff = pPager->journalOff;
3876539395
3876639396
/* We should never write to the journal file the page that
3876739397
** contains the database locks. The following assert verifies
3876839398
** that we do not. */
3876939399
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
3877039400
38771
- assert( pPager->journalHdr <= pPager->journalOff );
39401
+ assert( pPager->journalHdr<=pPager->journalOff );
3877239402
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
3877339403
cksum = pager_cksum(pPager, (u8*)pData2);
38774
- rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno);
38775
- if( rc==SQLITE_OK ){
38776
- rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize,
38777
- pPager->journalOff + 4);
38778
- pPager->journalOff += pPager->pageSize+4;
38779
- }
38780
- if( rc==SQLITE_OK ){
38781
- rc = write32bits(pPager->jfd, pPager->journalOff, cksum);
38782
- pPager->journalOff += 4;
38783
- }
39404
+
39405
+ /* Even if an IO or diskfull error occurs while journalling the
39406
+ ** page in the block above, set the need-sync flag for the page.
39407
+ ** Otherwise, when the transaction is rolled back, the logic in
39408
+ ** playback_one_page() will think that the page needs to be restored
39409
+ ** in the database file. And if an IO error occurs while doing so,
39410
+ ** then corruption may follow.
39411
+ */
39412
+ pPg->flags |= PGHDR_NEED_SYNC;
39413
+
39414
+ rc = write32bits(pPager->jfd, iOff, pPg->pgno);
39415
+ if( rc!=SQLITE_OK ) return rc;
39416
+ rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
39417
+ if( rc!=SQLITE_OK ) return rc;
39418
+ rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
39419
+ if( rc!=SQLITE_OK ) return rc;
39420
+
3878439421
IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
3878539422
pPager->journalOff, pPager->pageSize));
3878639423
PAGER_INCR(sqlite3_pager_writej_count);
3878739424
PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
3878839425
PAGERID(pPager), pPg->pgno,
3878939426
((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
3879039427
38791
- /* Even if an IO or diskfull error occurred while journalling the
38792
- ** page in the block above, set the need-sync flag for the page.
38793
- ** Otherwise, when the transaction is rolled back, the logic in
38794
- ** playback_one_page() will think that the page needs to be restored
38795
- ** in the database file. And if an IO error occurs while doing so,
38796
- ** then corruption may follow.
38797
- */
38798
- if( !pPager->noSync ){
38799
- pPg->flags |= PGHDR_NEED_SYNC;
38800
- pPager->needSync = 1;
38801
- }
38802
-
38803
- /* An error has occurred writing to the journal file. The
38804
- ** transaction will be rolled back by the layer above.
38805
- */
38806
- if( rc!=SQLITE_OK ){
38807
- return rc;
38808
- }
38809
-
39428
+ pPager->journalOff += 8 + pPager->pageSize;
3881039429
pPager->nRec++;
3881139430
assert( pPager->pInJournal!=0 );
3881239431
rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
3881339432
testcase( rc==SQLITE_NOMEM );
3881439433
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
@@ -38816,13 +39435,12 @@
3881639435
if( rc!=SQLITE_OK ){
3881739436
assert( rc==SQLITE_NOMEM );
3881839437
return rc;
3881939438
}
3882039439
}else{
38821
- if( !pPager->journalStarted && !pPager->noSync ){
39440
+ if( pPager->eState!=PAGER_WRITER_DBMOD ){
3882239441
pPg->flags |= PGHDR_NEED_SYNC;
38823
- pPager->needSync = 1;
3882439442
}
3882539443
PAGERTRACE(("APPEND %d page %d needSync=%d\n",
3882639444
PAGERID(pPager), pPg->pgno,
3882739445
((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
3882839446
}
@@ -38838,11 +39456,10 @@
3883839456
}
3883939457
}
3884039458
3884139459
/* Update the database size and return.
3884239460
*/
38843
- assert( pPager->state>=PAGER_SHARED );
3884439461
if( pPager->dbSize<pPg->pgno ){
3884539462
pPager->dbSize = pPg->pgno;
3884639463
}
3884739464
return rc;
3884839465
}
@@ -38866,10 +39483,14 @@
3886639483
3886739484
PgHdr *pPg = pDbPage;
3886839485
Pager *pPager = pPg->pPager;
3886939486
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
3887039487
39488
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
39489
+ assert( pPager->eState!=PAGER_ERROR );
39490
+ assert( assert_pager_state(pPager) );
39491
+
3887139492
if( nPagePerSector>1 ){
3887239493
Pgno nPageCount; /* Total number of pages in database file */
3887339494
Pgno pg1; /* First page of the sector pPg is located on. */
3887439495
int nPage = 0; /* Number of pages starting at pg1 to journal */
3887539496
int ii; /* Loop counter */
@@ -38887,23 +39508,21 @@
3888739508
** an integer power of 2. It sets variable pg1 to the identifier
3888839509
** of the first page of the sector pPg is located on.
3888939510
*/
3889039511
pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
3889139512
38892
- rc = sqlite3PagerPagecount(pPager, (int *)&nPageCount);
38893
- if( rc==SQLITE_OK ){
38894
- if( pPg->pgno>nPageCount ){
38895
- nPage = (pPg->pgno - pg1)+1;
38896
- }else if( (pg1+nPagePerSector-1)>nPageCount ){
38897
- nPage = nPageCount+1-pg1;
38898
- }else{
38899
- nPage = nPagePerSector;
38900
- }
38901
- assert(nPage>0);
38902
- assert(pg1<=pPg->pgno);
38903
- assert((pg1+nPage)>pPg->pgno);
38904
- }
39513
+ nPageCount = pPager->dbSize;
39514
+ if( pPg->pgno>nPageCount ){
39515
+ nPage = (pPg->pgno - pg1)+1;
39516
+ }else if( (pg1+nPagePerSector-1)>nPageCount ){
39517
+ nPage = nPageCount+1-pg1;
39518
+ }else{
39519
+ nPage = nPagePerSector;
39520
+ }
39521
+ assert(nPage>0);
39522
+ assert(pg1<=pPg->pgno);
39523
+ assert((pg1+nPage)>pPg->pgno);
3890539524
3890639525
for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
3890739526
Pgno pg = pg1+ii;
3890839527
PgHdr *pPage;
3890939528
if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
@@ -38911,11 +39530,10 @@
3891139530
rc = sqlite3PagerGet(pPager, pg, &pPage);
3891239531
if( rc==SQLITE_OK ){
3891339532
rc = pager_write(pPage);
3891439533
if( pPage->flags&PGHDR_NEED_SYNC ){
3891539534
needSync = 1;
38916
- assert(pPager->needSync);
3891739535
}
3891839536
sqlite3PagerUnref(pPage);
3891939537
}
3892039538
}
3892139539
}else if( (pPage = pager_lookup(pPager, pg))!=0 ){
@@ -38931,19 +39549,18 @@
3893139549
** writing to any of these nPage pages may damage the others, the
3893239550
** journal file must contain sync()ed copies of all of them
3893339551
** before any of them can be written out to the database file.
3893439552
*/
3893539553
if( rc==SQLITE_OK && needSync ){
38936
- assert( !MEMDB && pPager->noSync==0 );
39554
+ assert( !MEMDB );
3893739555
for(ii=0; ii<nPage; ii++){
3893839556
PgHdr *pPage = pager_lookup(pPager, pg1+ii);
3893939557
if( pPage ){
3894039558
pPage->flags |= PGHDR_NEED_SYNC;
3894139559
sqlite3PagerUnref(pPage);
3894239560
}
3894339561
}
38944
- assert(pPager->needSync);
3894539562
}
3894639563
3894739564
assert( pPager->doNotSyncSpill==1 );
3894839565
pPager->doNotSyncSpill--;
3894939566
}else{
@@ -38981,13 +39598,11 @@
3898139598
Pager *pPager = pPg->pPager;
3898239599
if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
3898339600
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
3898439601
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
3898539602
pPg->flags |= PGHDR_DONT_WRITE;
38986
-#ifdef SQLITE_CHECK_PAGES
38987
- pPg->pageHash = pager_pagehash(pPg);
38988
-#endif
39603
+ pager_set_pagehash(pPg);
3898939604
}
3899039605
}
3899139606
3899239607
/*
3899339608
** This routine is called to increment the value of the database file
@@ -39005,10 +39620,15 @@
3900539620
** by writing an updated version of page 1 using a call to the
3900639621
** sqlite3OsWrite() function.
3900739622
*/
3900839623
static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
3900939624
int rc = SQLITE_OK;
39625
+
39626
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
39627
+ || pPager->eState==PAGER_WRITER_DBMOD
39628
+ );
39629
+ assert( assert_pager_state(pPager) );
3901039630
3901139631
/* Declare and initialize constant integer 'isDirect'. If the
3901239632
** atomic-write optimization is enabled in this build, then isDirect
3901339633
** is initialized to the value passed as the isDirectMode parameter
3901439634
** to this function. Otherwise, it is always set to zero.
@@ -39024,11 +39644,10 @@
3902439644
UNUSED_PARAMETER(isDirectMode);
3902539645
#else
3902639646
# define DIRECT_MODE isDirectMode
3902739647
#endif
3902839648
39029
- assert( pPager->state>=PAGER_RESERVED );
3903039649
if( !pPager->changeCountDone && pPager->dbSize>0 ){
3903139650
PgHdr *pPgHdr; /* Reference to page 1 */
3903239651
u32 change_counter; /* Initial value of change-counter field */
3903339652
3903439653
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -39109,13 +39728,17 @@
3910939728
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
3911039729
** returned.
3911139730
*/
3911239731
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
3911339732
int rc = SQLITE_OK;
39114
- assert( pPager->state>=PAGER_RESERVED );
39733
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
39734
+ || pPager->eState==PAGER_WRITER_DBMOD
39735
+ || pPager->eState==PAGER_WRITER_LOCKED
39736
+ );
39737
+ assert( assert_pager_state(pPager) );
3911539738
if( 0==pagerUseWal(pPager) ){
39116
- rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE);
39739
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
3911739740
}
3911839741
return rc;
3911939742
}
3912039743
3912139744
/*
@@ -39149,26 +39772,33 @@
3914939772
const char *zMaster, /* If not NULL, the master journal name */
3915039773
int noSync /* True to omit the xSync on the db file */
3915139774
){
3915239775
int rc = SQLITE_OK; /* Return code */
3915339776
39154
- /* The dbOrigSize is never set if journal_mode=OFF */
39155
- assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 );
39777
+ assert( pPager->eState==PAGER_WRITER_LOCKED
39778
+ || pPager->eState==PAGER_WRITER_CACHEMOD
39779
+ || pPager->eState==PAGER_WRITER_DBMOD
39780
+ || pPager->eState==PAGER_ERROR
39781
+ );
39782
+ assert( assert_pager_state(pPager) );
3915639783
3915739784
/* If a prior error occurred, report that error again. */
39158
- if( pPager->errCode ) return pPager->errCode;
39785
+ if( NEVER(pPager->errCode) ) return pPager->errCode;
3915939786
3916039787
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
3916139788
pPager->zFilename, zMaster, pPager->dbSize));
3916239789
39163
- if( MEMDB && pPager->dbModified ){
39790
+ /* If no database changes have been made, return early. */
39791
+ if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
39792
+
39793
+ if( MEMDB ){
3916439794
/* If this is an in-memory db, or no pages have been written to, or this
3916539795
** function has already been called, it is mostly a no-op. However, any
3916639796
** backup in progress needs to be restarted.
3916739797
*/
3916839798
sqlite3BackupRestart(pPager->pBackup);
39169
- }else if( pPager->dbModified ){
39799
+ }else{
3917039800
if( pagerUseWal(pPager) ){
3917139801
PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
3917239802
if( pList ){
3917339803
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
3917439804
(pPager->fullSync ? pPager->sync_flags : 0)
@@ -39207,11 +39837,11 @@
3920739837
|| pPager->journalMode==PAGER_JOURNALMODE_OFF
3920839838
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
3920939839
);
3921039840
if( !zMaster && isOpen(pPager->jfd)
3921139841
&& pPager->journalOff==jrnlBufferSize(pPager)
39212
- && pPager->dbSize>=pPager->dbFileSize
39842
+ && pPager->dbSize>=pPager->dbOrigSize
3921339843
&& (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
3921439844
){
3921539845
/* Update the db file change counter via the direct-write method. The
3921639846
** following call will modify the in-memory representation of page 1
3921739847
** to include the updated change counter and then write page 1
@@ -39237,17 +39867,14 @@
3923739867
** Before reading the pages with page numbers larger than the
3923839868
** current value of Pager.dbSize, set dbSize back to the value
3923939869
** that it took at the start of the transaction. Otherwise, the
3924039870
** calls to sqlite3PagerGet() return zeroed pages instead of
3924139871
** reading data from the database file.
39242
- **
39243
- ** When journal_mode==OFF the dbOrigSize is always zero, so this
39244
- ** block never runs if journal_mode=OFF.
3924539872
*/
3924639873
#ifndef SQLITE_OMIT_AUTOVACUUM
3924739874
if( pPager->dbSize<pPager->dbOrigSize
39248
- && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF)
39875
+ && pPager->journalMode!=PAGER_JOURNALMODE_OFF
3924939876
){
3925039877
Pgno i; /* Iterator variable */
3925139878
const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
3925239879
const Pgno dbSize = pPager->dbSize; /* Database image size */
3925339880
pPager->dbSize = pPager->dbOrigSize;
@@ -39270,18 +39897,24 @@
3927039897
** or if zMaster is NULL (no master journal), then this call is a no-op.
3927139898
*/
3927239899
rc = writeMasterJournal(pPager, zMaster);
3927339900
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
3927439901
39275
- /* Sync the journal file. If the atomic-update optimization is being
39276
- ** used, this call will not create the journal file or perform any
39277
- ** real IO.
39902
+ /* Sync the journal file and write all dirty pages to the database.
39903
+ ** If the atomic-update optimization is being used, this sync will not
39904
+ ** create the journal file or perform any real IO.
39905
+ **
39906
+ ** Because the change-counter page was just modified, unless the
39907
+ ** atomic-update optimization is used it is almost certain that the
39908
+ ** journal requires a sync here. However, in locking_mode=exclusive
39909
+ ** on a system under memory pressure it is just possible that this is
39910
+ ** not the case. In this case it is likely enough that the redundant
39911
+ ** xSync() call will be changed to a no-op by the OS anyhow.
3927839912
*/
39279
- rc = syncJournal(pPager);
39913
+ rc = syncJournal(pPager, 0);
3928039914
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
3928139915
39282
- /* Write all dirty pages to the database file. */
3928339916
rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
3928439917
if( rc!=SQLITE_OK ){
3928539918
assert( rc!=SQLITE_IOERR_BLOCKED );
3928639919
goto commit_phase_one_exit;
3928739920
}
@@ -39290,11 +39923,11 @@
3929039923
/* If the file on disk is not the same size as the database image,
3929139924
** then use pager_truncate to grow or shrink the file here.
3929239925
*/
3929339926
if( pPager->dbSize!=pPager->dbFileSize ){
3929439927
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
39295
- assert( pPager->state>=PAGER_EXCLUSIVE );
39928
+ assert( pPager->eState==PAGER_WRITER_DBMOD );
3929639929
rc = pager_truncate(pPager, nNew);
3929739930
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
3929839931
}
3929939932
3930039933
/* Finally, sync the database file. */
@@ -39301,16 +39934,16 @@
3930139934
if( !pPager->noSync && !noSync ){
3930239935
rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
3930339936
}
3930439937
IOTRACE(("DBSYNC %p\n", pPager))
3930539938
}
39306
-
39307
- assert( pPager->state!=PAGER_SYNCED );
39308
- pPager->state = PAGER_SYNCED;
3930939939
}
3931039940
3931139941
commit_phase_one_exit:
39942
+ if( rc==SQLITE_OK && !pagerUseWal(pPager) ){
39943
+ pPager->eState = PAGER_WRITER_FINISHED;
39944
+ }
3931239945
return rc;
3931339946
}
3931439947
3931539948
3931639949
/*
@@ -39334,16 +39967,15 @@
3933439967
/* This routine should not be called if a prior error has occurred.
3933539968
** But if (due to a coding error elsewhere in the system) it does get
3933639969
** called, just return the same error code without doing anything. */
3933739970
if( NEVER(pPager->errCode) ) return pPager->errCode;
3933839971
39339
- /* This function should not be called if the pager is not in at least
39340
- ** PAGER_RESERVED state. **FIXME**: Make it so that this test always
39341
- ** fails - make it so that we never reach this point if we do not hold
39342
- ** all necessary locks.
39343
- */
39344
- if( NEVER(pPager->state<PAGER_RESERVED) ) return SQLITE_ERROR;
39972
+ assert( pPager->eState==PAGER_WRITER_LOCKED
39973
+ || pPager->eState==PAGER_WRITER_FINISHED
39974
+ || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD)
39975
+ );
39976
+ assert( assert_pager_state(pPager) );
3934539977
3934639978
/* An optimization. If the database was not actually modified during
3934739979
** this transaction, the pager is running in exclusive-mode and is
3934839980
** using persistent journals, then this function is a no-op.
3934939981
**
@@ -39352,106 +39984,80 @@
3935239984
** a hot-journal during hot-journal rollback, 0 changes will be made
3935339985
** to the database file. So there is no need to zero the journal
3935439986
** header. Since the pager is in exclusive mode, there is no need
3935539987
** to drop any locks either.
3935639988
*/
39357
- if( pPager->dbModified==0 && pPager->exclusiveMode
39989
+ if( pPager->eState==PAGER_WRITER_LOCKED
39990
+ && pPager->exclusiveMode
3935839991
&& pPager->journalMode==PAGER_JOURNALMODE_PERSIST
3935939992
){
3936039993
assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
39994
+ pPager->eState = PAGER_READER;
3936139995
return SQLITE_OK;
3936239996
}
3936339997
3936439998
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
39365
- assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dbModified );
3936639999
rc = pager_end_transaction(pPager, pPager->setMaster);
3936740000
return pager_error(pPager, rc);
3936840001
}
3936940002
3937040003
/*
39371
-** Rollback all changes. The database falls back to PAGER_SHARED mode.
40004
+** If a write transaction is open, then all changes made within the
40005
+** transaction are reverted and the current write-transaction is closed.
40006
+** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR
40007
+** state if an error occurs.
3937240008
**
39373
-** This function performs two tasks:
40009
+** If the pager is already in PAGER_ERROR state when this function is called,
40010
+** it returns Pager.errCode immediately. No work is performed in this case.
40011
+**
40012
+** Otherwise, in rollback mode, this function performs two functions:
3937440013
**
3937540014
** 1) It rolls back the journal file, restoring all database file and
3937640015
** in-memory cache pages to the state they were in when the transaction
3937740016
** was opened, and
40017
+**
3937840018
** 2) It finalizes the journal file, so that it is not used for hot
3937940019
** rollback at any point in the future.
3938040020
**
39381
-** subject to the following qualifications:
39382
-**
39383
-** * If the journal file is not yet open when this function is called,
39384
-** then only (2) is performed. In this case there is no journal file
39385
-** to roll back.
39386
-**
39387
-** * If in an error state other than SQLITE_FULL, then task (1) is
39388
-** performed. If successful, task (2). Regardless of the outcome
39389
-** of either, the error state error code is returned to the caller
39390
-** (i.e. either SQLITE_IOERR or SQLITE_CORRUPT).
39391
-**
39392
-** * If the pager is in PAGER_RESERVED state, then attempt (1). Whether
39393
-** or not (1) is successful, also attempt (2). If successful, return
39394
-** SQLITE_OK. Otherwise, enter the error state and return the first
39395
-** error code encountered.
39396
-**
39397
-** In this case there is no chance that the database was written to.
39398
-** So is safe to finalize the journal file even if the playback
39399
-** (operation 1) failed. However the pager must enter the error state
39400
-** as the contents of the in-memory cache are now suspect.
39401
-**
39402
-** * Finally, if in PAGER_EXCLUSIVE state, then attempt (1). Only
39403
-** attempt (2) if (1) is successful. Return SQLITE_OK if successful,
39404
-** otherwise enter the error state and return the error code from the
39405
-** failing operation.
39406
-**
39407
-** In this case the database file may have been written to. So if the
39408
-** playback operation did not succeed it would not be safe to finalize
39409
-** the journal file. It needs to be left in the file-system so that
39410
-** some other process can use it to restore the database state (by
39411
-** hot-journal rollback).
40021
+** Finalization of the journal file (task 2) is only performed if the
40022
+** rollback is successful.
40023
+**
40024
+** In WAL mode, all cache-entries containing data modified within the
40025
+** current transaction are either expelled from the cache or reverted to
40026
+** their pre-transaction state by re-reading data from the database or
40027
+** WAL files. The WAL transaction is then closed.
3941240028
*/
3941340029
SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
3941440030
int rc = SQLITE_OK; /* Return code */
3941540031
PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
40032
+
40033
+ /* PagerRollback() is a no-op if called in READER or OPEN state. If
40034
+ ** the pager is already in the ERROR state, the rollback is not
40035
+ ** attempted here. Instead, the error code is returned to the caller.
40036
+ */
40037
+ assert( assert_pager_state(pPager) );
40038
+ if( pPager->eState==PAGER_ERROR ) return pPager->errCode;
40039
+ if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
40040
+
3941640041
if( pagerUseWal(pPager) ){
3941740042
int rc2;
39418
-
3941940043
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
3942040044
rc2 = pager_end_transaction(pPager, pPager->setMaster);
3942140045
if( rc==SQLITE_OK ) rc = rc2;
39422
- rc = pager_error(pPager, rc);
39423
- }else if( !pPager->dbModified || !isOpen(pPager->jfd) ){
39424
- rc = pager_end_transaction(pPager, pPager->setMaster);
39425
- }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
39426
- if( pPager->state>=PAGER_EXCLUSIVE ){
39427
- pager_playback(pPager, 0);
39428
- }
39429
- rc = pPager->errCode;
39430
- }else{
39431
- if( pPager->state==PAGER_RESERVED ){
39432
- int rc2;
39433
- rc = pager_playback(pPager, 0);
39434
- rc2 = pager_end_transaction(pPager, pPager->setMaster);
39435
- if( rc==SQLITE_OK ){
39436
- rc = rc2;
39437
- }
39438
- }else{
39439
- rc = pager_playback(pPager, 0);
39440
- }
39441
-
39442
- if( !MEMDB ){
39443
- pPager->dbSizeValid = 0;
39444
- }
39445
-
39446
- /* If an error occurs during a ROLLBACK, we can no longer trust the pager
39447
- ** cache. So call pager_error() on the way out to make any error
39448
- ** persistent.
39449
- */
39450
- rc = pager_error(pPager, rc);
39451
- }
39452
- return rc;
40046
+ }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
40047
+ rc = pager_end_transaction(pPager, 0);
40048
+ }else{
40049
+ rc = pager_playback(pPager, 0);
40050
+ }
40051
+
40052
+ assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
40053
+ assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
40054
+
40055
+ /* If an error occurs during a ROLLBACK, we can no longer trust the pager
40056
+ ** cache. So call pager_error() on the way out to make any error persistent.
40057
+ */
40058
+ return pager_error(pPager, rc);
3945340059
}
3945440060
3945540061
/*
3945640062
** Return TRUE if the database file is opened read-only. Return FALSE
3945740063
** if the database is (in theory) writable.
@@ -39493,12 +40099,12 @@
3949340099
SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
3949440100
static int a[11];
3949540101
a[0] = sqlite3PcacheRefCount(pPager->pPCache);
3949640102
a[1] = sqlite3PcachePagecount(pPager->pPCache);
3949740103
a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
39498
- a[3] = pPager->dbSizeValid ? (int) pPager->dbSize : -1;
39499
- a[4] = pPager->state;
40104
+ a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
40105
+ a[4] = pPager->eState;
3950040106
a[5] = pPager->errCode;
3950140107
a[6] = pPager->nHit;
3950240108
a[7] = pPager->nMiss;
3950340109
a[8] = 0; /* Used to be pPager->nOvfl */
3950440110
a[9] = pPager->nRead;
@@ -39525,18 +40131,17 @@
3952540131
** returned. Otherwise, SQLITE_OK.
3952640132
*/
3952740133
SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
3952840134
int rc = SQLITE_OK; /* Return code */
3952940135
int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
40136
+
40137
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
40138
+ assert( assert_pager_state(pPager) );
3953040139
3953140140
if( nSavepoint>nCurrent && pPager->useJournal ){
3953240141
int ii; /* Iterator variable */
3953340142
PagerSavepoint *aNew; /* New Pager.aSavepoint array */
39534
- int nPage; /* Size of database file */
39535
-
39536
- rc = sqlite3PagerPagecount(pPager, &nPage);
39537
- if( rc ) return rc;
3953840143
3953940144
/* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
3954040145
** if the allocation fails. Otherwise, zero the new portion in case a
3954140146
** malloc failure occurs while populating it in the for(...) loop below.
3954240147
*/
@@ -39549,18 +40154,18 @@
3954940154
memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
3955040155
pPager->aSavepoint = aNew;
3955140156
3955240157
/* Populate the PagerSavepoint structures just allocated. */
3955340158
for(ii=nCurrent; ii<nSavepoint; ii++){
39554
- aNew[ii].nOrig = nPage;
40159
+ aNew[ii].nOrig = pPager->dbSize;
3955540160
if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
3955640161
aNew[ii].iOffset = pPager->journalOff;
3955740162
}else{
3955840163
aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
3955940164
}
3956040165
aNew[ii].iSubRec = pPager->nSubRec;
39561
- aNew[ii].pInSavepoint = sqlite3BitvecCreate(nPage);
40166
+ aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
3956240167
if( !aNew[ii].pInSavepoint ){
3956340168
return SQLITE_NOMEM;
3956440169
}
3956540170
if( pagerUseWal(pPager) ){
3956640171
sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -39603,16 +40208,16 @@
3960340208
** This function may return SQLITE_NOMEM if a memory allocation fails,
3960440209
** or an IO error code if an IO error occurs while rolling back a
3960540210
** savepoint. If no errors occur, SQLITE_OK is returned.
3960640211
*/
3960740212
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
39608
- int rc = SQLITE_OK;
40213
+ int rc = pPager->errCode; /* Return code */
3960940214
3961040215
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
3961140216
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
3961240217
39613
- if( iSavepoint<pPager->nSavepoint ){
40218
+ if( rc==SQLITE_OK && iSavepoint<pPager->nSavepoint ){
3961440219
int ii; /* Iterator variable */
3961540220
int nNew; /* Number of remaining savepoints after this op. */
3961640221
3961740222
/* Figure out how many savepoints will still be active after this
3961840223
** operation. Store this value in nNew. Then free resources associated
@@ -39644,12 +40249,12 @@
3964440249
else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
3964540250
PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
3964640251
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
3964740252
assert(rc!=SQLITE_DONE);
3964840253
}
39649
-
3965040254
}
40255
+
3965140256
return rc;
3965240257
}
3965340258
3965440259
/*
3965540260
** Return the full pathname of the database file.
@@ -39743,10 +40348,14 @@
3974340348
Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
3974440349
int rc; /* Return code */
3974540350
Pgno origPgno; /* The original page number */
3974640351
3974740352
assert( pPg->nRef>0 );
40353
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
40354
+ || pPager->eState==PAGER_WRITER_DBMOD
40355
+ );
40356
+ assert( assert_pager_state(pPager) );
3974840357
3974940358
/* In order to be able to rollback, an in-memory database must journal
3975040359
** the page we are moving from.
3975140360
*/
3975240361
if( MEMDB ){
@@ -39792,15 +40401,14 @@
3979240401
*/
3979340402
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
3979440403
needSyncPgno = pPg->pgno;
3979540404
assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
3979640405
assert( pPg->flags&PGHDR_DIRTY );
39797
- assert( pPager->needSync );
3979840406
}
3979940407
3980040408
/* If the cache contains a page with page-number pgno, remove it
39801
- ** from its hash chain. Also, if the PgHdr.needSync was set for
40409
+ ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for
3980240410
** page pgno before the 'move' operation, it needs to be retained
3980340411
** for the page moved there.
3980440412
*/
3980540413
pPg->flags &= ~PGHDR_NEED_SYNC;
3980640414
pPgOld = pager_lookup(pPager, pgno);
@@ -39808,67 +40416,59 @@
3980840416
if( pPgOld ){
3980940417
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
3981040418
if( MEMDB ){
3981140419
/* Do not discard pages from an in-memory database since we might
3981240420
** need to rollback later. Just move the page out of the way. */
39813
- assert( pPager->dbSizeValid );
3981440421
sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
3981540422
}else{
3981640423
sqlite3PcacheDrop(pPgOld);
3981740424
}
3981840425
}
3981940426
3982040427
origPgno = pPg->pgno;
3982140428
sqlite3PcacheMove(pPg, pgno);
3982240429
sqlite3PcacheMakeDirty(pPg);
39823
- pPager->dbModified = 1;
40430
+
40431
+ /* For an in-memory database, make sure the original page continues
40432
+ ** to exist, in case the transaction needs to roll back. Use pPgOld
40433
+ ** as the original page since it has already been allocated.
40434
+ */
40435
+ if( MEMDB ){
40436
+ assert( pPgOld );
40437
+ sqlite3PcacheMove(pPgOld, origPgno);
40438
+ sqlite3PagerUnref(pPgOld);
40439
+ }
3982440440
3982540441
if( needSyncPgno ){
3982640442
/* If needSyncPgno is non-zero, then the journal file needs to be
3982740443
** sync()ed before any data is written to database file page needSyncPgno.
3982840444
** Currently, no such page exists in the page-cache and the
3982940445
** "is journaled" bitvec flag has been set. This needs to be remedied by
39830
- ** loading the page into the pager-cache and setting the PgHdr.needSync
40446
+ ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC
3983140447
** flag.
3983240448
**
3983340449
** If the attempt to load the page into the page-cache fails, (due
3983440450
** to a malloc() or IO failure), clear the bit in the pInJournal[]
3983540451
** array. Otherwise, if the page is loaded and written again in
3983640452
** this transaction, it may be written to the database file before
3983740453
** it is synced into the journal file. This way, it may end up in
3983840454
** the journal file twice, but that is not a problem.
39839
- **
39840
- ** The sqlite3PagerGet() call may cause the journal to sync. So make
39841
- ** sure the Pager.needSync flag is set too.
3984240455
*/
3984340456
PgHdr *pPgHdr;
39844
- assert( pPager->needSync );
3984540457
rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
3984640458
if( rc!=SQLITE_OK ){
3984740459
if( needSyncPgno<=pPager->dbOrigSize ){
3984840460
assert( pPager->pTmpSpace!=0 );
3984940461
sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace);
3985040462
}
3985140463
return rc;
3985240464
}
39853
- pPager->needSync = 1;
39854
- assert( pPager->noSync==0 && !MEMDB );
3985540465
pPgHdr->flags |= PGHDR_NEED_SYNC;
3985640466
sqlite3PcacheMakeDirty(pPgHdr);
3985740467
sqlite3PagerUnref(pPgHdr);
3985840468
}
3985940469
39860
- /*
39861
- ** For an in-memory database, make sure the original page continues
39862
- ** to exist, in case the transaction needs to roll back. Use pPgOld
39863
- ** as the original page since it has already been allocated.
39864
- */
39865
- if( MEMDB ){
39866
- sqlite3PcacheMove(pPgOld, origPgno);
39867
- sqlite3PagerUnref(pPgOld);
39868
- }
39869
-
3987040470
return SQLITE_OK;
3987140471
}
3987240472
#endif
3987340473
3987440474
/*
@@ -39929,10 +40529,17 @@
3992940529
**
3993040530
** The returned indicate the current (possibly updated) journal-mode.
3993140531
*/
3993240532
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
3993340533
u8 eOld = pPager->journalMode; /* Prior journalmode */
40534
+
40535
+#ifdef SQLITE_DEBUG
40536
+ /* The print_pager_state() routine is intended to be used by the debugger
40537
+ ** only. We invoke it once here to suppress a compiler warning. */
40538
+ print_pager_state(pPager);
40539
+#endif
40540
+
3993440541
3993540542
/* The eMode parameter is always valid */
3993640543
assert( eMode==PAGER_JOURNALMODE_DELETE
3993740544
|| eMode==PAGER_JOURNALMODE_TRUNCATE
3993840545
|| eMode==PAGER_JOURNALMODE_PERSIST
@@ -39955,24 +40562,17 @@
3995540562
eMode = eOld;
3995640563
}
3995740564
}
3995840565
3995940566
if( eMode!=eOld ){
39960
- /* When changing between rollback modes, close the journal file prior
39961
- ** to the change. But when changing from a rollback mode to WAL, keep
39962
- ** the journal open since there is a rollback-style transaction in play
39963
- ** used to convert the version numbers in the btree header.
39964
- */
39965
- if( isOpen(pPager->jfd) && eMode!=PAGER_JOURNALMODE_WAL ){
39966
- sqlite3OsClose(pPager->jfd);
39967
- }
3996840567
3996940568
/* Change the journal mode. */
40569
+ assert( pPager->eState!=PAGER_ERROR );
3997040570
pPager->journalMode = (u8)eMode;
3997140571
3997240572
/* When transistioning from TRUNCATE or PERSIST to any other journal
39973
- ** mode except WAL (and we are not in locking_mode=EXCLUSIVE) then
40573
+ ** mode except WAL, unless the pager is in locking_mode=exclusive mode,
3997440574
** delete the journal file.
3997540575
*/
3997640576
assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
3997740577
assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
3997840578
assert( (PAGER_JOURNALMODE_DELETE & 5)==0 );
@@ -39989,28 +40589,34 @@
3998940589
**
3999040590
** Before deleting the journal file, obtain a RESERVED lock on the
3999140591
** database file. This ensures that the journal file is not deleted
3999240592
** while it is in use by some other client.
3999340593
*/
39994
- int rc = SQLITE_OK;
39995
- int state = pPager->state;
39996
- if( state<PAGER_SHARED ){
39997
- rc = sqlite3PagerSharedLock(pPager);
39998
- }
39999
- if( pPager->state==PAGER_SHARED ){
40000
- assert( rc==SQLITE_OK );
40001
- rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
40002
- }
40003
- if( rc==SQLITE_OK ){
40594
+ sqlite3OsClose(pPager->jfd);
40595
+ if( pPager->eLock>=RESERVED_LOCK ){
4000440596
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
40597
+ }else{
40598
+ int rc = SQLITE_OK;
40599
+ int state = pPager->eState;
40600
+ assert( state==PAGER_OPEN || state==PAGER_READER );
40601
+ if( state==PAGER_OPEN ){
40602
+ rc = sqlite3PagerSharedLock(pPager);
40603
+ }
40604
+ if( pPager->eState==PAGER_READER ){
40605
+ assert( rc==SQLITE_OK );
40606
+ rc = pagerLockDb(pPager, RESERVED_LOCK);
40607
+ }
40608
+ if( rc==SQLITE_OK ){
40609
+ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
40610
+ }
40611
+ if( rc==SQLITE_OK && state==PAGER_READER ){
40612
+ pagerUnlockDb(pPager, SHARED_LOCK);
40613
+ }else if( state==PAGER_OPEN ){
40614
+ pager_unlock(pPager);
40615
+ }
40616
+ assert( state==pPager->eState );
4000540617
}
40006
- if( rc==SQLITE_OK && state==PAGER_SHARED ){
40007
- sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
40008
- }else if( state==PAGER_UNLOCK ){
40009
- pager_unlock(pPager);
40010
- }
40011
- assert( state==pPager->state );
4001240618
}
4001340619
}
4001440620
4001540621
/* Return the new journal mode */
4001640622
return (int)pPager->journalMode;
@@ -40027,11 +40633,12 @@
4002740633
** Return TRUE if the pager is in a state where it is OK to change the
4002840634
** journalmode. Journalmode changes can only happen when the database
4002940635
** is unmodified.
4003040636
*/
4003140637
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
40032
- if( pPager->dbModified ) return 0;
40638
+ assert( assert_pager_state(pPager) );
40639
+ if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
4003340640
if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
4003440641
return 1;
4003540642
}
4003640643
4003740644
/*
@@ -40092,39 +40699,46 @@
4009240699
**
4009340700
** If the pager passed as the first argument is open on a real database
4009440701
** file (not a temp file or an in-memory database), and the WAL file
4009540702
** is not already open, make an attempt to open it now. If successful,
4009640703
** return SQLITE_OK. If an error occurs or the VFS used by the pager does
40097
-** not support the xShmXXX() methods, return an error code. *pisOpen is
40704
+** not support the xShmXXX() methods, return an error code. *pbOpen is
4009840705
** not modified in either case.
4009940706
**
4010040707
** If the pager is open on a temp-file (or in-memory database), or if
40101
-** the WAL file is already open, set *pisOpen to 1 and return SQLITE_OK
40708
+** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK
4010240709
** without doing anything.
4010340710
*/
4010440711
SQLITE_PRIVATE int sqlite3PagerOpenWal(
4010540712
Pager *pPager, /* Pager object */
40106
- int *pisOpen /* OUT: Set to true if call is a no-op */
40713
+ int *pbOpen /* OUT: Set to true if call is a no-op */
4010740714
){
4010840715
int rc = SQLITE_OK; /* Return code */
4010940716
40110
- assert( pPager->state>=PAGER_SHARED );
40111
- assert( (pisOpen==0 && !pPager->tempFile && !pPager->pWal) || *pisOpen==0 );
40717
+ assert( assert_pager_state(pPager) );
40718
+ assert( pPager->eState==PAGER_OPEN || pbOpen );
40719
+ assert( pPager->eState==PAGER_READER || !pbOpen );
40720
+ assert( pbOpen==0 || *pbOpen==0 );
40721
+ assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) );
4011240722
4011340723
if( !pPager->tempFile && !pPager->pWal ){
4011440724
if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;
40725
+
40726
+ /* Close any rollback journal previously open */
40727
+ sqlite3OsClose(pPager->jfd);
4011540728
4011640729
/* Open the connection to the log file. If this operation fails,
4011740730
** (e.g. due to malloc() failure), unlock the database file and
4011840731
** return an error code.
4011940732
*/
4012040733
rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
4012140734
if( rc==SQLITE_OK ){
4012240735
pPager->journalMode = PAGER_JOURNALMODE_WAL;
40736
+ pPager->eState = PAGER_OPEN;
4012340737
}
4012440738
}else{
40125
- *pisOpen = 1;
40739
+ *pbOpen = 1;
4012640740
}
4012740741
4012840742
return rc;
4012940743
}
4013040744
@@ -40146,11 +40760,11 @@
4014640760
** it may need to be checkpointed before the connection can switch to
4014740761
** rollback mode. Open it now so this can happen.
4014840762
*/
4014940763
if( !pPager->pWal ){
4015040764
int logexists = 0;
40151
- rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED);
40765
+ rc = pagerLockDb(pPager, SHARED_LOCK);
4015240766
if( rc==SQLITE_OK ){
4015340767
rc = sqlite3OsAccess(
4015440768
pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
4015540769
);
4015640770
}
@@ -40162,21 +40776,21 @@
4016240776
4016340777
/* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
4016440778
** the database file, the log and log-summary files will be deleted.
4016540779
*/
4016640780
if( rc==SQLITE_OK && pPager->pWal ){
40167
- rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE);
40781
+ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
4016840782
if( rc==SQLITE_OK ){
4016940783
rc = sqlite3WalClose(pPager->pWal,
4017040784
(pPager->noSync ? 0 : pPager->sync_flags),
4017140785
pPager->pageSize, (u8*)pPager->pTmpSpace
4017240786
);
4017340787
pPager->pWal = 0;
4017440788
}else{
4017540789
/* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
4017640790
** that we did get back to SHARED. */
40177
- sqlite3OsUnlock(pPager->fd, SQLITE_LOCK_SHARED);
40791
+ pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
4017840792
}
4017940793
}
4018040794
return rc;
4018140795
}
4018240796
@@ -40492,18 +41106,22 @@
4049241106
/*
4049341107
** The following object holds a copy of the wal-index header content.
4049441108
**
4049541109
** The actual header in the wal-index consists of two copies of this
4049641110
** object.
41111
+**
41112
+** The szPage value can be any power of 2 between 512 and 32768, inclusive.
41113
+** Or it can be 1 to represent a 65536-byte page. The latter case was
41114
+** added in 3.7.1 when support for 64K pages was added.
4049741115
*/
4049841116
struct WalIndexHdr {
4049941117
u32 iVersion; /* Wal-index version */
4050041118
u32 unused; /* Unused (padding) field */
4050141119
u32 iChange; /* Counter incremented each transaction */
4050241120
u8 isInit; /* 1 when initialized */
4050341121
u8 bigEndCksum; /* True if checksums in WAL are big-endian */
40504
- u16 szPage; /* Database page size in bytes */
41122
+ u16 szPage; /* Database page size in bytes. 1==64K */
4050541123
u32 mxFrame; /* Index of last valid frame in the WAL */
4050641124
u32 nPage; /* Size of database in pages */
4050741125
u32 aFrameCksum[2]; /* Checksum of last frame in log */
4050841126
u32 aSalt[2]; /* Two salt values copied from WAL header */
4050941127
u32 aCksum[2]; /* Checksum over all prior fields */
@@ -40610,11 +41228,11 @@
4061041228
sqlite3_file *pDbFd; /* File handle for the database file */
4061141229
sqlite3_file *pWalFd; /* File handle for WAL file */
4061241230
u32 iCallback; /* Value to pass to log callback (or 0) */
4061341231
int nWiData; /* Size of array apWiData */
4061441232
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
40615
- u16 szPage; /* Database page size */
41233
+ u32 szPage; /* Database page size */
4061641234
i16 readLock; /* Which read lock is being held. -1 for none */
4061741235
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
4061841236
u8 writeLock; /* True if in a write transaction */
4061941237
u8 ckptLock; /* True if holding a checkpoint lock */
4062041238
u8 readOnly; /* True if the WAL file is open read-only */
@@ -41281,11 +41899,11 @@
4128141899
|| szPage<512
4128241900
){
4128341901
goto finished;
4128441902
}
4128541903
pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
41286
- pWal->szPage = (u16)szPage;
41904
+ pWal->szPage = szPage;
4128741905
pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
4128841906
memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
4128941907
4129041908
/* Verify that the WAL header checksum is correct */
4129141909
walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
@@ -41331,11 +41949,13 @@
4133141949
4133241950
/* If nTruncate is non-zero, this is a commit record. */
4133341951
if( nTruncate ){
4133441952
pWal->hdr.mxFrame = iFrame;
4133541953
pWal->hdr.nPage = nTruncate;
41336
- pWal->hdr.szPage = (u16)szPage;
41954
+ pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
41955
+ testcase( szPage<=32768 );
41956
+ testcase( szPage>=65536 );
4133741957
aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
4133841958
aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
4133941959
}
4134041960
}
4134141961
@@ -41356,10 +41976,21 @@
4135641976
*/
4135741977
pInfo = walCkptInfo(pWal);
4135841978
pInfo->nBackfill = 0;
4135941979
pInfo->aReadMark[0] = 0;
4136041980
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
41981
+
41982
+ /* If more than one frame was recovered from the log file, report an
41983
+ ** event via sqlite3_log(). This is to help with identifying performance
41984
+ ** problems caused by applications routinely shutting down without
41985
+ ** checkpointing the log file.
41986
+ */
41987
+ if( pWal->hdr.nPage ){
41988
+ sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s",
41989
+ pWal->hdr.nPage, pWal->zWalName
41990
+ );
41991
+ }
4136141992
}
4136241993
4136341994
recovery_error:
4136441995
WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
4136541996
walUnlockExclusive(pWal, iLock, nLock);
@@ -41716,18 +42347,22 @@
4171642347
int sync_flags, /* Flags for OsSync() (or 0) */
4171742348
int nBuf, /* Size of zBuf in bytes */
4171842349
u8 *zBuf /* Temporary buffer to use */
4171942350
){
4172042351
int rc; /* Return code */
41721
- int szPage = pWal->hdr.szPage; /* Database page-size */
42352
+ int szPage; /* Database page-size */
4172242353
WalIterator *pIter = 0; /* Wal iterator context */
4172342354
u32 iDbpage = 0; /* Next database page to write */
4172442355
u32 iFrame = 0; /* Wal frame containing data for iDbpage */
4172542356
u32 mxSafeFrame; /* Max frame that can be backfilled */
42357
+ u32 mxPage; /* Max database page to write */
4172642358
int i; /* Loop counter */
4172742359
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
4172842360
42361
+ szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
42362
+ testcase( szPage<=32768 );
42363
+ testcase( szPage>=65536 );
4172942364
if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
4173042365
4173142366
/* Allocate the iterator */
4173242367
rc = walIteratorInit(pWal, &pIter);
4173342368
if( rc!=SQLITE_OK ){
@@ -41734,11 +42369,11 @@
4173442369
return rc;
4173542370
}
4173642371
assert( pIter );
4173742372
4173842373
/*** TODO: Move this test out to the caller. Make it an assert() here ***/
41739
- if( pWal->hdr.szPage!=nBuf ){
42374
+ if( szPage!=nBuf ){
4174042375
rc = SQLITE_CORRUPT_BKPT;
4174142376
goto walcheckpoint_out;
4174242377
}
4174342378
4174442379
/* Compute in mxSafeFrame the index of the last frame of the WAL that is
@@ -41745,10 +42380,11 @@
4174542380
** safe to write into the database. Frames beyond mxSafeFrame might
4174642381
** overwrite database pages that are in use by active readers and thus
4174742382
** cannot be backfilled from the WAL.
4174842383
*/
4174942384
mxSafeFrame = pWal->hdr.mxFrame;
42385
+ mxPage = pWal->hdr.nPage;
4175042386
pInfo = walCkptInfo(pWal);
4175142387
for(i=1; i<WAL_NREADER; i++){
4175242388
u32 y = pInfo->aReadMark[i];
4175342389
if( mxSafeFrame>=y ){
4175442390
assert( y<=pWal->hdr.mxFrame );
@@ -41765,22 +42401,34 @@
4176542401
}
4176642402
4176742403
if( pInfo->nBackfill<mxSafeFrame
4176842404
&& (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
4176942405
){
42406
+ i64 nSize; /* Current size of database file */
4177042407
u32 nBackfill = pInfo->nBackfill;
4177142408
4177242409
/* Sync the WAL to disk */
4177342410
if( sync_flags ){
4177442411
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
4177542412
}
42413
+
42414
+ /* If the database file may grow as a result of this checkpoint, hint
42415
+ ** about the eventual size of the db file to the VFS layer.
42416
+ */
42417
+ if( rc==SQLITE_OK ){
42418
+ i64 nReq = ((i64)mxPage * szPage);
42419
+ rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
42420
+ if( rc==SQLITE_OK && nSize<nReq ){
42421
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
42422
+ }
42423
+ }
4177642424
4177742425
/* Iterate through the contents of the WAL, copying data to the db file. */
4177842426
while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
4177942427
i64 iOffset;
4178042428
assert( walFramePgno(pWal, iFrame)==iDbpage );
41781
- if( iFrame<=nBackfill || iFrame>mxSafeFrame ) continue;
42429
+ if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
4178242430
iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
4178342431
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
4178442432
rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
4178542433
if( rc!=SQLITE_OK ) break;
4178642434
iOffset = (iDbpage-1)*(i64)szPage;
@@ -41883,11 +42531,11 @@
4188342531
WalIndexHdr volatile *aHdr; /* Header in shared memory */
4188442532
4188542533
/* The first page of the wal-index must be mapped at this point. */
4188642534
assert( pWal->nWiData>0 && pWal->apWiData[0] );
4188742535
41888
- /* Read the header. This might happen currently with a write to the
42536
+ /* Read the header. This might happen concurrently with a write to the
4188942537
** same area of shared memory on a different CPU in a SMP,
4189042538
** meaning it is possible that an inconsistent snapshot is read
4189142539
** from the file. If this happens, return non-zero.
4189242540
**
4189342541
** There are two copies of the header at the beginning of the wal-index.
@@ -41912,11 +42560,13 @@
4191242560
}
4191342561
4191442562
if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
4191542563
*pChanged = 1;
4191642564
memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
41917
- pWal->szPage = pWal->hdr.szPage;
42565
+ pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
42566
+ testcase( pWal->szPage<=32768 );
42567
+ testcase( pWal->szPage>=65536 );
4191842568
}
4191942569
4192042570
/* The header was successfully read. Return zero. */
4192142571
return 0;
4192242572
}
@@ -42231,10 +42881,11 @@
4223142881
/*
4223242882
** Finish with a read transaction. All this does is release the
4223342883
** read-lock.
4223442884
*/
4223542885
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
42886
+ sqlite3WalEndWriteTransaction(pWal);
4223642887
if( pWal->readLock>=0 ){
4223742888
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
4223842889
pWal->readLock = -1;
4223942890
}
4224042891
}
@@ -42341,11 +42992,17 @@
4234142992
4234242993
/* If iRead is non-zero, then it is the log frame number that contains the
4234342994
** required page. Read and return data from the log file.
4234442995
*/
4234542996
if( iRead ){
42346
- i64 iOffset = walFrameOffset(iRead, pWal->hdr.szPage) + WAL_FRAME_HDRSIZE;
42997
+ int sz;
42998
+ i64 iOffset;
42999
+ sz = pWal->hdr.szPage;
43000
+ sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
43001
+ testcase( sz<=32768 );
43002
+ testcase( sz>=65536 );
43003
+ iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
4234743004
*pInWal = 1;
4234843005
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
4234943006
return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
4235043007
}
4235143008
@@ -42353,15 +43010,17 @@
4235343010
return SQLITE_OK;
4235443011
}
4235543012
4235643013
4235743014
/*
42358
-** Set *pPgno to the size of the database file (or zero, if unknown).
43015
+** Return the size of the database in pages (or zero, if unknown).
4235943016
*/
42360
-SQLITE_PRIVATE void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno){
42361
- assert( pWal->readLock>=0 || pWal->lockError );
42362
- *pPgno = pWal->hdr.nPage;
43017
+SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
43018
+ if( pWal && ALWAYS(pWal->readLock>=0) ){
43019
+ return pWal->hdr.nPage;
43020
+ }
43021
+ return 0;
4236343022
}
4236443023
4236543024
4236643025
/*
4236743026
** This function starts a write transaction on the WAL.
@@ -42433,11 +43092,11 @@
4243343092
** Otherwise, if the callback function does not return an error, this
4243443093
** function returns SQLITE_OK.
4243543094
*/
4243643095
SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
4243743096
int rc = SQLITE_OK;
42438
- if( pWal->writeLock ){
43097
+ if( ALWAYS(pWal->writeLock) ){
4243943098
Pgno iMax = pWal->hdr.mxFrame;
4244043099
Pgno iFrame;
4244143100
4244243101
/* Restore the clients cache of the wal-index header to the state it
4244343102
** was in before the client began writing to the database.
@@ -42622,11 +43281,11 @@
4262243281
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
4262343282
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
4262443283
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
4262543284
sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
4262643285
42627
- pWal->szPage = (u16)szPage;
43286
+ pWal->szPage = szPage;
4262843287
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
4262943288
pWal->hdr.aFrameCksum[0] = aCksum[0];
4263043289
pWal->hdr.aFrameCksum[1] = aCksum[1];
4263143290
4263243291
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
@@ -42717,11 +43376,13 @@
4271743376
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
4271843377
}
4271943378
4272043379
if( rc==SQLITE_OK ){
4272143380
/* Update the private copy of the header. */
42722
- pWal->hdr.szPage = (u16)szPage;
43381
+ pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
43382
+ testcase( szPage<=32768 );
43383
+ testcase( szPage>=65536 );
4272343384
pWal->hdr.mxFrame = iFrame;
4272443385
if( isCommit ){
4272543386
pWal->hdr.iChange++;
4272643387
pWal->hdr.nPage = nTruncate;
4272743388
}
@@ -42929,11 +43590,11 @@
4292943590
**
4293043591
** FORMAT DETAILS
4293143592
**
4293243593
** The file is divided into pages. The first page is called page 1,
4293343594
** the second is page 2, and so forth. A page number of zero indicates
42934
-** "no such page". The page size can be any power of 2 between 512 and 32768.
43595
+** "no such page". The page size can be any power of 2 between 512 and 65536.
4293543596
** Each page can be either a btree page, a freelist page, an overflow
4293643597
** page, or a pointer-map page.
4293743598
**
4293843599
** The first page is always a btree page. The first 100 bytes of the first
4293943600
** page contain a special header (the "file header") that describes the file.
@@ -43295,18 +43956,18 @@
4329543956
u8 initiallyEmpty; /* Database is empty at start of transaction */
4329643957
#ifndef SQLITE_OMIT_AUTOVACUUM
4329743958
u8 autoVacuum; /* True if auto-vacuum is enabled */
4329843959
u8 incrVacuum; /* True if incr-vacuum is enabled */
4329943960
#endif
43300
- u16 pageSize; /* Total number of bytes on a page */
43301
- u16 usableSize; /* Number of usable bytes on each page */
4330243961
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
4330343962
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
4330443963
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
4330543964
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
4330643965
u8 inTransaction; /* Transaction state */
4330743966
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
43967
+ u32 pageSize; /* Total number of bytes on a page */
43968
+ u32 usableSize; /* Number of usable bytes on each page */
4330843969
int nTransaction; /* Number of open transactions (read + write) */
4330943970
u32 nPage; /* Number of pages in the database */
4331043971
void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
4331143972
void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
4331243973
sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
@@ -43901,11 +44562,20 @@
4390144562
# define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
4390244563
#else
4390344564
# define TRACE(X)
4390444565
#endif
4390544566
43906
-
44567
+/*
44568
+** Extract a 2-byte big-endian integer from an array of unsigned bytes.
44569
+** But if the value is zero, make it 65536.
44570
+**
44571
+** This routine is used to extract the "offset to cell content area" value
44572
+** from the header of a btree page. If the page size is 65536 and the page
44573
+** is empty, the offset should be 65536, but the 2-byte value stores zero.
44574
+** This routine makes the necessary adjustment to 65536.
44575
+*/
44576
+#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
4390744577
4390844578
#ifndef SQLITE_OMIT_SHARED_CACHE
4390944579
/*
4391044580
** A list of BtShared objects that are eligible for participation
4391144581
** in shared cache. This variable has file scope during normal builds,
@@ -44590,15 +45260,20 @@
4459045260
#ifndef SQLITE_OMIT_AUTOVACUUM
4459145261
/*
4459245262
** Given a page number of a regular database page, return the page
4459345263
** number for the pointer-map page that contains the entry for the
4459445264
** input page number.
45265
+**
45266
+** Return 0 (not a valid page) for pgno==1 since there is
45267
+** no pointer map associated with page 1. The integrity_check logic
45268
+** requires that ptrmapPageno(*,1)!=1.
4459545269
*/
4459645270
static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
4459745271
int nPagesPerMapPage;
4459845272
Pgno iPtrMap, ret;
4459945273
assert( sqlite3_mutex_held(pBt->mutex) );
45274
+ if( pgno<2 ) return 0;
4460045275
nPagesPerMapPage = (pBt->usableSize/5)+1;
4460145276
iPtrMap = (pgno-2)/nPagesPerMapPage;
4460245277
ret = (iPtrMap*nPagesPerMapPage) + 2;
4460345278
if( ret==PENDING_BYTE_PAGE(pBt) ){
4460445279
ret++;
@@ -45023,21 +45698,21 @@
4502345698
assert( nByte < usableSize-8 );
4502445699
4502545700
nFrag = data[hdr+7];
4502645701
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
4502745702
gap = pPage->cellOffset + 2*pPage->nCell;
45028
- top = get2byte(&data[hdr+5]);
45703
+ top = get2byteNotZero(&data[hdr+5]);
4502945704
if( gap>top ) return SQLITE_CORRUPT_BKPT;
4503045705
testcase( gap+2==top );
4503145706
testcase( gap+1==top );
4503245707
testcase( gap==top );
4503345708
4503445709
if( nFrag>=60 ){
4503545710
/* Always defragment highly fragmented pages */
4503645711
rc = defragmentPage(pPage);
4503745712
if( rc ) return rc;
45038
- top = get2byte(&data[hdr+5]);
45713
+ top = get2byteNotZero(&data[hdr+5]);
4503945714
}else if( gap+2<=top ){
4504045715
/* Search the freelist looking for a free slot big enough to satisfy
4504145716
** the request. The allocation is made from the first free slot in
4504245717
** the list that is large enough to accomadate it.
4504345718
*/
@@ -45075,11 +45750,11 @@
4507545750
*/
4507645751
testcase( gap+2+nByte==top );
4507745752
if( gap+2+nByte>top ){
4507845753
rc = defragmentPage(pPage);
4507945754
if( rc ) return rc;
45080
- top = get2byte(&data[hdr+5]);
45755
+ top = get2byteNotZero(&data[hdr+5]);
4508145756
assert( gap+nByte<=top );
4508245757
}
4508345758
4508445759
4508545760
/* Allocate memory from the gap in between the cell pointer array
@@ -45241,28 +45916,28 @@
4524145916
if( !pPage->isInit ){
4524245917
u16 pc; /* Address of a freeblock within pPage->aData[] */
4524345918
u8 hdr; /* Offset to beginning of page header */
4524445919
u8 *data; /* Equal to pPage->aData */
4524545920
BtShared *pBt; /* The main btree structure */
45246
- u16 usableSize; /* Amount of usable space on each page */
45921
+ int usableSize; /* Amount of usable space on each page */
4524745922
u16 cellOffset; /* Offset from start of page to first cell pointer */
45248
- u16 nFree; /* Number of unused bytes on the page */
45249
- u16 top; /* First byte of the cell content area */
45923
+ int nFree; /* Number of unused bytes on the page */
45924
+ int top; /* First byte of the cell content area */
4525045925
int iCellFirst; /* First allowable cell or freeblock offset */
4525145926
int iCellLast; /* Last possible cell or freeblock offset */
4525245927
4525345928
pBt = pPage->pBt;
4525445929
4525545930
hdr = pPage->hdrOffset;
4525645931
data = pPage->aData;
4525745932
if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
45258
- assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
45259
- pPage->maskPage = pBt->pageSize - 1;
45933
+ assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
45934
+ pPage->maskPage = (u16)(pBt->pageSize - 1);
4526045935
pPage->nOverflow = 0;
4526145936
usableSize = pBt->usableSize;
4526245937
pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
45263
- top = get2byte(&data[hdr+5]);
45938
+ top = get2byteNotZero(&data[hdr+5]);
4526445939
pPage->nCell = get2byte(&data[hdr+3]);
4526545940
if( pPage->nCell>MX_CELL(pBt) ){
4526645941
/* To many cells for a single page. The page must be corrupt */
4526745942
return SQLITE_CORRUPT_BKPT;
4526845943
}
@@ -45357,17 +46032,17 @@
4535746032
data[hdr] = (char)flags;
4535846033
first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
4535946034
memset(&data[hdr+1], 0, 4);
4536046035
data[hdr+7] = 0;
4536146036
put2byte(&data[hdr+5], pBt->usableSize);
45362
- pPage->nFree = pBt->usableSize - first;
46037
+ pPage->nFree = (u16)(pBt->usableSize - first);
4536346038
decodeFlags(pPage, flags);
4536446039
pPage->hdrOffset = hdr;
4536546040
pPage->cellOffset = first;
4536646041
pPage->nOverflow = 0;
45367
- assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
45368
- pPage->maskPage = pBt->pageSize - 1;
46042
+ assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
46043
+ pPage->maskPage = (u16)(pBt->pageSize - 1);
4536946044
pPage->nCell = 0;
4537046045
pPage->isInit = 1;
4537146046
}
4537246047
4537346048
@@ -45671,11 +46346,11 @@
4567146346
pBt->pPage1 = 0;
4567246347
pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
4567346348
#ifdef SQLITE_SECURE_DELETE
4567446349
pBt->secureDelete = 1;
4567546350
#endif
45676
- pBt->pageSize = get2byte(&zDbHeader[16]);
46351
+ pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
4567746352
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
4567846353
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
4567946354
pBt->pageSize = 0;
4568046355
#ifndef SQLITE_OMIT_AUTOVACUUM
4568146356
/* If the magic name ":memory:" will create an in-memory database, then
@@ -45985,11 +46660,11 @@
4598546660
assert( nReserve>=0 && nReserve<=255 );
4598646661
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
4598746662
((pageSize-1)&pageSize)==0 ){
4598846663
assert( (pageSize & 7)==0 );
4598946664
assert( !pBt->pPage1 && !pBt->pCursor );
45990
- pBt->pageSize = (u16)pageSize;
46665
+ pBt->pageSize = (u32)pageSize;
4599146666
freeTempSpace(pBt);
4599246667
}
4599346668
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
4599446669
pBt->usableSize = pBt->pageSize - (u16)nReserve;
4599546670
if( iFix ) pBt->pageSizeFixed = 1;
@@ -46120,19 +46795,17 @@
4612046795
4612146796
/* Do some checking to help insure the file we opened really is
4612246797
** a valid database file.
4612346798
*/
4612446799
nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
46125
- if( (rc = sqlite3PagerPagecount(pBt->pPager, &nPageFile))!=SQLITE_OK ){;
46126
- goto page1_init_failed;
46127
- }
46800
+ sqlite3PagerPagecount(pBt->pPager, &nPageFile);
4612846801
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
4612946802
nPage = nPageFile;
4613046803
}
4613146804
if( nPage>0 ){
46132
- int pageSize;
46133
- int usableSize;
46805
+ u32 pageSize;
46806
+ u32 usableSize;
4613446807
u8 *page1 = pPage1->aData;
4613546808
rc = SQLITE_NOTADB;
4613646809
if( memcmp(page1, zMagicHeader, 16)!=0 ){
4613746810
goto page1_init_failed;
4613846811
}
@@ -46179,28 +46852,29 @@
4617946852
** version 3.6.0, we require them to be fixed.
4618046853
*/
4618146854
if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
4618246855
goto page1_init_failed;
4618346856
}
46184
- pageSize = get2byte(&page1[16]);
46185
- if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ||
46186
- (SQLITE_MAX_PAGE_SIZE<32768 && pageSize>SQLITE_MAX_PAGE_SIZE)
46857
+ pageSize = (page1[16]<<8) | (page1[17]<<16);
46858
+ if( ((pageSize-1)&pageSize)!=0
46859
+ || pageSize>SQLITE_MAX_PAGE_SIZE
46860
+ || pageSize<=256
4618746861
){
4618846862
goto page1_init_failed;
4618946863
}
4619046864
assert( (pageSize & 7)==0 );
4619146865
usableSize = pageSize - page1[20];
46192
- if( pageSize!=pBt->pageSize ){
46866
+ if( (u32)pageSize!=pBt->pageSize ){
4619346867
/* After reading the first page of the database assuming a page size
4619446868
** of BtShared.pageSize, we have discovered that the page-size is
4619546869
** actually pageSize. Unlock the database, leave pBt->pPage1 at
4619646870
** zero and return SQLITE_OK. The caller will call this function
4619746871
** again with the correct page-size.
4619846872
*/
4619946873
releasePage(pPage1);
46200
- pBt->usableSize = (u16)usableSize;
46201
- pBt->pageSize = (u16)pageSize;
46874
+ pBt->usableSize = usableSize;
46875
+ pBt->pageSize = pageSize;
4620246876
freeTempSpace(pBt);
4620346877
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
4620446878
pageSize-usableSize);
4620546879
return rc;
4620646880
}
@@ -46209,12 +46883,12 @@
4620946883
goto page1_init_failed;
4621046884
}
4621146885
if( usableSize<480 ){
4621246886
goto page1_init_failed;
4621346887
}
46214
- pBt->pageSize = (u16)pageSize;
46215
- pBt->usableSize = (u16)usableSize;
46888
+ pBt->pageSize = pageSize;
46889
+ pBt->usableSize = usableSize;
4621646890
#ifndef SQLITE_OMIT_AUTOVACUUM
4621746891
pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
4621846892
pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
4621946893
#endif
4622046894
}
@@ -46226,18 +46900,18 @@
4622646900
** 2-byte pointer to the cell
4622746901
** 4-byte child pointer
4622846902
** 9-byte nKey value
4622946903
** 4-byte nData value
4623046904
** 4-byte overflow page pointer
46231
- ** So a cell consists of a 2-byte poiner, a header which is as much as
46905
+ ** So a cell consists of a 2-byte pointer, a header which is as much as
4623246906
** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
4623346907
** page pointer.
4623446908
*/
46235
- pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23;
46236
- pBt->minLocal = (pBt->usableSize-12)*32/255 - 23;
46237
- pBt->maxLeaf = pBt->usableSize - 35;
46238
- pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23;
46909
+ pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23);
46910
+ pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
46911
+ pBt->maxLeaf = (u16)(pBt->usableSize - 35);
46912
+ pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
4623946913
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
4624046914
pBt->pPage1 = pPage1;
4624146915
pBt->nPage = nPage;
4624246916
return SQLITE_OK;
4624346917
@@ -46286,11 +46960,12 @@
4628646960
data = pP1->aData;
4628746961
rc = sqlite3PagerWrite(pP1->pDbPage);
4628846962
if( rc ) return rc;
4628946963
memcpy(data, zMagicHeader, sizeof(zMagicHeader));
4629046964
assert( sizeof(zMagicHeader)==16 );
46291
- put2byte(&data[16], pBt->pageSize);
46965
+ data[16] = (u8)((pBt->pageSize>>8)&0xff);
46966
+ data[17] = (u8)((pBt->pageSize>>16)&0xff);
4629246967
data[18] = 1;
4629346968
data[19] = 1;
4629446969
assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize);
4629546970
data[20] = (u8)(pBt->pageSize - pBt->usableSize);
4629646971
data[21] = 64;
@@ -48297,13 +48972,13 @@
4829748972
c = +1;
4829848973
}
4829948974
pCur->validNKey = 1;
4830048975
pCur->info.nKey = nCellKey;
4830148976
}else{
48302
- /* The maximum supported page-size is 32768 bytes. This means that
48977
+ /* The maximum supported page-size is 65536 bytes. This means that
4830348978
** the maximum number of record bytes stored on an index B-Tree
48304
- ** page is at most 8198 bytes, which may be stored as a 2-byte
48979
+ ** page is less than 16384 bytes and may be stored as a 2-byte
4830548980
** varint. This information is used to attempt to avoid parsing
4830648981
** the entire cell by checking for the cases where the record is
4830748982
** stored entirely within the b-tree page by inspecting the first
4830848983
** 2 bytes of the cell.
4830948984
*/
@@ -48662,10 +49337,14 @@
4866249337
}
4866349338
if( k==0 ){
4866449339
if( !pPrevTrunk ){
4866549340
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
4866649341
}else{
49342
+ rc = sqlite3PagerWrite(pPrevTrunk->pDbPage);
49343
+ if( rc!=SQLITE_OK ){
49344
+ goto end_allocate_page;
49345
+ }
4866749346
memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
4866849347
}
4866949348
}else{
4867049349
/* The trunk page is required by the caller but it contains
4867149350
** pointers to free-list leaves. The first leaf becomes a trunk
@@ -48968,11 +49647,11 @@
4896849647
BtShared *pBt = pPage->pBt;
4896949648
CellInfo info;
4897049649
Pgno ovflPgno;
4897149650
int rc;
4897249651
int nOvfl;
48973
- u16 ovflPageSize;
49652
+ u32 ovflPageSize;
4897449653
4897549654
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
4897649655
btreeParseCellPtr(pPage, pCell, &info);
4897749656
if( info.iOverflow==0 ){
4897849657
return SQLITE_OK; /* No overflow pages. Return without doing anything */
@@ -49193,11 +49872,11 @@
4919349872
**
4919449873
** "sz" must be the number of bytes in the cell.
4919549874
*/
4919649875
static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
4919749876
int i; /* Loop counter */
49198
- int pc; /* Offset to cell content of cell being deleted */
49877
+ u32 pc; /* Offset to cell content of cell being deleted */
4919949878
u8 *data; /* pPage->aData */
4920049879
u8 *ptr; /* Used to move bytes around within data[] */
4920149880
int rc; /* The return code */
4920249881
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
4920349882
@@ -49211,11 +49890,11 @@
4921149890
ptr = &data[pPage->cellOffset + 2*idx];
4921249891
pc = get2byte(ptr);
4921349892
hdr = pPage->hdrOffset;
4921449893
testcase( pc==get2byte(&data[hdr+5]) );
4921549894
testcase( pc+sz==pPage->pBt->usableSize );
49216
- if( pc < get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
49895
+ if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
4921749896
*pRC = SQLITE_CORRUPT_BKPT;
4921849897
return;
4921949898
}
4922049899
rc = freeSpace(pPage, pc, sz);
4922149900
if( rc ){
@@ -49268,11 +49947,11 @@
4926849947
int nSkip = (iChild ? 4 : 0);
4926949948
4927049949
if( *pRC ) return;
4927149950
4927249951
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
49273
- assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
49952
+ assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
4927449953
assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
4927549954
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
4927649955
/* The cell should normally be sized correctly. However, when moving a
4927749956
** malformed cell from a leaf page to an interior page, if the cell size
4927849957
** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
@@ -49348,16 +50027,16 @@
4934850027
const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
4934950028
const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
4935050029
4935150030
assert( pPage->nOverflow==0 );
4935250031
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49353
- assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
50032
+ assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921);
4935450033
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
4935550034
4935650035
/* Check that the page has just been zeroed by zeroPage() */
4935750036
assert( pPage->nCell==0 );
49358
- assert( get2byte(&data[hdr+5])==nUsable );
50037
+ assert( get2byteNotZero(&data[hdr+5])==nUsable );
4935950038
4936050039
pCellptr = &data[pPage->cellOffset + nCell*2];
4936150040
cellbody = nUsable;
4936250041
for(i=nCell-1; i>=0; i--){
4936350042
pCellptr -= 2;
@@ -49419,10 +50098,11 @@
4941950098
4942050099
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
4942150100
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
4942250101
assert( pPage->nOverflow==1 );
4942350102
50103
+ /* This error condition is now caught prior to reaching this function */
4942450104
if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
4942550105
4942650106
/* Allocate a new page. This page will become the right-sibling of
4942750107
** pPage. Make the parent page writable, so that the new divider cell
4942850108
** may be inserted. If both these operations are successful, proceed.
@@ -49748,11 +50428,11 @@
4974850428
** In this case, temporarily copy the cell into the aOvflSpace[]
4974950429
** buffer. It will be copied out again as soon as the aSpace[] buffer
4975050430
** is allocated. */
4975150431
if( pBt->secureDelete ){
4975250432
int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
49753
- if( (iOff+szNew[i])>pBt->usableSize ){
50433
+ if( (iOff+szNew[i])>(int)pBt->usableSize ){
4975450434
rc = SQLITE_CORRUPT_BKPT;
4975550435
memset(apOld, 0, (i+1)*sizeof(MemPage*));
4975650436
goto balance_cleanup;
4975750437
}else{
4975850438
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
@@ -49827,11 +50507,11 @@
4982750507
u8 *pTemp;
4982850508
assert( nCell<nMaxCells );
4982950509
szCell[nCell] = sz;
4983050510
pTemp = &aSpace1[iSpace1];
4983150511
iSpace1 += sz;
49832
- assert( sz<=pBt->pageSize/4 );
50512
+ assert( sz<=pBt->maxLocal+23 );
4983350513
assert( iSpace1<=pBt->pageSize );
4983450514
memcpy(pTemp, apDiv[i], sz);
4983550515
apCell[nCell] = pTemp+leafCorrection;
4983650516
assert( leafCorrection==0 || leafCorrection==4 );
4983750517
szCell[nCell] = szCell[nCell] - leafCorrection;
@@ -50073,11 +50753,11 @@
5007350753
assert(leafCorrection==4);
5007450754
sz = cellSizePtr(pParent, pCell);
5007550755
}
5007650756
}
5007750757
iOvflSpace += sz;
50078
- assert( sz<=pBt->pageSize/4 );
50758
+ assert( sz<=pBt->maxLocal+23 );
5007950759
assert( iOvflSpace<=pBt->pageSize );
5008050760
insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
5008150761
if( rc!=SQLITE_OK ) goto balance_cleanup;
5008250762
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
5008350763
@@ -51317,11 +51997,11 @@
5131751997
#ifndef SQLITE_OMIT_AUTOVACUUM
5131851998
if( pCheck->pBt->autoVacuum ){
5131951999
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
5132052000
}
5132152001
#endif
51322
- if( n>pCheck->pBt->usableSize/4-2 ){
52002
+ if( n>(int)pCheck->pBt->usableSize/4-2 ){
5132352003
checkAppendMsg(pCheck, zContext,
5132452004
"freelist leaf count too big on page %d", iPage);
5132552005
N--;
5132652006
}else{
5132752007
for(i=0; i<n; i++){
@@ -51528,24 +52208,24 @@
5152852208
hdr = pPage->hdrOffset;
5152952209
hit = sqlite3PageMalloc( pBt->pageSize );
5153052210
if( hit==0 ){
5153152211
pCheck->mallocFailed = 1;
5153252212
}else{
51533
- u16 contentOffset = get2byte(&data[hdr+5]);
52213
+ int contentOffset = get2byteNotZero(&data[hdr+5]);
5153452214
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
5153552215
memset(hit+contentOffset, 0, usableSize-contentOffset);
5153652216
memset(hit, 1, contentOffset);
5153752217
nCell = get2byte(&data[hdr+3]);
5153852218
cellStart = hdr + 12 - 4*pPage->leaf;
5153952219
for(i=0; i<nCell; i++){
5154052220
int pc = get2byte(&data[cellStart+i*2]);
51541
- u16 size = 1024;
52221
+ u32 size = 65536;
5154252222
int j;
5154352223
if( pc<=usableSize-4 ){
5154452224
size = cellSizePtr(pPage, &data[pc]);
5154552225
}
51546
- if( (pc+size-1)>=usableSize ){
52226
+ if( (int)(pc+size-1)>=usableSize ){
5154752227
checkAppendMsg(pCheck, 0,
5154852228
"Corruption detected in cell %d on page %d",i,iPage);
5154952229
}else{
5155052230
for(j=pc+size-1; j>=pc; j--) hit[j]++;
5155152231
}
@@ -52164,10 +52844,19 @@
5216452844
** page sizes of the source and destination differ.
5216552845
*/
5216652846
if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
5216752847
rc = SQLITE_READONLY;
5216852848
}
52849
+
52850
+#ifdef SQLITE_HAS_CODEC
52851
+ /* Backup is not possible if the page size of the destination is changing
52852
+ ** a a codec is in use.
52853
+ */
52854
+ if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){
52855
+ rc = SQLITE_READONLY;
52856
+ }
52857
+#endif
5216952858
5217052859
/* This loop runs once for each destination page spanned by the source
5217152860
** page. For each iteration, variable iOff is set to the byte offset
5217252861
** of the destination page.
5217352862
*/
@@ -55769,12 +56458,21 @@
5576956458
mrc = p->rc & 0xff;
5577056459
assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
5577156460
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
5577256461
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
5577356462
if( isSpecialError ){
55774
- /* If the query was read-only, we need do no rollback at all. Otherwise,
55775
- ** proceed with the special handling.
56463
+ /* If the query was read-only and the error code is SQLITE_INTERRUPT,
56464
+ ** no rollback is necessary. Otherwise, at least a savepoint
56465
+ ** transaction must be rolled back to restore the database to a
56466
+ ** consistent state.
56467
+ **
56468
+ ** Even if the statement is read-only, it is important to perform
56469
+ ** a statement or transaction rollback operation. If the error
56470
+ ** occured while writing to the journal, sub-journal or database
56471
+ ** file as part of an effort to free up cache space (see function
56472
+ ** pagerStress() in pager.c), the rollback is required to restore
56473
+ ** the pager to a consistent state.
5577656474
*/
5577756475
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
5577856476
if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
5577956477
eStatementOp = SAVEPOINT_ROLLBACK;
5578056478
}else{
@@ -63200,11 +63898,11 @@
6320063898
** If P5 is non-zero then the key value is increased by an epsilon
6320163899
** prior to the comparison. This make the opcode work like IdxGT except
6320263900
** that if the key from register P3 is a prefix of the key in the cursor,
6320363901
** the result is false whereas it would be true with IdxGT.
6320463902
*/
63205
-/* Opcode: IdxLT P1 P2 P3 * P5
63903
+/* Opcode: IdxLT P1 P2 P3 P4 P5
6320663904
**
6320763905
** The P4 register values beginning with P3 form an unpacked index
6320863906
** key that omits the ROWID. Compare this key value against the index
6320963907
** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
6321063908
**
@@ -67547,22 +68245,23 @@
6754768245
assert( z[0]=='?' );
6754868246
pExpr->iColumn = (ynVar)(++pParse->nVar);
6754968247
}else if( z[0]=='?' ){
6755068248
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
6755168249
** use it as the variable number */
67552
- int i = atoi((char*)&z[1]);
68250
+ i64 i;
68251
+ int bOk = sqlite3Atoi64(&z[1], &i);
6755368252
pExpr->iColumn = (ynVar)i;
6755468253
testcase( i==0 );
6755568254
testcase( i==1 );
6755668255
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
6755768256
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
67558
- if( i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
68257
+ if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
6755968258
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
6756068259
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
6756168260
}
6756268261
if( i>pParse->nVar ){
67563
- pParse->nVar = i;
68262
+ pParse->nVar = (int)i;
6756468263
}
6756568264
}else{
6756668265
/* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
6756768266
** number as the prior appearance of the same name, or if the name
6756868267
** has never appeared before, reuse the same variable number
@@ -72092,10 +72791,11 @@
7209272791
}
7209372792
}
7209472793
sqlite3DbFree(db, pIdx->aSample);
7209572794
}
7209672795
#else
72796
+ UNUSED_PARAMETER(db);
7209772797
UNUSED_PARAMETER(pIdx);
7209872798
#endif
7209972799
}
7210072800
7210172801
/*
@@ -87738,11 +88438,11 @@
8773888438
** be sent.
8773988439
**
8774088440
** regReturn is the number of the register holding the subroutine
8774188441
** return address.
8774288442
**
87743
-** If regPrev>0 then it is a the first register in a vector that
88443
+** If regPrev>0 then it is the first register in a vector that
8774488444
** records the previous output. mem[regPrev] is a flag that is false
8774588445
** if there has been no previous output. If regPrev>0 then code is
8774688446
** generated to suppress duplicates. pKeyInfo is used for comparing
8774788447
** keys.
8774888448
**
@@ -88435,16 +89135,17 @@
8843589135
** (1) The subquery and the outer query do not both use aggregates.
8843689136
**
8843789137
** (2) The subquery is not an aggregate or the outer query is not a join.
8843889138
**
8843989139
** (3) The subquery is not the right operand of a left outer join
88440
-** (Originally ticket #306. Strenghtened by ticket #3300)
89140
+** (Originally ticket #306. Strengthened by ticket #3300)
8844189141
**
88442
-** (4) The subquery is not DISTINCT or the outer query is not a join.
89142
+** (4) The subquery is not DISTINCT.
8844389143
**
88444
-** (5) The subquery is not DISTINCT or the outer query does not use
88445
-** aggregates.
89144
+** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT
89145
+** sub-queries that were excluded from this optimization. Restriction
89146
+** (4) has since been expanded to exclude all DISTINCT subqueries.
8844689147
**
8844789148
** (6) The subquery does not use aggregates or the outer query is not
8844889149
** DISTINCT.
8844989150
**
8845089151
** (7) The subquery has a FROM clause.
@@ -88460,13 +89161,13 @@
8846089161
** (11) The subquery and the outer query do not both have ORDER BY clauses.
8846189162
**
8846289163
** (**) Not implemented. Subsumed into restriction (3). Was previously
8846389164
** a separate restriction deriving from ticket #350.
8846489165
**
88465
-** (13) The subquery and outer query do not both use LIMIT
89166
+** (13) The subquery and outer query do not both use LIMIT.
8846689167
**
88467
-** (14) The subquery does not use OFFSET
89168
+** (14) The subquery does not use OFFSET.
8846889169
**
8846989170
** (15) The outer query is not part of a compound select or the
8847089171
** subquery does not have a LIMIT clause.
8847189172
** (See ticket #2339 and ticket [02a8e81d44]).
8847289173
**
@@ -88553,13 +89254,13 @@
8855389254
if( pSub->pOffset ) return 0; /* Restriction (14) */
8855489255
if( p->pRightmost && pSub->pLimit ){
8855589256
return 0; /* Restriction (15) */
8855689257
}
8855789258
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
88558
- if( ((pSub->selFlags & SF_Distinct)!=0 || pSub->pLimit)
88559
- && (pSrc->nSrc>1 || isAgg) ){ /* Restrictions (4)(5)(8)(9) */
88560
- return 0;
89259
+ if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */
89260
+ if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){
89261
+ return 0; /* Restrictions (8)(9) */
8856189262
}
8856289263
if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
8856389264
return 0; /* Restriction (6) */
8856489265
}
8856589266
if( p->pOrderBy && pSub->pOrderBy ){
@@ -93225,11 +93926,11 @@
9322593926
pParse->pNewTable->aCol = 0;
9322693927
}
9322793928
db->pVTab = 0;
9322893929
}else{
9322993930
sqlite3Error(db, SQLITE_ERROR, zErr);
93230
- sqlite3_free(zErr);
93931
+ sqlite3DbFree(db, zErr);
9323193932
rc = SQLITE_ERROR;
9323293933
}
9323393934
pParse->declareVtab = 0;
9323493935
9323593936
if( pParse->pVdbe ){
@@ -96859,39 +97560,39 @@
9685997560
**
9686097561
** This case is also used when there are no WHERE clause
9686197562
** constraints but an index is selected anyway, in order
9686297563
** to force the output order to conform to an ORDER BY.
9686397564
*/
96864
- int aStartOp[] = {
97565
+ static const u8 aStartOp[] = {
9686597566
0,
9686697567
0,
9686797568
OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
9686897569
OP_Last, /* 3: (!start_constraints && startEq && bRev) */
9686997570
OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
9687097571
OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
9687197572
OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
9687297573
OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
9687397574
};
96874
- int aEndOp[] = {
97575
+ static const u8 aEndOp[] = {
9687597576
OP_Noop, /* 0: (!end_constraints) */
9687697577
OP_IdxGE, /* 1: (end_constraints && !bRev) */
9687797578
OP_IdxLT /* 2: (end_constraints && bRev) */
9687897579
};
96879
- int nEq = pLevel->plan.nEq;
97580
+ int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
9688097581
int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
9688197582
int regBase; /* Base register holding constraint values */
9688297583
int r1; /* Temp register */
9688397584
WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
9688497585
WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
9688597586
int startEq; /* True if range start uses ==, >= or <= */
9688697587
int endEq; /* True if range end uses ==, >= or <= */
9688797588
int start_constraints; /* Start of range is constrained */
9688897589
int nConstraint; /* Number of constraint terms */
96889
- Index *pIdx; /* The index we will be using */
96890
- int iIdxCur; /* The VDBE cursor for the index */
96891
- int nExtraReg = 0; /* Number of extra registers needed */
96892
- int op; /* Instruction opcode */
97590
+ Index *pIdx; /* The index we will be using */
97591
+ int iIdxCur; /* The VDBE cursor for the index */
97592
+ int nExtraReg = 0; /* Number of extra registers needed */
97593
+ int op; /* Instruction opcode */
9689397594
char *zStartAff; /* Affinity for start of range constraint */
9689497595
char *zEndAff; /* Affinity for end of range constraint */
9689597596
9689697597
pIdx = pLevel->plan.u.pIdx;
9689797598
iIdxCur = pLevel->iIdxCur;
@@ -108720,11 +109421,11 @@
108720109421
** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following
108721109422
** two forward declarations are for functions declared in these files
108722109423
** used to retrieve the respective implementations.
108723109424
**
108724109425
** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
108725
-** to by the argument to point a the "simple" tokenizer implementation.
109426
+** to by the argument to point to the "simple" tokenizer implementation.
108726109427
** Function ...PorterTokenizerModule() sets *pModule to point to the
108727109428
** porter tokenizer/stemmer implementation.
108728109429
*/
108729109430
SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
108730109431
SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
@@ -108922,11 +109623,11 @@
108922109623
** is defined to accept an argument of type char, and always returns 0 for
108923109624
** any values that fall outside of the range of the unsigned char type (i.e.
108924109625
** negative values).
108925109626
*/
108926109627
static int fts3isspace(char c){
108927
- return (c&0x80)==0 ? isspace(c) : 0;
109628
+ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
108928109629
}
108929109630
108930109631
/*
108931109632
** Extract the next token from buffer z (length n) using the tokenizer
108932109633
** and other information (column names etc.) in pParse. Create an Fts3Expr
@@ -111309,10 +112010,13 @@
111309112010
111310112011
111311112012
static int simpleDelim(simple_tokenizer *t, unsigned char c){
111312112013
return c<0x80 && t->delim[c];
111313112014
}
112015
+static int fts3_isalnum(int x){
112016
+ return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z');
112017
+}
111314112018
111315112019
/*
111316112020
** Create a new tokenizer instance.
111317112021
*/
111318112022
static int simpleCreate(
@@ -111343,11 +112047,11 @@
111343112047
}
111344112048
} else {
111345112049
/* Mark non-alphanumeric ASCII characters as delimiters */
111346112050
int i;
111347112051
for(i=1; i<0x80; i++){
111348
- t->delim[i] = !isalnum(i) ? -1 : 0;
112052
+ t->delim[i] = !fts3_isalnum(i) ? -1 : 0;
111349112053
}
111350112054
}
111351112055
111352112056
*ppTokenizer = &t->base;
111353112057
return SQLITE_OK;
@@ -111449,11 +112153,11 @@
111449112153
for(i=0; i<n; i++){
111450112154
/* TODO(shess) This needs expansion to handle UTF-8
111451112155
** case-insensitivity.
111452112156
*/
111453112157
unsigned char ch = p[iStartOffset+i];
111454
- c->pToken[i] = (char)(ch<0x80 ? tolower(ch) : ch);
112158
+ c->pToken[i] = (char)((ch>='A' && ch<='Z') ? ch-'A'+'a' : ch);
111455112159
}
111456112160
*ppToken = c->pToken;
111457112161
*pnBytes = n;
111458112162
*piStartOffset = iStartOffset;
111459112163
*piEndOffset = c->iOffset;
@@ -116355,15 +117059,14 @@
116355117059
** least desirable):
116356117060
**
116357117061
** idxNum idxStr Strategy
116358117062
** ------------------------------------------------
116359117063
** 1 Unused Direct lookup by rowid.
116360
-** 2 See below R-tree query.
116361
-** 3 Unused Full table scan.
117064
+** 2 See below R-tree query or full-table scan.
116362117065
** ------------------------------------------------
116363117066
**
116364
-** If strategy 1 or 3 is used, then idxStr is not meaningful. If strategy
117067
+** If strategy 1 is used, then idxStr is not meaningful. If strategy
116365117068
** 2 is used, idxStr is formatted to contain 2 bytes for each
116366117069
** constraint used. The first two bytes of idxStr correspond to
116367117070
** the constraint in sqlite3_index_info.aConstraintUsage[] with
116368117071
** (argvIndex==1) etc.
116369117072
**
116370117073
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.7.1. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a one translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% are more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -213,24 +213,25 @@
213 */
214 #ifndef SQLITE_MAX_VARIABLE_NUMBER
215 # define SQLITE_MAX_VARIABLE_NUMBER 999
216 #endif
217
218 /* Maximum page size. The upper bound on this value is 32768. This a limit
219 ** imposed by the necessity of storing the value in a 2-byte unsigned integer
220 ** and the fact that the page size must be a power of 2.
221 **
222 ** If this limit is changed, then the compiled library is technically
223 ** incompatible with an SQLite library compiled with a different limit. If
224 ** a process operating on a database with a page-size of 65536 bytes
225 ** crashes, then an instance of SQLite compiled with the default page-size
226 ** limit will not be able to rollback the aborted transaction. This could
227 ** lead to database corruption.
 
228 */
229 #ifndef SQLITE_MAX_PAGE_SIZE
230 # define SQLITE_MAX_PAGE_SIZE 32768
231 #endif
 
232
233
234 /*
235 ** The default size of a database page.
236 */
@@ -631,23 +632,23 @@
631 ** be held constant and Z will be incremented or else Y will be incremented
632 ** and Z will be reset to zero.
633 **
634 ** Since version 3.6.18, SQLite source code has been stored in the
635 ** <a href="http://www.fossil-scm.org/">Fossil configuration management
636 ** system</a>. ^The SQLITE_SOURCE_ID macro evalutes to
637 ** a string which identifies a particular check-in of SQLite
638 ** within its configuration management system. ^The SQLITE_SOURCE_ID
639 ** string contains the date and time of the check-in (UTC) and an SHA1
640 ** hash of the entire source tree.
641 **
642 ** See also: [sqlite3_libversion()],
643 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
644 ** [sqlite_version()] and [sqlite_source_id()].
645 */
646 #define SQLITE_VERSION "3.7.1"
647 #define SQLITE_VERSION_NUMBER 3007001
648 #define SQLITE_SOURCE_ID "2010-08-05 03:21:40 fbe70e1106bcc5086ceb9d8f39cc39baf3643092"
649
650 /*
651 ** CAPI3REF: Run-Time Library Version Numbers
652 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
653 **
@@ -688,19 +689,19 @@
688 ** ^The sqlite3_compileoption_used() function returns 0 or 1
689 ** indicating whether the specified option was defined at
690 ** compile time. ^The SQLITE_ prefix may be omitted from the
691 ** option name passed to sqlite3_compileoption_used().
692 **
693 ** ^The sqlite3_compileoption_get() function allows interating
694 ** over the list of options that were defined at compile time by
695 ** returning the N-th compile time option string. ^If N is out of range,
696 ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
697 ** prefix is omitted from any strings returned by
698 ** sqlite3_compileoption_get().
699 **
700 ** ^Support for the diagnostic functions sqlite3_compileoption_used()
701 ** and sqlite3_compileoption_get() may be omitted by specifing the
702 ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
703 **
704 ** See also: SQL functions [sqlite_compileoption_used()] and
705 ** [sqlite_compileoption_get()] and the [compile_options pragma].
706 */
@@ -802,11 +803,11 @@
802 /*
803 ** CAPI3REF: Closing A Database Connection
804 **
805 ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
806 ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
807 ** successfullly destroyed and all associated resources are deallocated.
808 **
809 ** Applications must [sqlite3_finalize | finalize] all [prepared statements]
810 ** and [sqlite3_blob_close | close] all [BLOB handles] associated with
811 ** the [sqlite3] object prior to attempting to close the object. ^If
812 ** sqlite3_close() is called on a [database connection] that still has
@@ -1229,16 +1230,25 @@
1229 ** layer a hint of how large the database file will grow to be during the
1230 ** current transaction. This hint is not guaranteed to be accurate but it
1231 ** is often close. The underlying VFS might choose to preallocate database
1232 ** file space based on this hint in order to help writes to the database
1233 ** file run faster.
 
 
 
 
 
 
 
 
1234 */
1235 #define SQLITE_FCNTL_LOCKSTATE 1
1236 #define SQLITE_GET_LOCKPROXYFILE 2
1237 #define SQLITE_SET_LOCKPROXYFILE 3
1238 #define SQLITE_LAST_ERRNO 4
1239 #define SQLITE_FCNTL_SIZE_HINT 5
 
1240
1241 /*
1242 ** CAPI3REF: Mutex Handle
1243 **
1244 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -3197,11 +3207,11 @@
3197 ** <li> @VVV
3198 ** <li> $VVV
3199 ** </ul>
3200 **
3201 ** In the templates above, NNN represents an integer literal,
3202 ** and VVV represents an alphanumeric identifer.)^ ^The values of these
3203 ** parameters (also called "host parameter names" or "SQL parameters")
3204 ** can be set using the sqlite3_bind_*() routines defined here.
3205 **
3206 ** ^The first argument to the sqlite3_bind_*() routines is always
3207 ** a pointer to the [sqlite3_stmt] object returned from
@@ -3976,11 +3986,11 @@
3976 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
3977
3978 /*
3979 ** CAPI3REF: Obtain Aggregate Function Context
3980 **
3981 ** Implementions of aggregate SQL functions use this
3982 ** routine to allocate memory for storing their state.
3983 **
3984 ** ^The first time the sqlite3_aggregate_context(C,N) routine is called
3985 ** for a particular aggregate function, SQLite
3986 ** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -4248,11 +4258,11 @@
4248 ** the routine expects pointers to 16-bit word aligned strings
4249 ** of UTF-16 in the native byte order.
4250 **
4251 ** A pointer to the user supplied routine must be passed as the fifth
4252 ** argument. ^If it is NULL, this is the same as deleting the collation
4253 ** sequence (so that SQLite cannot call it anymore).
4254 ** ^Each time the application supplied function is invoked, it is passed
4255 ** as its first parameter a copy of the void* passed as the fourth argument
4256 ** to sqlite3_create_collation() or sqlite3_create_collation16().
4257 **
4258 ** ^The remaining arguments to the application-supplied routine are two strings,
@@ -5466,11 +5476,11 @@
5466 ** of passing a NULL pointer instead of a valid mutex handle are undefined
5467 ** (i.e. it is acceptable to provide an implementation that segfaults if
5468 ** it is passed a NULL pointer).
5469 **
5470 ** The xMutexInit() method must be threadsafe. ^It must be harmless to
5471 ** invoke xMutexInit() mutiple times within the same process and without
5472 ** intervening calls to xMutexEnd(). Second and subsequent calls to
5473 ** xMutexInit() must be no-ops.
5474 **
5475 ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
5476 ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5636,11 +5646,11 @@
5636
5637 /*
5638 ** CAPI3REF: SQLite Runtime Status
5639 **
5640 ** ^This interface is used to retrieve runtime status information
5641 ** about the preformance of SQLite, and optionally to reset various
5642 ** highwater marks. ^The first argument is an integer code for
5643 ** the specific parameter to measure. ^(Recognized integer codes
5644 ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
5645 ** ^The current value of the parameter is returned into *pCurrent.
5646 ** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5762,11 +5772,11 @@
5762 ** ^This interface is used to retrieve runtime status information
5763 ** about a single [database connection]. ^The first argument is the
5764 ** database connection object to be interrogated. ^The second argument
5765 ** is an integer constant, taken from the set of
5766 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5767 ** determiness the parameter to interrogate. The set of
5768 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
5769 ** to grow in future releases of SQLite.
5770 **
5771 ** ^The current value of the requested parameter is written into *pCur
5772 ** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -6184,11 +6194,11 @@
6184 **
6185 ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
6186 **
6187 ** ^Each call to sqlite3_backup_step() sets two values inside
6188 ** the [sqlite3_backup] object: the number of pages still to be backed
6189 ** up and the total number of pages in the source databae file.
6190 ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
6191 ** retrieve these two values, respectively.
6192 **
6193 ** ^The values returned by these functions are only updated by
6194 ** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -6280,11 +6290,11 @@
6280 ** ^(There may be at most one unlock-notify callback registered by a
6281 ** blocked connection. If sqlite3_unlock_notify() is called when the
6282 ** blocked connection already has a registered unlock-notify callback,
6283 ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
6284 ** called with a NULL pointer as its second argument, then any existing
6285 ** unlock-notify callback is cancelled. ^The blocked connections
6286 ** unlock-notify callback may also be canceled by closing the blocked
6287 ** connection using [sqlite3_close()].
6288 **
6289 ** The unlock-notify callback is not reentrant. If an application invokes
6290 ** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -6362,11 +6372,11 @@
6362 /*
6363 ** CAPI3REF: String Comparison
6364 **
6365 ** ^The [sqlite3_strnicmp()] API allows applications and extensions to
6366 ** compare the contents of two buffers containing UTF-8 strings in a
6367 ** case-indendent fashion, using the same definition of case independence
6368 ** that SQLite uses internally when comparing identifiers.
6369 */
6370 SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
6371
6372 /*
@@ -7868,11 +7878,11 @@
7868 SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager);
7869 SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
7870
7871 /* Functions used to configure a Pager object. */
7872 SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
7873 SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*, int);
7874 SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
7875 SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
7876 SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int);
7877 SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
7878 SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
@@ -7895,11 +7905,11 @@
7895 SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*);
7896 SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *);
7897 SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *);
7898
7899 /* Functions used to manage pager transactions and savepoints. */
7900 SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*, int*);
7901 SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
7902 SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
7903 SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
7904 SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager);
7905 SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
@@ -9183,13 +9193,13 @@
9183 ** argument to sqlite3VdbeKeyCompare and is used to control the
9184 ** comparison of the two index keys.
9185 */
9186 struct KeyInfo {
9187 sqlite3 *db; /* The database connection */
9188 u8 enc; /* Text encoding - one of the TEXT_Utf* values */
9189 u16 nField; /* Number of entries in aColl[] */
9190 u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */
9191 CollSeq *aColl[1]; /* Collating sequence for each term of the key */
9192 };
9193
9194 /*
9195 ** An instance of the following structure holds information about a
@@ -14331,11 +14341,11 @@
14331
14332 /*
14333 ** Set the "type" of an allocation.
14334 */
14335 SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
14336 if( p ){
14337 struct MemBlockHdr *pHdr;
14338 pHdr = sqlite3MemsysGetHeader(p);
14339 assert( pHdr->iForeGuard==FOREGUARD );
14340 pHdr->eType = eType;
14341 }
@@ -14350,11 +14360,11 @@
14350 **
14351 ** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
14352 */
14353 SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
14354 int rc = 1;
14355 if( p ){
14356 struct MemBlockHdr *pHdr;
14357 pHdr = sqlite3MemsysGetHeader(p);
14358 assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
14359 if( (pHdr->eType&eType)==0 ){
14360 rc = 0;
@@ -14372,11 +14382,11 @@
14372 **
14373 ** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
14374 */
14375 SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
14376 int rc = 1;
14377 if( p ){
14378 struct MemBlockHdr *pHdr;
14379 pHdr = sqlite3MemsysGetHeader(p);
14380 assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
14381 if( (pHdr->eType&eType)!=0 ){
14382 rc = 0;
@@ -16624,10 +16634,11 @@
16624 #else
16625 /* Use the built-in recursive mutexes if they are available.
16626 */
16627 pthread_mutex_lock(&p->mutex);
16628 #if SQLITE_MUTEX_NREF
 
16629 p->owner = pthread_self();
16630 p->nRef++;
16631 #endif
16632 #endif
16633
@@ -16696,10 +16707,11 @@
16696 */
16697 static void pthreadMutexLeave(sqlite3_mutex *p){
16698 assert( pthreadMutexHeld(p) );
16699 #if SQLITE_MUTEX_NREF
16700 p->nRef--;
 
16701 #endif
16702 assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
16703
16704 #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
16705 if( p->nRef==0 ){
@@ -16960,11 +16972,11 @@
16960 ** allocated mutex. SQLite is careful to deallocate every
16961 ** mutex that it allocates.
16962 */
16963 static void winMutexFree(sqlite3_mutex *p){
16964 assert( p );
16965 assert( p->nRef==0 );
16966 assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
16967 DeleteCriticalSection(&p->mutex);
16968 sqlite3_free(p);
16969 }
16970
@@ -16984,10 +16996,11 @@
16984 DWORD tid = GetCurrentThreadId();
16985 assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
16986 #endif
16987 EnterCriticalSection(&p->mutex);
16988 #ifdef SQLITE_DEBUG
 
16989 p->owner = tid;
16990 p->nRef++;
16991 if( p->trace ){
16992 printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
16993 }
@@ -17037,10 +17050,11 @@
17037 #ifndef NDEBUG
17038 DWORD tid = GetCurrentThreadId();
17039 assert( p->nRef>0 );
17040 assert( p->owner==tid );
17041 p->nRef--;
 
17042 assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
17043 #endif
17044 LeaveCriticalSection(&p->mutex);
17045 #ifdef SQLITE_DEBUG
17046 if( p->trace ){
@@ -22609,10 +22623,11 @@
22609 void *lockingContext; /* Locking style specific state */
22610 UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
22611 int fileFlags; /* Miscellanous flags */
22612 const char *zPath; /* Name of the file */
22613 unixShm *pShm; /* Shared memory segment information */
 
22614 #if SQLITE_ENABLE_LOCKING_STYLE
22615 int openFlags; /* The flags specified at open() */
22616 #endif
22617 #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
22618 unsigned fsFlags; /* cached details from statfs() */
@@ -25367,19 +25382,21 @@
25367 offset += wrote;
25368 pBuf = &((char*)pBuf)[wrote];
25369 }
25370 SimulateIOError(( wrote=(-1), amt=1 ));
25371 SimulateDiskfullError(( wrote=0, amt=1 ));
 
25372 if( amt>0 ){
25373 if( wrote<0 ){
25374 /* lastErrno set by seekAndWrite */
25375 return SQLITE_IOERR_WRITE;
25376 }else{
25377 pFile->lastErrno = 0; /* not a system error */
25378 return SQLITE_FULL;
25379 }
25380 }
 
25381 return SQLITE_OK;
25382 }
25383
25384 #ifdef SQLITE_TEST
25385 /*
@@ -25577,16 +25594,27 @@
25577
25578 /*
25579 ** Truncate an open file to a specified size
25580 */
25581 static int unixTruncate(sqlite3_file *id, i64 nByte){
 
25582 int rc;
25583 assert( id );
25584 SimulateIOError( return SQLITE_IOERR_TRUNCATE );
25585 rc = ftruncate(((unixFile*)id)->h, (off_t)nByte);
 
 
 
 
 
 
 
 
 
 
25586 if( rc ){
25587 ((unixFile*)id)->lastErrno = errno;
25588 return SQLITE_IOERR_TRUNCATE;
25589 }else{
25590 #ifndef NDEBUG
25591 /* If we are doing a normal write to a database file (as opposed to
25592 ** doing a hot-journal rollback or a write to some file other than a
@@ -25593,12 +25621,12 @@
25593 ** normal database file) and we truncate the file to zero length,
25594 ** that effectively updates the change counter. This might happen
25595 ** when restoring a database using the backup API from a zero-length
25596 ** source.
25597 */
25598 if( ((unixFile*)id)->inNormalWrite && nByte==0 ){
25599 ((unixFile*)id)->transCntrChng = 1;
25600 }
25601 #endif
25602
25603 return SQLITE_OK;
25604 }
@@ -25637,10 +25665,58 @@
25637 ** proxying locking division.
25638 */
25639 static int proxyFileControl(sqlite3_file*,int,void*);
25640 #endif
25641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25642
25643 /*
25644 ** Information and control of an open file handle.
25645 */
25646 static int unixFileControl(sqlite3_file *id, int op, void *pArg){
@@ -25650,18 +25726,17 @@
25650 return SQLITE_OK;
25651 }
25652 case SQLITE_LAST_ERRNO: {
25653 *(int*)pArg = ((unixFile*)id)->lastErrno;
25654 return SQLITE_OK;
 
 
 
 
25655 }
25656 case SQLITE_FCNTL_SIZE_HINT: {
25657 #if 0 /* No performance advantage seen on Linux */
25658 sqlite3_int64 szFile = *(sqlite3_int64*)pArg;
25659 unixFile *pFile = (unixFile*)id;
25660 ftruncate(pFile->h, szFile);
25661 #endif
25662 return SQLITE_OK;
25663 }
25664 #ifndef NDEBUG
25665 /* The pager calls this method to signal that it has done
25666 ** a rollback and that the database is therefore unchanged and
25667 ** it hence it is OK for the transaction change counter to be
@@ -28522,11 +28597,11 @@
28522 }else{
28523 if( pCtx->conchFile ){
28524 pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
28525 sqlite3_free(pCtx->conchFile);
28526 }
28527 sqlite3_free(pCtx->lockProxyPath);
28528 sqlite3_free(pCtx->conchFilePath);
28529 sqlite3_free(pCtx);
28530 }
28531 OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
28532 (rc==SQLITE_OK ? "ok" : "failed")));
@@ -28713,13 +28788,13 @@
28713 }
28714 rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
28715 if( rc ) return rc;
28716 sqlite3_free(conchFile);
28717 }
28718 sqlite3_free(pCtx->lockProxyPath);
28719 sqlite3_free(pCtx->conchFilePath);
28720 sqlite3_free(pCtx->dbPath);
28721 /* restore the original locking context and pMethod then close it */
28722 pFile->lockingContext = pCtx->oldLockingContext;
28723 pFile->pMethod = pCtx->pOldMethod;
28724 sqlite3_free(pCtx);
28725 return pFile->pMethod->xClose(id);
@@ -29161,10 +29236,11 @@
29161 short sharedLockByte; /* Randomly chosen byte used as a shared lock */
29162 DWORD lastErrno; /* The Windows errno from the last I/O error */
29163 DWORD sectorSize; /* Sector size of the device file is on */
29164 winShm *pShm; /* Instance of shared memory on this file */
29165 const char *zPath; /* Full pathname of this file */
 
29166 #if SQLITE_OS_WINCE
29167 WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
29168 HANDLE hMutex; /* Mutex used to control access to shared lock */
29169 HANDLE hShared; /* Shared memory segment used for locking */
29170 winceLock local; /* Locks obtained by this instance of winFile */
@@ -29671,10 +29747,46 @@
29671
29672 /*****************************************************************************
29673 ** The next group of routines implement the I/O methods specified
29674 ** by the sqlite3_io_methods object.
29675 ******************************************************************************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29676
29677 /*
29678 ** Close a file.
29679 **
29680 ** It is reported that an attempt to close a handle might sometimes
@@ -29714,17 +29826,10 @@
29714 OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
29715 OpenCounter(-1);
29716 return rc ? SQLITE_OK : SQLITE_IOERR;
29717 }
29718
29719 /*
29720 ** Some microsoft compilers lack this definition.
29721 */
29722 #ifndef INVALID_SET_FILE_POINTER
29723 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
29724 #endif
29725
29726 /*
29727 ** Read data from a file into a buffer. Return SQLITE_OK if all
29728 ** bytes were read successfully and SQLITE_IOERR if anything goes
29729 ** wrong.
29730 */
@@ -29732,112 +29837,108 @@
29732 sqlite3_file *id, /* File to read from */
29733 void *pBuf, /* Write content into this buffer */
29734 int amt, /* Number of bytes to read */
29735 sqlite3_int64 offset /* Begin reading at this offset */
29736 ){
29737 LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
29738 LONG lowerBits = (LONG)(offset & 0xffffffff);
29739 DWORD rc;
29740 winFile *pFile = (winFile*)id;
29741 DWORD error;
29742 DWORD got;
29743
29744 assert( id!=0 );
29745 SimulateIOError(return SQLITE_IOERR_READ);
29746 OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
29747 rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29748 if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29749 pFile->lastErrno = error;
29750 return SQLITE_FULL;
29751 }
29752 if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){
29753 pFile->lastErrno = GetLastError();
29754 return SQLITE_IOERR_READ;
29755 }
29756 if( got==(DWORD)amt ){
29757 return SQLITE_OK;
29758 }else{
29759 /* Unread parts of the buffer must be zero-filled */
29760 memset(&((char*)pBuf)[got], 0, amt-got);
29761 return SQLITE_IOERR_SHORT_READ;
29762 }
 
 
29763 }
29764
29765 /*
29766 ** Write data from a buffer into a file. Return SQLITE_OK on success
29767 ** or some other error code on failure.
29768 */
29769 static int winWrite(
29770 sqlite3_file *id, /* File to write into */
29771 const void *pBuf, /* The bytes to be written */
29772 int amt, /* Number of bytes to write */
29773 sqlite3_int64 offset /* Offset into the file to begin writing at */
29774 ){
29775 LONG upperBits = (LONG)((offset>>32) & 0x7fffffff);
29776 LONG lowerBits = (LONG)(offset & 0xffffffff);
29777 DWORD rc;
29778 winFile *pFile = (winFile*)id;
29779 DWORD error;
29780 DWORD wrote = 0;
29781
29782 assert( id!=0 );
29783 SimulateIOError(return SQLITE_IOERR_WRITE);
29784 SimulateDiskfullError(return SQLITE_FULL);
29785 OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
29786 rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29787 if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29788 pFile->lastErrno = error;
29789 if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29790 return SQLITE_FULL;
29791 }else{
29792 return SQLITE_IOERR_WRITE;
29793 }
29794 }
29795 assert( amt>0 );
29796 while(
29797 amt>0
29798 && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0
29799 && wrote>0
29800 ){
29801 amt -= wrote;
29802 pBuf = &((char*)pBuf)[wrote];
29803 }
29804 if( !rc || amt>(int)wrote ){
29805 pFile->lastErrno = GetLastError();
29806 if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29807 return SQLITE_FULL;
29808 }else{
29809 return SQLITE_IOERR_WRITE;
29810 }
29811 }
29812 return SQLITE_OK;
29813 }
29814
29815 /*
29816 ** Truncate an open file to a specified size
29817 */
29818 static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
29819 LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
29820 LONG lowerBits = (LONG)(nByte & 0xffffffff);
29821 DWORD dwRet;
29822 winFile *pFile = (winFile*)id;
29823 DWORD error;
29824 int rc = SQLITE_OK;
29825
29826 assert( id!=0 );
29827 OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
29828 SimulateIOError(return SQLITE_IOERR_TRUNCATE);
29829 dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29830 if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
29831 pFile->lastErrno = error;
 
 
 
 
 
 
 
 
 
29832 rc = SQLITE_IOERR_TRUNCATE;
29833 /* SetEndOfFile will fail if nByte is negative */
29834 }else if( !SetEndOfFile(pFile->h) ){
29835 pFile->lastErrno = GetLastError();
29836 rc = SQLITE_IOERR_TRUNCATE;
29837 }
29838 OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed"));
 
29839 return rc;
29840 }
29841
29842 #ifdef SQLITE_TEST
29843 /*
@@ -30197,10 +30298,14 @@
30197 return SQLITE_OK;
30198 }
30199 case SQLITE_LAST_ERRNO: {
30200 *(int*)pArg = (int)((winFile*)id)->lastErrno;
30201 return SQLITE_OK;
 
 
 
 
30202 }
30203 case SQLITE_FCNTL_SIZE_HINT: {
30204 sqlite3_int64 sz = *(sqlite3_int64*)pArg;
30205 SimulateIOErrorBenign(1);
30206 winTruncate(id, sz);
@@ -31749,11 +31854,11 @@
31749 ** start of a transaction, and is thus usually less than a few thousand,
31750 ** but can be as large as 2 billion for a really big database.
31751 */
31752
31753 /* Size of the Bitvec structure in bytes. */
31754 #define BITVEC_SZ (sizeof(void*)*128) /* 512 on 32bit. 1024 on 64bit */
31755
31756 /* Round the union size down to the nearest pointer boundary, since that's how
31757 ** it will be aligned within the Bitvec struct. */
31758 #define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
31759
@@ -32907,10 +33012,29 @@
32907 sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
32908 sqlite3_free(p);
32909 }
32910 }
32911
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32912 /*
32913 ** Allocate a new page object initially associated with cache pCache.
32914 */
32915 static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
32916 int nByte = sizeof(PgHdr1) + pCache->szPage;
@@ -33455,11 +33579,11 @@
33455 int nFree = 0;
33456 if( pcache1.pStart==0 ){
33457 PgHdr1 *p;
33458 pcache1EnterMutex();
33459 while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
33460 nFree += sqlite3MallocSize(PGHDR1_TO_PAGE(p));
33461 pcache1PinPage(p);
33462 pcache1RemoveFromHash(p);
33463 pcache1FreePage(p);
33464 }
33465 pcache1LeaveMutex();
@@ -33964,11 +34088,11 @@
33964 # define sqlite3WalOpen(x,y,z) 0
33965 # define sqlite3WalClose(w,x,y,z) 0
33966 # define sqlite3WalBeginReadTransaction(y,z) 0
33967 # define sqlite3WalEndReadTransaction(z)
33968 # define sqlite3WalRead(v,w,x,y,z) 0
33969 # define sqlite3WalDbsize(y,z)
33970 # define sqlite3WalBeginWriteTransaction(y) 0
33971 # define sqlite3WalEndWriteTransaction(x) 0
33972 # define sqlite3WalUndo(x,y,z) 0
33973 # define sqlite3WalSavepoint(y,z)
33974 # define sqlite3WalSavepointUndo(y,z) 0
@@ -34000,13 +34124,12 @@
34000 SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal);
34001
34002 /* Read a page from the write-ahead log, if it is present. */
34003 SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
34004
34005 /* Return the size of the database as it existed at the beginning
34006 ** of the snapshot */
34007 SQLITE_PRIVATE void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno);
34008
34009 /* Obtain or release the WRITER lock. */
34010 SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal);
34011 SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal);
34012
@@ -34048,12 +34171,16 @@
34048 #endif /* _WAL_H_ */
34049
34050 /************** End of wal.h *************************************************/
34051 /************** Continuing where we left off in pager.c **********************/
34052
34053 /*
34054 ******************** NOTES ON THE DESIGN OF THE PAGER ************************
 
 
 
 
34055 **
34056 ** Within this comment block, a page is deemed to have been synced
34057 ** automatically as soon as it is written when PRAGMA synchronous=OFF.
34058 ** Otherwise, the page is not synced until the xSync method of the VFS
34059 ** is called successfully on the file containing the page.
@@ -34083,11 +34210,11 @@
34083 ** both the content in the database when the rollback journal was written
34084 ** and the content in the database at the beginning of the current
34085 ** transaction.
34086 **
34087 ** (3) Writes to the database file are an integer multiple of the page size
34088 ** in length and are aligned to a page boundary.
34089 **
34090 ** (4) Reads from the database file are either aligned on a page boundary and
34091 ** an integer multiple of the page size in length or are taken from the
34092 ** first 100 bytes of the database file.
34093 **
@@ -34114,11 +34241,12 @@
34114 ** method is a no-op, but that does not change the fact the SQLite will
34115 ** invoke it.)
34116 **
34117 ** (9) Whenever the database file is modified, at least one bit in the range
34118 ** of bytes from 24 through 39 inclusive will be changed prior to releasing
34119 ** the EXCLUSIVE lock.
 
34120 **
34121 ** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less
34122 ** than one billion transactions.
34123 **
34124 ** (11) A database file is well-formed at the beginning and at the conclusion
@@ -34127,11 +34255,12 @@
34127 ** (12) An EXCLUSIVE lock is held on the database file when writing to
34128 ** the database file.
34129 **
34130 ** (13) A SHARED lock is held on the database file while reading any
34131 ** content out of the database file.
34132 */
 
34133
34134 /*
34135 ** Macros for troubleshooting. Normally turned off
34136 */
34137 #if 0
@@ -34152,62 +34281,283 @@
34152 */
34153 #define PAGERID(p) ((int)(p->fd))
34154 #define FILEHANDLEID(fd) ((int)fd)
34155
34156 /*
34157 ** The page cache as a whole is always in one of the following
34158 ** states:
34159 **
34160 ** PAGER_UNLOCK The page cache is not currently reading or
34161 ** writing the database file. There is no
34162 ** data held in memory. This is the initial
34163 ** state.
34164 **
34165 ** PAGER_SHARED The page cache is reading the database.
34166 ** Writing is not permitted. There can be
34167 ** multiple readers accessing the same database
34168 ** file at the same time.
34169 **
34170 ** PAGER_RESERVED This process has reserved the database for writing
34171 ** but has not yet made any changes. Only one process
34172 ** at a time can reserve the database. The original
34173 ** database file has not been modified so other
34174 ** processes may still be reading the on-disk
34175 ** database file.
34176 **
34177 ** PAGER_EXCLUSIVE The page cache is writing the database.
34178 ** Access is exclusive. No other processes or
34179 ** threads can be reading or writing while one
34180 ** process is writing.
34181 **
34182 ** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE
34183 ** after all dirty pages have been written to the
34184 ** database file and the file has been synced to
34185 ** disk. All that remains to do is to remove or
34186 ** truncate the journal file and the transaction
34187 ** will be committed.
34188 **
34189 ** The page cache comes up in PAGER_UNLOCK. The first time a
34190 ** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED.
34191 ** After all pages have been released using sqlite_page_unref(),
34192 ** the state transitions back to PAGER_UNLOCK. The first time
34193 ** that sqlite3PagerWrite() is called, the state transitions to
34194 ** PAGER_RESERVED. (Note that sqlite3PagerWrite() can only be
34195 ** called on an outstanding page which means that the pager must
34196 ** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
34197 ** PAGER_RESERVED means that there is an open rollback journal.
34198 ** The transition to PAGER_EXCLUSIVE occurs before any changes
34199 ** are made to the database file, though writes to the rollback
34200 ** journal occurs with just PAGER_RESERVED. After an sqlite3PagerRollback()
34201 ** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED,
34202 ** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode.
34203 */
34204 #define PAGER_UNLOCK 0
34205 #define PAGER_SHARED 1 /* same as SHARED_LOCK */
34206 #define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
34207 #define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
34208 #define PAGER_SYNCED 5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34209
34210 /*
34211 ** A macro used for invoking the codec if there is one
34212 */
34213 #ifdef SQLITE_HAS_CODEC
@@ -34253,37 +34603,32 @@
34253 u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
34254 #endif
34255 };
34256
34257 /*
34258 ** A open page cache is an instance of the following structure.
34259 **
34260 ** errCode
34261 **
34262 ** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
34263 ** or SQLITE_FULL. Once one of the first three errors occurs, it persists
34264 ** and is returned as the result of every major pager API call. The
34265 ** SQLITE_FULL return code is slightly different. It persists only until the
34266 ** next successful rollback is performed on the pager cache. Also,
34267 ** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup()
34268 ** APIs, they may still be used successfully.
34269 **
34270 ** dbSizeValid, dbSize, dbOrigSize, dbFileSize
34271 **
34272 ** Managing the size of the database file in pages is a little complicated.
34273 ** The variable Pager.dbSize contains the number of pages that the database
34274 ** image currently contains. As the database image grows or shrinks this
34275 ** variable is updated. The variable Pager.dbFileSize contains the number
34276 ** of pages in the database file. This may be different from Pager.dbSize
34277 ** if some pages have been appended to the database image but not yet written
34278 ** out from the cache to the actual file on disk. Or if the image has been
34279 ** truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable
34280 ** contains the number of pages in the database image when the current
34281 ** transaction was opened. The contents of all three of these variables is
34282 ** only guaranteed to be correct if the boolean Pager.dbSizeValid is true.
34283 **
34284 ** TODO: Under what conditions is dbSizeValid set? Cleared?
34285 **
34286 ** changeCountDone
34287 **
34288 ** This boolean variable is used to make sure that the change-counter
34289 ** (the 4-byte header field at byte offset 24 of the database file) is
@@ -34298,28 +34643,10 @@
34298 **
34299 ** This mechanism means that when running in exclusive mode, a connection
34300 ** need only update the change-counter once, for the first transaction
34301 ** committed.
34302 **
34303 ** dbModified
34304 **
34305 ** The dbModified flag is set whenever a database page is dirtied.
34306 ** It is cleared at the end of each transaction.
34307 **
34308 ** It is used when committing or otherwise ending a transaction. If
34309 ** the dbModified flag is clear then less work has to be done.
34310 **
34311 ** journalStarted
34312 **
34313 ** This flag is set during a write-transaction after the first
34314 ** journal-header is written and synced to disk.
34315 **
34316 ** After this has happened, new pages appended to the database
34317 ** do not need the PGHDR_NEED_SYNC flag set, as they do not need
34318 ** to wait for a journal sync before they can be written out to
34319 ** the database file (see function pager_write()).
34320 **
34321 ** setMaster
34322 **
34323 ** When PagerCommitPhaseOne() is called to commit a transaction, it may
34324 ** (or may not) specify a master-journal name to be written into the
34325 ** journal file before it is synced to disk.
@@ -34326,84 +34653,146 @@
34326 **
34327 ** Whether or not a journal file contains a master-journal pointer affects
34328 ** the way in which the journal file is finalized after the transaction is
34329 ** committed or rolled back when running in "journal_mode=PERSIST" mode.
34330 ** If a journal file does not contain a master-journal pointer, it is
34331 ** finalized by overwriting the first journal header with zeroes. If,
34332 ** on the other hand, it does contain a master-journal pointer, the
34333 ** journal file is finalized by truncating it to zero bytes, just as if
34334 ** the connection were running in "journal_mode=truncate" mode.
34335 **
34336 ** Journal files that contain master journal pointers cannot be finalized
34337 ** simply by overwriting the first journal-header with zeroes, as the
34338 ** master journal pointer could interfere with hot-journal rollback of any
34339 ** subsequently interrupted transaction that reuses the journal file.
34340 **
34341 ** The flag is cleared as soon as the journal file is finalized (either
34342 ** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the
34343 ** journal file from being successfully finalized, the setMaster flag
34344 ** is cleared anyway.
34345 **
34346 ** doNotSpill, doNotSyncSpill
34347 **
34348 ** When enabled, cache spills are prohibited. The doNotSpill variable
34349 ** inhibits all cache spill and doNotSyncSpill inhibits those spills that
34350 ** would require a journal sync. The doNotSyncSpill is set and cleared
34351 ** by sqlite3PagerWrite() in order to prevent a journal sync from happening
34352 ** in between the journalling of two pages on the same sector. The
34353 ** doNotSpill value set to prevent pagerStress() from trying to use
34354 ** the journal during a rollback.
34355 **
34356 ** needSync
34357 **
34358 ** TODO: It might be easier to set this variable in writeJournalHdr()
34359 ** and writeMasterJournal() only. Change its meaning to "unsynced data
34360 ** has been written to the journal".
 
 
34361 **
34362 ** subjInMemory
34363 **
34364 ** This is a boolean variable. If true, then any required sub-journal
34365 ** is opened as an in-memory journal file. If false, then in-memory
34366 ** sub-journals are only used for in-memory pager files.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34367 */
34368 struct Pager {
34369 sqlite3_vfs *pVfs; /* OS functions to use for IO */
34370 u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
34371 u8 journalMode; /* On of the PAGER_JOURNALMODE_* values */
34372 u8 useJournal; /* Use a rollback journal on this file */
34373 u8 noReadlock; /* Do not bother to obtain readlocks */
34374 u8 noSync; /* Do not sync the journal if true */
34375 u8 fullSync; /* Do extra syncs of the journal for robustness */
34376 u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
34377 u8 tempFile; /* zFilename is a temporary file */
34378 u8 readOnly; /* True for a read-only database */
34379 u8 memDb; /* True to inhibit all file I/O */
34380
34381 /* The following block contains those class members that are dynamically
34382 ** modified during normal operations. The other variables in this structure
34383 ** are either constant throughout the lifetime of the pager, or else
34384 ** used to store configuration parameters that affect the way the pager
34385 ** operates.
34386 **
34387 ** The 'state' variable is described in more detail along with the
34388 ** descriptions of the values it may take - PAGER_UNLOCK etc. Many of the
34389 ** other variables in this block are described in the comment directly
34390 ** above this class definition.
34391 */
34392 u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
34393 u8 dbModified; /* True if there are any changes to the Db */
34394 u8 needSync; /* True if an fsync() is needed on the journal */
34395 u8 journalStarted; /* True if header of journal is synced */
34396 u8 changeCountDone; /* Set after incrementing the change-counter */
34397 u8 setMaster; /* True if a m-j name has been written to jrnl */
34398 u8 doNotSpill; /* Do not spill the cache when non-zero */
34399 u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
34400 u8 dbSizeValid; /* Set when dbSize is correct */
34401 u8 subjInMemory; /* True to use in-memory sub-journals */
34402 Pgno dbSize; /* Number of pages in the database */
34403 Pgno dbOrigSize; /* dbSize before the current transaction */
34404 Pgno dbFileSize; /* Number of pages in the database file */
 
34405 int errCode; /* One of several kinds of errors */
34406 int nRec; /* Pages journalled since last j-header written */
34407 u32 cksumInit; /* Quasi-random value added to every checksum */
34408 u32 nSubRec; /* Number of records written to sub-journal */
34409 Bitvec *pInJournal; /* One bit for each page in the database file */
@@ -34410,21 +34799,25 @@
34410 sqlite3_file *fd; /* File descriptor for database */
34411 sqlite3_file *jfd; /* File descriptor for main journal */
34412 sqlite3_file *sjfd; /* File descriptor for sub-journal */
34413 i64 journalOff; /* Current write offset in the journal file */
34414 i64 journalHdr; /* Byte offset to previous journal header */
34415 i64 journalSizeLimit; /* Size limit for persistent journal files */
34416 PagerSavepoint *aSavepoint; /* Array of active savepoints */
34417 int nSavepoint; /* Number of elements in aSavepoint[] */
34418 char dbFileVers[16]; /* Changes whenever database file changes */
34419 u32 sectorSize; /* Assumed sector size during rollback */
 
 
34420
34421 u16 nExtra; /* Add this many bytes to each in-memory page */
34422 i16 nReserve; /* Number of unused bytes at end of each page */
34423 u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
 
34424 int pageSize; /* Number of bytes in a page */
34425 Pgno mxPgno; /* Maximum allowed size of the database */
 
34426 char *zFilename; /* Name of the database file */
34427 char *zJournal; /* Name of the journal file */
34428 int (*xBusyHandler)(void*); /* Function to call when busy */
34429 void *pBusyHandlerArg; /* Context argument for xBusyHandler */
34430 #ifdef SQLITE_TEST
@@ -34438,11 +34831,10 @@
34438 void (*xCodecFree)(void*); /* Destructor for the codec */
34439 void *pCodec; /* First argument to xCodec... methods */
34440 #endif
34441 char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
34442 PCache *pPCache; /* Pointer to page cache object */
34443 sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
34444 #ifndef SQLITE_OMIT_WAL
34445 Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
34446 char *zWal; /* File name for write-ahead log */
34447 #endif
34448 };
@@ -34517,26 +34909,225 @@
34517 /*
34518 ** The maximum legal page number is (2^31 - 1).
34519 */
34520 #define PAGER_MAX_PGNO 2147483647
34521
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34522 #ifndef NDEBUG
34523 /*
34524 ** Usage:
34525 **
34526 ** assert( assert_pager_state(pPager) );
 
 
 
34527 */
34528 static int assert_pager_state(Pager *pPager){
 
34529
34530 /* A temp-file is always in PAGER_EXCLUSIVE or PAGER_SYNCED state. */
34531 assert( pPager->tempFile==0 || pPager->state>=PAGER_EXCLUSIVE );
 
 
 
 
 
 
 
34532
34533 /* The changeCountDone flag is always set for temp-files */
34534 assert( pPager->tempFile==0 || pPager->changeCountDone );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34535
34536 return 1;
34537 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34538 #endif
34539
34540 /*
34541 ** Return true if it is necessary to write page *pPg into the sub-journal.
34542 ** A page needs to be written into the sub-journal if there exists one
@@ -34584,10 +35175,11 @@
34584
34585 /*
34586 ** Write a 32-bit integer into a string buffer in big-endian byte order.
34587 */
34588 #define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
 
34589
34590 /*
34591 ** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
34592 ** on success or an error code is something goes wrong.
34593 */
@@ -34596,31 +35188,57 @@
34596 put32bits(ac, val);
34597 return sqlite3OsWrite(fd, ac, 4, offset);
34598 }
34599
34600 /*
34601 ** The argument to this macro is a file descriptor (type sqlite3_file*).
34602 ** Return 0 if it is not open, or non-zero (but not 1) if it is.
34603 **
34604 ** This is so that expressions can be written as:
34605 **
34606 ** if( isOpen(pPager->jfd) ){ ...
34607 **
34608 ** instead of
34609 **
34610 ** if( pPager->jfd->pMethods ){ ...
34611 */
34612 #define isOpen(pFd) ((pFd)->pMethods)
 
 
 
 
 
 
 
 
 
 
 
 
34613
34614 /*
34615 ** If file pFd is open, call sqlite3OsUnlock() on it.
 
 
 
 
 
 
 
34616 */
34617 static int osUnlock(sqlite3_file *pFd, int eLock){
34618 if( !isOpen(pFd) ){
34619 return SQLITE_OK;
 
 
 
 
 
 
 
34620 }
34621 return sqlite3OsUnlock(pFd, eLock);
34622 }
34623
34624 /*
34625 ** This function determines whether or not the atomic-write optimization
34626 ** can be used with this pager. The optimization can be used if:
@@ -34692,17 +35310,18 @@
34692 ** that the page is either dirty or still matches the calculated page-hash.
34693 */
34694 #define CHECK_PAGE(x) checkPage(x)
34695 static void checkPage(PgHdr *pPg){
34696 Pager *pPager = pPg->pPager;
34697 assert( !pPg->pageHash || pPager->errCode
34698 || (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
34699 }
34700
34701 #else
34702 #define pager_datahash(X,Y) 0
34703 #define pager_pagehash(X) 0
 
34704 #define CHECK_PAGE(x)
34705 #endif /* SQLITE_CHECK_PAGES */
34706
34707 /*
34708 ** When this is called the journal file for pager pPager must be open.
@@ -34865,11 +35484,11 @@
34865 ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
34866 */
34867 static int writeJournalHdr(Pager *pPager){
34868 int rc = SQLITE_OK; /* Return code */
34869 char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */
34870 u32 nHeader = pPager->pageSize; /* Size of buffer pointed to by zHeader */
34871 u32 nWrite; /* Bytes of header sector written */
34872 int ii; /* Loop counter */
34873
34874 assert( isOpen(pPager->jfd) ); /* Journal file must be open. */
34875
@@ -34908,11 +35527,11 @@
34908 **
34909 ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
34910 ** that garbage data is never appended to the journal file.
34911 */
34912 assert( isOpen(pPager->fd) || pPager->noSync );
34913 if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
34914 || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
34915 ){
34916 memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
34917 put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
34918 }else{
@@ -35032,18 +35651,25 @@
35032 }
35033
35034 if( pPager->journalOff==0 ){
35035 u32 iPageSize; /* Page-size field of journal header */
35036 u32 iSectorSize; /* Sector-size field of journal header */
35037 u16 iPageSize16; /* Copy of iPageSize in 16-bit variable */
35038
35039 /* Read the page-size and sector-size journal header fields. */
35040 if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize))
35041 || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize))
35042 ){
35043 return rc;
35044 }
 
 
 
 
 
 
 
 
35045
35046 /* Check that the values read from the page-size and sector-size fields
35047 ** are within range. To be 'in range', both values need to be a power
35048 ** of two greater than or equal to 512 or 32, and not greater than their
35049 ** respective compile time maximum limits.
@@ -35062,14 +35688,12 @@
35062
35063 /* Update the page-size to match the value read from the journal.
35064 ** Use a testcase() macro to make sure that malloc failure within
35065 ** PagerSetPagesize() is tested.
35066 */
35067 iPageSize16 = (u16)iPageSize;
35068 rc = sqlite3PagerSetPagesize(pPager, &iPageSize16, -1);
35069 testcase( rc!=SQLITE_OK );
35070 assert( rc!=SQLITE_OK || iPageSize16==(u16)iPageSize );
35071
35072 /* Update the assumed sector-size to match the value used by
35073 ** the process that created this journal. If this journal was
35074 ** created by a process other than this one, then this routine
35075 ** is being called from within pager_playback(). The local value
@@ -35108,10 +35732,12 @@
35108 i64 iHdrOff; /* Offset of header in journal file */
35109 i64 jrnlSize; /* Size of journal file on disk */
35110 u32 cksum = 0; /* Checksum of string zMaster */
35111
35112 assert( pPager->setMaster==0 );
 
 
35113 if( !zMaster
35114 || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
35115 || pPager->journalMode==PAGER_JOURNALMODE_OFF
35116 ){
35117 return SQLITE_OK;
@@ -35144,11 +35770,10 @@
35144 || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
35145 ){
35146 return rc;
35147 }
35148 pPager->journalOff += (nMaster+20);
35149 pPager->needSync = !pPager->noSync;
35150
35151 /* If the pager is in peristent-journal mode, then the physical
35152 ** journal-file may extend past the end of the master-journal name
35153 ** and 8 bytes of magic data just written to the file. This is
35154 ** dangerous because the code to rollback a hot-journal file
@@ -35180,21 +35805,15 @@
35180 (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
35181 return p;
35182 }
35183
35184 /*
35185 ** Unless the pager is in error-state, discard all in-memory pages. If
35186 ** the pager is in error-state, then this call is a no-op.
35187 **
35188 ** TODO: Why can we not reset the pager while in error state?
35189 */
35190 static void pager_reset(Pager *pPager){
35191 if( SQLITE_OK==pPager->errCode ){
35192 sqlite3BackupRestart(pPager->pBackup);
35193 sqlite3PcacheClear(pPager->pPCache);
35194 pPager->dbSizeValid = 0;
35195 }
35196 }
35197
35198 /*
35199 ** Free all structures in the Pager.aSavepoint[] array and set both
35200 ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
@@ -35233,38 +35852,43 @@
35233 }
35234 return rc;
35235 }
35236
35237 /*
35238 ** Return true if this pager uses a write-ahead log instead of the usual
35239 ** rollback journal. Otherwise false.
35240 */
35241 #ifndef SQLITE_OMIT_WAL
35242 static int pagerUseWal(Pager *pPager){
35243 return (pPager->pWal!=0);
35244 }
35245 #else
35246 # define pagerUseWal(x) 0
35247 # define pagerRollbackWal(x) 0
35248 # define pagerWalFrames(v,w,x,y,z) 0
35249 # define pagerOpenWalIfPresent(z) SQLITE_OK
35250 # define pagerBeginReadTransaction(z) SQLITE_OK
35251 #endif
35252
35253 /*
35254 ** Unlock the database file. This function is a no-op if the pager
35255 ** is in exclusive mode.
35256 **
35257 ** If the pager is currently in error state, discard the contents of
35258 ** the cache and reset the Pager structure internal state. If there is
35259 ** an open journal-file, then the next time a shared-lock is obtained
35260 ** on the pager file (by this or any other process), it will be
35261 ** treated as a hot-journal and rolled back.
 
 
 
 
 
 
35262 */
35263 static void pager_unlock(Pager *pPager){
35264 if( !pPager->exclusiveMode ){
35265 int rc = SQLITE_OK; /* Return code */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35266 int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
35267
35268 /* If the operating system support deletion of open files, then
35269 ** close the journal file when dropping the database lock. Otherwise
35270 ** another connection with journal_mode=delete might delete the file
@@ -35280,62 +35904,60 @@
35280 || 1!=(pPager->journalMode & 5)
35281 ){
35282 sqlite3OsClose(pPager->jfd);
35283 }
35284
35285 sqlite3BitvecDestroy(pPager->pInJournal);
35286 pPager->pInJournal = 0;
35287 releaseAllSavepoints(pPager);
35288
35289 /* If the file is unlocked, somebody else might change it. The
35290 ** values stored in Pager.dbSize etc. might become invalid if
35291 ** this happens. One can argue that this doesn't need to be cleared
35292 ** until the change-counter check fails in PagerSharedLock().
35293 ** Clearing the page size cache here is being conservative.
35294 */
35295 pPager->dbSizeValid = 0;
35296
35297 if( pagerUseWal(pPager) ){
35298 sqlite3WalEndReadTransaction(pPager->pWal);
35299 }else{
35300 rc = osUnlock(pPager->fd, NO_LOCK);
35301 }
35302 if( rc ){
35303 pPager->errCode = rc;
35304 }
35305 IOTRACE(("UNLOCK %p\n", pPager))
35306
35307 /* If Pager.errCode is set, the contents of the pager cache cannot be
35308 ** trusted. Now that the pager file is unlocked, the contents of the
35309 ** cache can be discarded and the error code safely cleared.
35310 */
35311 if( pPager->errCode ){
35312 if( rc==SQLITE_OK ){
35313 pPager->errCode = SQLITE_OK;
35314 }
35315 pager_reset(pPager);
35316 }
35317
35318 pPager->changeCountDone = 0;
35319 pPager->state = PAGER_UNLOCK;
35320 pPager->dbModified = 0;
35321 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35322 }
35323
35324 /*
35325 ** This function should be called when an IOERR, CORRUPT or FULL error
35326 ** may have occurred. The first argument is a pointer to the pager
35327 ** structure, the second the error-code about to be returned by a pager
35328 ** API function. The value returned is a copy of the second argument
35329 ** to this function.
35330 **
35331 ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT, or SQLITE_FULL
35332 ** the error becomes persistent. Until the persistent error is cleared,
35333 ** subsequent API calls on this Pager will immediately return the same
35334 ** error code.
35335 **
35336 ** A persistent error indicates that the contents of the pager-cache
35337 ** cannot be trusted. This state can be cleared by completely discarding
35338 ** the contents of the pager-cache. If a transaction was active when
35339 ** the persistent error occurred, then the rollback journal may need
35340 ** to be replayed to restore the contents of the database file (as if
35341 ** it were a hot-journal).
@@ -35348,49 +35970,25 @@
35348 pPager->errCode==SQLITE_OK ||
35349 (pPager->errCode & 0xff)==SQLITE_IOERR
35350 );
35351 if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
35352 pPager->errCode = rc;
 
35353 }
35354 return rc;
35355 }
35356
35357 /*
35358 ** Execute a rollback if a transaction is active and unlock the
35359 ** database file.
35360 **
35361 ** If the pager has already entered the error state, do not attempt
35362 ** the rollback at this time. Instead, pager_unlock() is called. The
35363 ** call to pager_unlock() will discard all in-memory pages, unlock
35364 ** the database file and clear the error state. If this means that
35365 ** there is a hot-journal left in the file-system, the next connection
35366 ** to obtain a shared lock on the pager (which may be this one) will
35367 ** roll it back.
35368 **
35369 ** If the pager has not already entered the error state, but an IO or
35370 ** malloc error occurs during a rollback, then this will itself cause
35371 ** the pager to enter the error state. Which will be cleared by the
35372 ** call to pager_unlock(), as described above.
35373 */
35374 static void pagerUnlockAndRollback(Pager *pPager){
35375 if( pPager->errCode==SQLITE_OK && pPager->state>=PAGER_RESERVED ){
35376 sqlite3BeginBenignMalloc();
35377 sqlite3PagerRollback(pPager);
35378 sqlite3EndBenignMalloc();
35379 }
35380 pager_unlock(pPager);
35381 }
35382
35383 /*
35384 ** This routine ends a transaction. A transaction is usually ended by
35385 ** either a COMMIT or a ROLLBACK operation. This routine may be called
35386 ** after rollback of a hot-journal, or if an error occurs while opening
35387 ** the journal file or writing the very first journal-header of a
35388 ** database transaction.
35389 **
35390 ** If the pager is in PAGER_SHARED or PAGER_UNLOCK state when this
35391 ** routine is called, it is a no-op (returns SQLITE_OK).
 
35392 **
35393 ** Otherwise, any active savepoints are released.
35394 **
35395 ** If the journal file is open, then it is "finalized". Once a journal
35396 ** file has been finalized it is not possible to use it to roll back a
@@ -35417,17 +36015,13 @@
35417 ** If the pager is running in exclusive mode, this method of finalizing
35418 ** the journal file is never used. Instead, if the journalMode is
35419 ** DELETE and the pager is in exclusive mode, the method described under
35420 ** journalMode==PERSIST is used instead.
35421 **
35422 ** After the journal is finalized, if running in non-exclusive mode, the
35423 ** pager moves to PAGER_SHARED state (and downgrades the lock on the
35424 ** database file accordingly).
35425 **
35426 ** If the pager is running in exclusive mode and is in PAGER_SYNCED state,
35427 ** it moves to PAGER_EXCLUSIVE. No locks are downgraded when running in
35428 ** exclusive mode.
35429 **
35430 ** SQLITE_OK is returned if no error occurs. If an error occurs during
35431 ** any of the IO operations to finalize the journal file or unlock the
35432 ** database then the IO error code is returned to the user. If the
35433 ** operation to finalize the journal file fails, then the code still
@@ -35438,15 +36032,30 @@
35438 */
35439 static int pager_end_transaction(Pager *pPager, int hasMaster){
35440 int rc = SQLITE_OK; /* Error code from journal finalization operation */
35441 int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
35442
35443 if( pPager->state<PAGER_RESERVED ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35444 return SQLITE_OK;
35445 }
 
35446 releaseAllSavepoints(pPager);
35447
35448 assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
35449 if( isOpen(pPager->jfd) ){
35450 assert( !pagerUseWal(pPager) );
35451
35452 /* Finalize the journal file. */
@@ -35458,18 +36067,15 @@
35458 rc = SQLITE_OK;
35459 }else{
35460 rc = sqlite3OsTruncate(pPager->jfd, 0);
35461 }
35462 pPager->journalOff = 0;
35463 pPager->journalStarted = 0;
35464 }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
35465 || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
35466 ){
35467 rc = zeroJournalHdr(pPager, hasMaster);
35468 pager_error(pPager, rc);
35469 pPager->journalOff = 0;
35470 pPager->journalStarted = 0;
35471 }else{
35472 /* This branch may be executed with Pager.journalMode==MEMORY if
35473 ** a hot-journal was just rolled back. In this case the journal
35474 ** file should be closed and deleted. If this connection writes to
35475 ** the database file, it will do so using an in-memory journal.
@@ -35481,52 +36087,80 @@
35481 sqlite3OsClose(pPager->jfd);
35482 if( !pPager->tempFile ){
35483 rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
35484 }
35485 }
 
35486
35487 #ifdef SQLITE_CHECK_PAGES
35488 sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
 
 
 
 
 
 
 
35489 #endif
35490 }
35491 sqlite3BitvecDestroy(pPager->pInJournal);
35492 pPager->pInJournal = 0;
35493 pPager->nRec = 0;
35494 sqlite3PcacheCleanAll(pPager->pPCache);
 
35495
35496 if( pagerUseWal(pPager) ){
 
 
 
 
35497 rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
35498 assert( rc2==SQLITE_OK );
35499 pPager->state = PAGER_SHARED;
35500
35501 /* If the connection was in locking_mode=exclusive mode but is no longer,
35502 ** drop the EXCLUSIVE lock held on the database file.
35503 */
35504 if( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){
35505 rc2 = osUnlock(pPager->fd, SHARED_LOCK);
35506 }
35507 }else if( !pPager->exclusiveMode ){
35508 rc2 = osUnlock(pPager->fd, SHARED_LOCK);
35509 pPager->state = PAGER_SHARED;
35510 pPager->changeCountDone = 0;
35511 }else if( pPager->state==PAGER_SYNCED ){
35512 pPager->state = PAGER_EXCLUSIVE;
35513 }
35514 pPager->setMaster = 0;
35515 pPager->needSync = 0;
35516 pPager->dbModified = 0;
35517
35518 /* TODO: Is this optimal? Why is the db size invalidated here
35519 ** when the database file is not unlocked? */
35520 pPager->dbOrigSize = 0;
35521 sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
35522 if( !MEMDB ){
35523 pPager->dbSizeValid = 0;
35524 }
35525
35526 return (rc==SQLITE_OK?rc2:rc);
35527 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35528
35529 /*
35530 ** Parameter aData must point to a buffer of pPager->pageSize bytes
35531 ** of data. Compute and return a checksum based ont the contents of the
35532 ** page of data and the current value of pPager->cksumInit.
@@ -35574,13 +36208,12 @@
35574 ** Read a single page from either the journal file (if isMainJrnl==1) or
35575 ** from the sub-journal (if isMainJrnl==0) and playback that page.
35576 ** The page begins at offset *pOffset into the file. The *pOffset
35577 ** value is increased to the start of the next page in the journal.
35578 **
35579 ** The isMainJrnl flag is true if this is the main rollback journal and
35580 ** false for the statement journal. The main rollback journal uses
35581 ** checksums - the statement journal does not.
35582 **
35583 ** If the page number of the page record read from the (sub-)journal file
35584 ** is greater than the current value of Pager.dbSize, then playback is
35585 ** skipped and SQLITE_OK is returned.
35586 **
@@ -35629,10 +36262,21 @@
35629 assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */
35630
35631 aData = pPager->pTmpSpace;
35632 assert( aData ); /* Temp storage must have already been allocated */
35633 assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) );
 
 
 
 
 
 
 
 
 
 
 
35634
35635 /* Read the page number and page data from the journal or sub-journal
35636 ** file. Return an error code to the caller if an IO error occurs.
35637 */
35638 jfd = isMainJrnl ? pPager->jfd : pPager->sjfd;
@@ -35666,20 +36310,19 @@
35666 ** rollback, then don't bother to play it back again.
35667 */
35668 if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
35669 return rc;
35670 }
35671 assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
35672
35673 /* When playing back page 1, restore the nReserve setting
35674 */
35675 if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){
35676 pPager->nReserve = ((u8*)aData)[20];
35677 pagerReportSize(pPager);
35678 }
35679
35680 /* If the pager is in RESERVED state, then there must be a copy of this
35681 ** page in the pager cache. In this case just update the pager cache,
35682 ** not the database file. The page is left marked dirty in this case.
35683 **
35684 ** An exception to the above rule: If the database is in no-sync mode
35685 ** and a page is moved during an incremental vacuum then the page may
@@ -35686,12 +36329,15 @@
35686 ** not be in the pager cache. Later: if a malloc() or IO error occurs
35687 ** during a Movepage() call, then the page may not be in the cache
35688 ** either. So the condition described in the above paragraph is not
35689 ** assert()able.
35690 **
35691 ** If in EXCLUSIVE state, then we update the pager cache if it exists
35692 ** and the main file. The page is then marked not dirty.
 
 
 
35693 **
35694 ** Ticket #1171: The statement journal might contain page content that is
35695 ** different from the page content at the start of the transaction.
35696 ** This occurs when a page is changed prior to the start of a statement
35697 ** then changed again within the statement. When rolling back such a
@@ -35713,21 +36359,22 @@
35713 pPg = 0;
35714 }else{
35715 pPg = pager_lookup(pPager, pgno);
35716 }
35717 assert( pPg || !MEMDB );
 
35718 PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
35719 PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
35720 (isMainJrnl?"main-journal":"sub-journal")
35721 ));
35722 if( isMainJrnl ){
35723 isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr);
35724 }else{
35725 isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
35726 }
35727 if( (pPager->state>=PAGER_EXCLUSIVE)
35728 && isOpen(pPager->fd)
35729 && isSynced
35730 ){
35731 i64 ofst = (pgno-1)*(i64)pPager->pageSize;
35732 testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
35733 assert( !pagerUseWal(pPager) );
@@ -35799,13 +36446,12 @@
35799 ** database corruption may ensue.
35800 */
35801 assert( !pagerUseWal(pPager) );
35802 sqlite3PcacheMakeClean(pPg);
35803 }
35804 #ifdef SQLITE_CHECK_PAGES
35805 pPg->pageHash = pager_pagehash(pPg);
35806 #endif
35807 /* If this was page 1, then restore the value of Pager.dbFileVers.
35808 ** Do this before any decoding. */
35809 if( pgno==1 ){
35810 memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
35811 }
@@ -35953,14 +36599,14 @@
35953 /*
35954 ** This function is used to change the actual size of the database
35955 ** file in the file-system. This only happens when committing a transaction,
35956 ** or rolling back a transaction (including rolling back a hot-journal).
35957 **
35958 ** If the main database file is not open, or an exclusive lock is not
35959 ** held, this function is a no-op. Otherwise, the size of the file is
35960 ** changed to nPage pages (nPage*pPager->pageSize bytes). If the file
35961 ** on disk is currently larger than nPage pages, then use the VFS
35962 ** xTruncate() method to truncate it.
35963 **
35964 ** Or, it might might be the case that the file on disk is smaller than
35965 ** nPage pages. Some operating system implementations can get confused if
35966 ** you try to truncate a file to some size that is larger than it
@@ -35970,12 +36616,18 @@
35970 ** If successful, return SQLITE_OK. If an IO error occurs while modifying
35971 ** the database file, return the error code to the caller.
35972 */
35973 static int pager_truncate(Pager *pPager, Pgno nPage){
35974 int rc = SQLITE_OK;
35975 if( pPager->state>=PAGER_EXCLUSIVE && isOpen(pPager->fd) ){
 
 
 
 
 
35976 i64 currentSize, newSize;
 
35977 /* TODO: Is it safe to use Pager.dbFileSize here? */
35978 rc = sqlite3OsFileSize(pPager->fd, &currentSize);
35979 newSize = pPager->pageSize*(i64)nPage;
35980 if( rc==SQLITE_OK && currentSize!=newSize ){
35981 if( currentSize>newSize ){
@@ -36095,11 +36747,11 @@
36095 /* Figure out how many records are in the journal. Abort early if
36096 ** the journal is empty.
36097 */
36098 assert( isOpen(pPager->jfd) );
36099 rc = sqlite3OsFileSize(pPager->jfd, &szJ);
36100 if( rc!=SQLITE_OK || szJ==0 ){
36101 goto end_playback;
36102 }
36103
36104 /* Read the master journal name from the journal, if it is present.
36105 ** If a master journal file name is specified, but the file is not
@@ -36129,11 +36781,11 @@
36129 ** occurs.
36130 */
36131 while( 1 ){
36132 /* Read the next journal header from the journal file. If there are
36133 ** not enough bytes left in the journal file for a complete header, or
36134 ** it is corrupted, then a process must of failed while writing it.
36135 ** This indicates nothing more needs to be rolled back.
36136 */
36137 rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
36138 if( rc!=SQLITE_OK ){
36139 if( rc==SQLITE_DONE ){
@@ -36243,14 +36895,13 @@
36243 if( rc==SQLITE_OK ){
36244 zMaster = pPager->pTmpSpace;
36245 rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
36246 testcase( rc!=SQLITE_OK );
36247 }
36248 if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
36249 rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
36250 }
36251 if( rc==SQLITE_OK && pPager->noSync==0 && pPager->state>=PAGER_EXCLUSIVE ){
36252 rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
36253 }
36254 if( rc==SQLITE_OK ){
36255 rc = pager_end_transaction(pPager, zMaster[0]!='\0');
36256 testcase( rc!=SQLITE_OK );
@@ -36288,11 +36939,11 @@
36288 Pgno pgno = pPg->pgno; /* Page number to read */
36289 int rc = SQLITE_OK; /* Return code */
36290 int isInWal = 0; /* True if page is in log file */
36291 int pgsz = pPager->pageSize; /* Number of bytes to read */
36292
36293 assert( pPager->state>=PAGER_SHARED && !MEMDB );
36294 assert( isOpen(pPager->fd) );
36295
36296 if( NEVER(!isOpen(pPager->fd)) ){
36297 assert( pPager->tempFile );
36298 memset(pPg->pData, 0, pPager->pageSize);
@@ -36435,10 +37086,18 @@
36435 PgHdr *p;
36436 for(p=pList; p; p=p->pDirty){
36437 sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
36438 }
36439 }
 
 
 
 
 
 
 
 
36440 return rc;
36441 }
36442
36443 /*
36444 ** Begin a read transaction on the WAL.
@@ -36451,31 +37110,82 @@
36451 static int pagerBeginReadTransaction(Pager *pPager){
36452 int rc; /* Return code */
36453 int changed = 0; /* True if cache must be reset */
36454
36455 assert( pagerUseWal(pPager) );
 
36456
36457 /* sqlite3WalEndReadTransaction() was not called for the previous
36458 ** transaction in locking_mode=EXCLUSIVE. So call it now. If we
36459 ** are in locking_mode=NORMAL and EndRead() was previously called,
36460 ** the duplicate call is harmless.
36461 */
36462 sqlite3WalEndReadTransaction(pPager->pWal);
36463
36464 rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
36465 if( rc==SQLITE_OK ){
36466 int dummy;
36467 if( changed ){
36468 pager_reset(pPager);
36469 assert( pPager->errCode || pPager->dbSizeValid==0 );
36470 }
36471 rc = sqlite3PagerPagecount(pPager, &dummy);
36472 }
36473 pPager->state = PAGER_SHARED;
36474
36475 return rc;
36476 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36477
36478 /*
36479 ** Check if the *-wal file that corresponds to the database opened by pPager
36480 ** exists if the database is not empy, or verify that the *-wal file does
36481 ** not exist (by deleting it) if the database file is empty.
@@ -36485,25 +37195,26 @@
36485 ** if no error occurs, make sure Pager.journalMode is not set to
36486 ** PAGER_JOURNALMODE_WAL.
36487 **
36488 ** Return SQLITE_OK or an error code.
36489 **
36490 ** If the WAL file is opened, also open a snapshot (read transaction).
36491 **
36492 ** The caller must hold a SHARED lock on the database file to call this
36493 ** function. Because an EXCLUSIVE lock on the db file is required to delete
36494 ** a WAL on a none-empty database, this ensures there is no race condition
36495 ** between the xAccess() below and an xDelete() being executed by some
36496 ** other connection.
36497 */
36498 static int pagerOpenWalIfPresent(Pager *pPager){
36499 int rc = SQLITE_OK;
 
 
 
36500 if( !pPager->tempFile ){
36501 int isWal; /* True if WAL file exists */
36502 int nPage; /* Size of the database file */
36503 assert( pPager->state>=SHARED_LOCK );
36504 rc = sqlite3PagerPagecount(pPager, &nPage);
36505 if( rc ) return rc;
36506 if( nPage==0 ){
36507 rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
36508 isWal = 0;
36509 }else{
@@ -36511,15 +37222,12 @@
36511 pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal
36512 );
36513 }
36514 if( rc==SQLITE_OK ){
36515 if( isWal ){
36516 pager_reset(pPager);
36517 rc = sqlite3PagerOpenWal(pPager, 0);
36518 if( rc==SQLITE_OK ){
36519 rc = pagerBeginReadTransaction(pPager);
36520 }
36521 }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
36522 pPager->journalMode = PAGER_JOURNALMODE_DELETE;
36523 }
36524 }
36525 }
@@ -36567,11 +37275,12 @@
36567 i64 szJ; /* Effective size of the main journal */
36568 i64 iHdrOff; /* End of first segment of main-journal records */
36569 int rc = SQLITE_OK; /* Return code */
36570 Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */
36571
36572 assert( pPager->state>=PAGER_SHARED );
 
36573
36574 /* Allocate a bitvec to use to store the set of pages rolled back */
36575 if( pSavepoint ){
36576 pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
36577 if( !pDone ){
@@ -36706,11 +37415,10 @@
36706 #ifndef SQLITE_OMIT_PAGER_PRAGMAS
36707 SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
36708 pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
36709 pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
36710 pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
36711 if( pPager->noSync ) pPager->needSync = 0;
36712 }
36713 #endif
36714
36715 /*
36716 ** The following global variable is incremented whenever the library
@@ -36788,11 +37496,11 @@
36788 ** Change the page size used by the Pager object. The new page size
36789 ** is passed in *pPageSize.
36790 **
36791 ** If the pager is in the error state when this function is called, it
36792 ** is a no-op. The value returned is the error state error code (i.e.
36793 ** one of SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_FULL).
36794 **
36795 ** Otherwise, if all of the following are true:
36796 **
36797 ** * the new page size (value of *pPageSize) is valid (a power
36798 ** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and
@@ -36812,32 +37520,52 @@
36812 ** If the page size is not changed, either because one of the enumerated
36813 ** conditions above is not true, the pager was in error state when this
36814 ** function was called, or because the memory allocation attempt failed,
36815 ** then *pPageSize is set to the old, retained page size before returning.
36816 */
36817 SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){
36818 int rc = pPager->errCode;
36819
36820 if( rc==SQLITE_OK ){
36821 u16 pageSize = *pPageSize;
36822 assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
36823 if( (pPager->memDb==0 || pPager->dbSize==0)
36824 && sqlite3PcacheRefCount(pPager->pPCache)==0
36825 && pageSize && pageSize!=pPager->pageSize
36826 ){
36827 char *pNew = (char *)sqlite3PageMalloc(pageSize);
36828 if( !pNew ){
36829 rc = SQLITE_NOMEM;
36830 }else{
36831 pager_reset(pPager);
36832 pPager->pageSize = pageSize;
36833 sqlite3PageFree(pPager->pTmpSpace);
36834 pPager->pTmpSpace = pNew;
36835 sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
36836 }
36837 }
36838 *pPageSize = (u16)pPager->pageSize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36839 if( nReserve<0 ) nReserve = pPager->nReserve;
36840 assert( nReserve>=0 && nReserve<1000 );
36841 pPager->nReserve = (i16)nReserve;
36842 pagerReportSize(pPager);
36843 }
@@ -36862,17 +37590,15 @@
36862 ** maximum page count below the current size of the database.
36863 **
36864 ** Regardless of mxPage, return the current maximum page count.
36865 */
36866 SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
36867 int nPage;
36868 if( mxPage>0 ){
36869 pPager->mxPgno = mxPage;
36870 }
36871 if( pPager->state!=PAGER_UNLOCK ){
36872 sqlite3PagerPagecount(pPager, &nPage);
36873 assert( (int)pPager->mxPgno>=nPage );
36874 }
36875 return pPager->mxPgno;
36876 }
36877
36878 /*
@@ -36933,70 +37659,20 @@
36933 }
36934 return rc;
36935 }
36936
36937 /*
36938 ** Return the total number of pages in the database file associated
36939 ** with pPager. Normally, this is calculated as (<db file size>/<page-size>).
 
36940 ** However, if the file is between 1 and <page-size> bytes in size, then
36941 ** this is considered a 1 page file.
36942 **
36943 ** If the pager is in error state when this function is called, then the
36944 ** error state error code is returned and *pnPage left unchanged. Or,
36945 ** if the file system has to be queried for the size of the file and
36946 ** the query attempt returns an IO error, the IO error code is returned
36947 ** and *pnPage is left unchanged.
36948 **
36949 ** Otherwise, if everything is successful, then SQLITE_OK is returned
36950 ** and *pnPage is set to the number of pages in the database.
36951 */
36952 SQLITE_PRIVATE int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
36953 Pgno nPage = 0; /* Value to return via *pnPage */
36954
36955 /* Determine the number of pages in the file. Store this in nPage. */
36956 if( pPager->dbSizeValid ){
36957 nPage = pPager->dbSize;
36958 }else{
36959 int rc; /* Error returned by OsFileSize() */
36960 i64 n = 0; /* File size in bytes returned by OsFileSize() */
36961
36962 if( pagerUseWal(pPager) && pPager->state!=PAGER_UNLOCK ){
36963 sqlite3WalDbsize(pPager->pWal, &nPage);
36964 }
36965
36966 if( nPage==0 ){
36967 assert( isOpen(pPager->fd) || pPager->tempFile );
36968 if( isOpen(pPager->fd) ){
36969 if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){
36970 pager_error(pPager, rc);
36971 return rc;
36972 }
36973 }
36974 if( n>0 && n<pPager->pageSize ){
36975 nPage = 1;
36976 }else{
36977 nPage = (Pgno)(n / pPager->pageSize);
36978 }
36979 }
36980 if( pPager->state!=PAGER_UNLOCK ){
36981 pPager->dbSize = nPage;
36982 pPager->dbFileSize = nPage;
36983 pPager->dbSizeValid = 1;
36984 }
36985 }
36986
36987 /* If the current number of pages in the file is greater than the
36988 ** configured maximum pager number, increase the allowed limit so
36989 ** that the file can be read.
36990 */
36991 if( nPage>pPager->mxPgno ){
36992 pPager->mxPgno = (Pgno)nPage;
36993 }
36994
36995 /* Set the output variable and return SQLITE_OK */
36996 *pnPage = nPage;
36997 return SQLITE_OK;
36998 }
36999
37000
37001 /*
37002 ** Try to obtain a lock of type locktype on the database file. If
@@ -37013,42 +37689,23 @@
37013 ** variable to locktype before returning.
37014 */
37015 static int pager_wait_on_lock(Pager *pPager, int locktype){
37016 int rc; /* Return code */
37017
37018 /* The OS lock values must be the same as the Pager lock values */
37019 assert( PAGER_SHARED==SHARED_LOCK );
37020 assert( PAGER_RESERVED==RESERVED_LOCK );
37021 assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
37022
37023 /* If the file is currently unlocked then the size must be unknown. It
37024 ** must not have been modified at this point.
37025 */
37026 assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 );
37027 assert( pPager->state>=PAGER_SHARED || pPager->dbModified==0 );
37028
37029 /* Check that this is either a no-op (because the requested lock is
37030 ** already held, or one of the transistions that the busy-handler
37031 ** may be invoked during, according to the comment above
37032 ** sqlite3PagerSetBusyhandler().
37033 */
37034 assert( (pPager->state>=locktype)
37035 || (pPager->state==PAGER_UNLOCK && locktype==PAGER_SHARED)
37036 || (pPager->state==PAGER_RESERVED && locktype==PAGER_EXCLUSIVE)
37037 );
37038
37039 if( pPager->state>=locktype ){
37040 rc = SQLITE_OK;
37041 }else{
37042 do {
37043 rc = sqlite3OsLock(pPager->fd, locktype);
37044 }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
37045 if( rc==SQLITE_OK ){
37046 pPager->state = (u8)locktype;
37047 IOTRACE(("LOCK %p %d\n", pPager, locktype))
37048 }
37049 }
37050 return rc;
37051 }
37052
37053 /*
37054 ** Function assertTruncateConstraint(pPager) checks that one of the
@@ -37089,13 +37746,12 @@
37089 ** function does not actually modify the database file on disk. It
37090 ** just sets the internal state of the pager object so that the
37091 ** truncation will be done when the current transaction is committed.
37092 */
37093 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
37094 assert( pPager->dbSizeValid );
37095 assert( pPager->dbSize>=nPage );
37096 assert( pPager->state>=PAGER_RESERVED );
37097 pPager->dbSize = nPage;
37098 assertTruncateConstraint(pPager);
37099 }
37100
37101
@@ -37141,11 +37797,11 @@
37141 SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
37142 u8 *pTmp = (u8 *)pPager->pTmpSpace;
37143
37144 disable_simulated_io_errors();
37145 sqlite3BeginBenignMalloc();
37146 pPager->errCode = 0;
37147 pPager->exclusiveMode = 0;
37148 #ifndef SQLITE_OMIT_WAL
37149 sqlite3WalClose(pPager->pWal,
37150 (pPager->noSync ? 0 : pPager->sync_flags),
37151 pPager->pageSize, pTmp
@@ -37154,18 +37810,23 @@
37154 #endif
37155 pager_reset(pPager);
37156 if( MEMDB ){
37157 pager_unlock(pPager);
37158 }else{
37159 /* Set Pager.journalHdr to -1 for the benefit of the pager_playback()
37160 ** call which may be made from within pagerUnlockAndRollback(). If it
37161 ** is not -1, then the unsynced portion of an open journal file may
37162 ** be played back into the database. If a power failure occurs while
37163 ** this is happening, the database may become corrupt.
 
 
 
 
 
37164 */
37165 if( isOpen(pPager->jfd) ){
37166 pPager->errCode = pagerSyncHotJournal(pPager);
37167 }
37168 pagerUnlockAndRollback(pPager);
37169 }
37170 sqlite3EndBenignMalloc();
37171 enable_simulated_io_errors();
@@ -37206,13 +37867,13 @@
37206 /*
37207 ** Sync the journal. In other words, make sure all the pages that have
37208 ** been written to the journal have actually reached the surface of the
37209 ** disk and can be restored in the event of a hot-journal rollback.
37210 **
37211 ** If the Pager.needSync flag is not set, then this function is a
37212 ** no-op. Otherwise, the actions required depend on the journal-mode
37213 ** and the device characteristics of the the file-system, as follows:
37214 **
37215 ** * If the journal file is an in-memory journal file, no action need
37216 ** be taken.
37217 **
37218 ** * Otherwise, if the device does not support the SAFE_APPEND property,
@@ -37232,22 +37893,29 @@
37232 ** <update nRec field>
37233 ** }
37234 ** if( NOT SEQUENTIAL ) xSync(<journal file>);
37235 ** }
37236 **
37237 ** The Pager.needSync flag is never be set for temporary files, or any
37238 ** file operating in no-sync mode (Pager.noSync set to non-zero).
37239 **
37240 ** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
37241 ** page currently held in memory before returning SQLITE_OK. If an IO
37242 ** error is encountered, then the IO error code is returned to the caller.
37243 */
37244 static int syncJournal(Pager *pPager){
37245 if( pPager->needSync ){
 
 
 
 
 
 
 
 
 
 
 
37246 assert( !pPager->tempFile );
37247 if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
37248 int rc; /* Return code */
37249 const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
37250 assert( isOpen(pPager->jfd) );
37251
37252 if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
37253 /* This block deals with an obscure problem. If the last connection
@@ -37318,21 +37986,29 @@
37318 rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
37319 (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
37320 );
37321 if( rc!=SQLITE_OK ) return rc;
37322 }
37323 }
37324
37325 /* The journal file was just successfully synced. Set Pager.needSync
37326 ** to zero and clear the PGHDR_NEED_SYNC flag on all pagess.
37327 */
37328 pPager->needSync = 0;
37329 pPager->journalStarted = 1;
37330 pPager->journalHdr = pPager->journalOff;
37331 sqlite3PcacheClearSyncFlags(pPager->pPCache);
 
37332 }
37333
 
 
 
 
 
 
 
37334 return SQLITE_OK;
37335 }
37336
37337 /*
37338 ** The argument is the first in a linked list of dirty pages connected
@@ -37365,31 +38041,16 @@
37365 ** If everything is successful, SQLITE_OK is returned. If an IO error
37366 ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
37367 ** be obtained, SQLITE_BUSY is returned.
37368 */
37369 static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
37370 int rc; /* Return code */
37371
37372 /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
37373 ** database file. If there is already an EXCLUSIVE lock, the following
37374 ** call is a no-op.
37375 **
37376 ** Moving the lock from RESERVED to EXCLUSIVE actually involves going
37377 ** through an intermediate state PENDING. A PENDING lock prevents new
37378 ** readers from attaching to the database but is unsufficient for us to
37379 ** write. The idea of a PENDING lock is to prevent new readers from
37380 ** coming in while we wait for existing readers to clear.
37381 **
37382 ** While the pager is in the RESERVED state, the original database file
37383 ** is unchanged and we can rollback without having to playback the
37384 ** journal into the original database file. Once we transition to
37385 ** EXCLUSIVE, it means the database file has been changed and any rollback
37386 ** will require a journal playback.
37387 */
37388 assert( !pagerUseWal(pPager) );
37389 assert( pPager->state>=PAGER_RESERVED );
37390 rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
37391
37392 /* If the file is a temp-file has not yet been opened, open it now. It
37393 ** is not possible for rc to be other than SQLITE_OK if this branch
37394 ** is taken, as pager_wait_on_lock() is a no-op for temp-files.
37395 */
@@ -37400,13 +38061,14 @@
37400
37401 /* Before the first write, give the VFS a hint of what the final
37402 ** file size will be.
37403 */
37404 assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
37405 if( rc==SQLITE_OK && pPager->dbSize>(pPager->dbOrigSize+1) ){
37406 sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
37407 sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
 
37408 }
37409
37410 while( rc==SQLITE_OK && pList ){
37411 Pgno pgno = pList->pgno;
37412
@@ -37419,10 +38081,12 @@
37419 ** set (set by sqlite3PagerDontWrite()).
37420 */
37421 if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
37422 i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */
37423 char *pData; /* Data to write */
 
 
37424
37425 /* Encode the database */
37426 CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
37427
37428 /* Write out the page data. */
@@ -37448,13 +38112,11 @@
37448 PAGER_INCR(sqlite3_pager_writedb_count);
37449 PAGER_INCR(pPager->nWrite);
37450 }else{
37451 PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
37452 }
37453 #ifdef SQLITE_CHECK_PAGES
37454 pList->pageHash = pager_pagehash(pList);
37455 #endif
37456 pList = pList->pDirty;
37457 }
37458
37459 return rc;
37460 }
@@ -37562,13 +38224,18 @@
37562 ** pages belonging to the same sector.
37563 **
37564 ** The doNotSpill flag inhibits all cache spilling regardless of whether
37565 ** or not a sync is required. This is set during a rollback.
37566 **
37567 ** Spilling is also inhibited when in an error state.
 
 
 
 
 
37568 */
37569 if( pPager->errCode ) return SQLITE_OK;
37570 if( pPager->doNotSpill ) return SQLITE_OK;
37571 if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
37572 return SQLITE_OK;
37573 }
37574
@@ -37582,20 +38249,14 @@
37582 rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
37583 }
37584 }else{
37585
37586 /* Sync the journal file if required. */
37587 if( pPg->flags&PGHDR_NEED_SYNC ){
37588 assert( !pPager->noSync );
37589 rc = syncJournal(pPager);
37590 if( rc==SQLITE_OK &&
37591 !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) &&
37592 !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
37593 ){
37594 pPager->nRec = 0;
37595 rc = writeJournalHdr(pPager);
37596 }
37597 }
37598
37599 /* If the page number of this page is larger than the current size of
37600 ** the database image, it may need to be written to the sub-journal.
37601 ** This is because the call to pager_write_pagelist() below will not
@@ -37629,10 +38290,11 @@
37629 rc = subjournalPage(pPg);
37630 }
37631
37632 /* Write the contents of the page out to the database file. */
37633 if( rc==SQLITE_OK ){
 
37634 rc = pager_write_pagelist(pPager, pPg);
37635 }
37636 }
37637
37638 /* Mark the page as clean. */
@@ -37639,11 +38301,11 @@
37639 if( rc==SQLITE_OK ){
37640 PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
37641 sqlite3PcacheMakeClean(pPg);
37642 }
37643
37644 return pager_error(pPager, rc);
37645 }
37646
37647
37648 /*
37649 ** Allocate and initialize a new Pager object and put a pointer to it
@@ -37694,11 +38356,11 @@
37694 char *zPathname = 0; /* Full path to database file */
37695 int nPathname = 0; /* Number of bytes in zPathname */
37696 int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
37697 int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
37698 int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
37699 u16 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
37700
37701 /* Figure out how much space is required for each journal file-handle
37702 ** (there are two of them, the main journal and the sub-journal). This
37703 ** is the maximum space required for an in-memory journal file handle
37704 ** and a regular journal file-handle. Note that a "regular journal-handle"
@@ -37829,11 +38491,11 @@
37829 assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
37830 if( szPageDflt<pPager->sectorSize ){
37831 if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
37832 szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
37833 }else{
37834 szPageDflt = (u16)pPager->sectorSize;
37835 }
37836 }
37837 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
37838 {
37839 int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
@@ -37857,11 +38519,12 @@
37857 ** This branch is also run for an in-memory database. An in-memory
37858 ** database is the same as a temp-file that is never written out to
37859 ** disk and uses an in-memory rollback journal.
37860 */
37861 tempFile = 1;
37862 pPager->state = PAGER_EXCLUSIVE;
 
37863 readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
37864 }
37865
37866 /* The following call to PagerSetPagesize() serves to set the value of
37867 ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer.
@@ -37894,27 +38557,27 @@
37894 pPager->useJournal = (u8)useJournal;
37895 pPager->noReadlock = (noReadlock && readOnly) ?1:0;
37896 /* pPager->stmtOpen = 0; */
37897 /* pPager->stmtInUse = 0; */
37898 /* pPager->nRef = 0; */
37899 pPager->dbSizeValid = (u8)memDb;
37900 /* pPager->stmtSize = 0; */
37901 /* pPager->stmtJSize = 0; */
37902 /* pPager->nPage = 0; */
37903 pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
37904 /* pPager->state = PAGER_UNLOCK; */
 
37905 assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
 
37906 /* pPager->errMask = 0; */
37907 pPager->tempFile = (u8)tempFile;
37908 assert( tempFile==PAGER_LOCKINGMODE_NORMAL
37909 || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
37910 assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
37911 pPager->exclusiveMode = (u8)tempFile;
37912 pPager->changeCountDone = pPager->tempFile;
37913 pPager->memDb = (u8)memDb;
37914 pPager->readOnly = (u8)readOnly;
37915 /* pPager->needSync = 0; */
37916 assert( useJournal || pPager->tempFile );
37917 pPager->noSync = pPager->tempFile;
37918 pPager->fullSync = pPager->noSync ?0:1;
37919 pPager->sync_flags = SQLITE_SYNC_NORMAL;
37920 /* pPager->pFirst = 0; */
@@ -37975,24 +38638,24 @@
37975 sqlite3_vfs * const pVfs = pPager->pVfs;
37976 int rc = SQLITE_OK; /* Return code */
37977 int exists = 1; /* True if a journal file is present */
37978 int jrnlOpen = !!isOpen(pPager->jfd);
37979
37980 assert( pPager!=0 );
37981 assert( pPager->useJournal );
37982 assert( isOpen(pPager->fd) );
37983 assert( pPager->state <= PAGER_SHARED );
 
37984 assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
37985 SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
37986 ));
37987
37988 *pExists = 0;
37989 if( !jrnlOpen ){
37990 rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
37991 }
37992 if( rc==SQLITE_OK && exists ){
37993 int locked; /* True if some process holds a RESERVED lock */
37994
37995 /* Race condition here: Another process might have been holding the
37996 ** the RESERVED lock and have a journal open at the sqlite3OsAccess()
37997 ** call above, but then delete the journal and drop the lock before
37998 ** we get to the following sqlite3OsCheckReservedLock() call. If that
@@ -38000,25 +38663,25 @@
38000 ** in fact there is none. This results in a false-positive which will
38001 ** be dealt with by the playback routine. Ticket #3883.
38002 */
38003 rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
38004 if( rc==SQLITE_OK && !locked ){
38005 int nPage;
38006
38007 /* Check the size of the database file. If it consists of 0 pages,
38008 ** then delete the journal file. See the header comment above for
38009 ** the reasoning here. Delete the obsolete journal file under
38010 ** a RESERVED lock to avoid race conditions and to avoid violating
38011 ** [H33020].
38012 */
38013 rc = sqlite3PagerPagecount(pPager, &nPage);
38014 if( rc==SQLITE_OK ){
38015 if( nPage==0 ){
38016 sqlite3BeginBenignMalloc();
38017 if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){
38018 sqlite3OsDelete(pVfs, pPager->zJournal, 0);
38019 sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
38020 }
38021 sqlite3EndBenignMalloc();
38022 }else{
38023 /* The journal file exists and no other connection has a reserved
38024 ** or greater lock on the database file. Now check that there is
@@ -38067,11 +38730,11 @@
38067 ** has been successfully called. If a shared-lock is already held when
38068 ** this function is called, it is a no-op.
38069 **
38070 ** The following operations are also performed by this function.
38071 **
38072 ** 1) If the pager is currently in PAGER_UNLOCK state (no lock held
38073 ** on the database file), then an attempt is made to obtain a
38074 ** SHARED lock on the database file. Immediately after obtaining
38075 ** the SHARED lock, the file-system is checked for a hot-journal,
38076 ** which is played back if present. Following any hot-journal
38077 ** rollback, the contents of the cache are validated by checking
@@ -38082,70 +38745,51 @@
38082 ** no outstanding references to any pages, and is in the error state,
38083 ** then an attempt is made to clear the error state by discarding
38084 ** the contents of the page cache and rolling back any open journal
38085 ** file.
38086 **
38087 ** If the operation described by (2) above is not attempted, and if the
38088 ** pager is in an error state other than SQLITE_FULL when this is called,
38089 ** the error state error code is returned. It is permitted to read the
38090 ** database when in SQLITE_FULL error state.
38091 **
38092 ** Otherwise, if everything is successful, SQLITE_OK is returned. If an
38093 ** IO error occurs while locking the database, checking for a hot-journal
38094 ** file or rolling back a journal file, the IO error code is returned.
38095 */
38096 SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
38097 int rc = SQLITE_OK; /* Return code */
38098 int isErrorReset = 0; /* True if recovering from error state */
38099
38100 /* This routine is only called from b-tree and only when there are no
38101 ** outstanding pages */
 
 
 
38102 assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
 
 
38103 if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
38104
38105 /* If this database is in an error-state, now is a chance to clear
38106 ** the error. Discard the contents of the pager-cache and rollback
38107 ** any hot journal in the file-system.
38108 */
38109 if( pPager->errCode ){
38110 if( isOpen(pPager->jfd) || pPager->zJournal ){
38111 isErrorReset = 1;
38112 }
38113 pPager->errCode = SQLITE_OK;
38114 pager_reset(pPager);
38115 }
38116
38117 if( pagerUseWal(pPager) ){
38118 rc = pagerBeginReadTransaction(pPager);
38119 }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
38120 sqlite3_vfs * const pVfs = pPager->pVfs;
38121 int isHotJournal = 0;
38122 assert( !MEMDB );
38123 assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
38124 if( pPager->noReadlock ){
38125 assert( pPager->readOnly );
38126 pPager->state = PAGER_SHARED;
38127 }else{
38128 rc = pager_wait_on_lock(pPager, SHARED_LOCK);
38129 if( rc!=SQLITE_OK ){
38130 assert( pPager->state==PAGER_UNLOCK );
38131 return pager_error(pPager, rc);
38132 }
38133 }
38134 assert( pPager->state>=SHARED_LOCK );
38135
38136 /* If a journal file exists, and there is no RESERVED lock on the
38137 ** database file, then it either needs to be played back or deleted.
38138 */
38139 if( !isErrorReset ){
38140 assert( pPager->state <= PAGER_SHARED );
38141 rc = hasHotJournal(pPager, &isHotJournal);
38142 if( rc!=SQLITE_OK ){
38143 goto failed;
38144 }
38145 }
38146 if( isErrorReset || isHotJournal ){
38147 /* Get an EXCLUSIVE lock on the database file. At this point it is
38148 ** important that a RESERVED lock is not obtained on the way to the
38149 ** EXCLUSIVE lock. If it were, another process might open the
38150 ** database file, detect the RESERVED lock, and conclude that the
38151 ** database is safe to read while this process is still rolling the
@@ -38153,62 +38797,49 @@
38153 **
38154 ** Because the intermediate RESERVED lock is not requested, any
38155 ** other process attempting to access the database file will get to
38156 ** this point in the code and fail to obtain its own EXCLUSIVE lock
38157 ** on the database file.
38158 */
38159 if( pPager->state<EXCLUSIVE_LOCK ){
38160 rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
38161 if( rc!=SQLITE_OK ){
38162 rc = pager_error(pPager, rc);
38163 goto failed;
38164 }
38165 pPager->state = PAGER_EXCLUSIVE;
38166 }
38167
38168 /* Open the journal for read/write access. This is because in
38169 ** exclusive-access mode the file descriptor will be kept open and
38170 ** possibly used for a transaction later on. On some systems, the
38171 ** OsTruncate() call used in exclusive-access mode also requires
38172 ** a read/write file handle.
38173 */
38174 if( !isOpen(pPager->jfd) ){
38175 int res;
38176 rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res);
38177 if( rc==SQLITE_OK ){
38178 if( res ){
38179 int fout = 0;
38180 int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
38181 assert( !pPager->tempFile );
38182 rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
38183 assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38184 if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
38185 rc = SQLITE_CANTOPEN_BKPT;
38186 sqlite3OsClose(pPager->jfd);
38187 }
38188 }else{
38189 /* If the journal does not exist, it usually means that some
38190 ** other connection managed to get in and roll it back before
38191 ** this connection obtained the exclusive lock above. Or, it
38192 ** may mean that the pager was in the error-state when this
38193 ** function was called and the journal file does not exist. */
38194 rc = pager_end_transaction(pPager, 0);
38195 }
38196 }
38197 }
38198 if( rc!=SQLITE_OK ){
38199 goto failed;
38200 }
38201
38202 /* Reset the journal status fields to indicates that we have no
38203 ** rollback journal at this time. */
38204 pPager->journalStarted = 0;
38205 pPager->journalOff = 0;
38206 pPager->setMaster = 0;
38207 pPager->journalHdr = 0;
38208
38209 /* Make sure the journal file has been synced to disk. */
38210
38211 /* Playback and delete the journal. Drop the database write
38212 ** lock and reacquire the read lock. Purge the cache before
38213 ** playing back the hot-journal so that we don't end up with
38214 ** an inconsistent cache. Sync the hot journal before playing
@@ -38215,25 +38846,50 @@
38215 ** it back since the process that crashed and left the hot journal
38216 ** probably did not sync it and we are required to always sync
38217 ** the journal before playing it back.
38218 */
38219 if( isOpen(pPager->jfd) ){
 
38220 rc = pagerSyncHotJournal(pPager);
38221 if( rc==SQLITE_OK ){
38222 rc = pager_playback(pPager, 1);
38223 }
38224 if( rc!=SQLITE_OK ){
38225 rc = pager_error(pPager, rc);
38226 goto failed;
38227 }
38228 }
38229 assert( (pPager->state==PAGER_SHARED)
38230 || (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38231 );
38232 }
38233
38234 if( pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0 ){
 
 
38235 /* The shared-lock has just been acquired on the database file
38236 ** and there are already pages in the cache (from a previous
38237 ** read or write transaction). Check to see if the database
38238 ** has been modified. If the database has changed, flush the
38239 ** cache.
@@ -38246,18 +38902,15 @@
38246 **
38247 ** There is a vanishingly small chance that a change will not be
38248 ** detected. The chance of an undetected change is so small that
38249 ** it can be neglected.
38250 */
38251 int nPage = 0;
38252 char dbFileVers[sizeof(pPager->dbFileVers)];
38253 sqlite3PagerPagecount(pPager, &nPage);
38254
38255 if( pPager->errCode ){
38256 rc = pPager->errCode;
38257 goto failed;
38258 }
38259
38260 if( nPage>0 ){
38261 IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
38262 rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
38263 if( rc!=SQLITE_OK ){
@@ -38269,22 +38922,34 @@
38269
38270 if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
38271 pager_reset(pPager);
38272 }
38273 }
38274 assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );
38275
38276 /* If there is a WAL file in the file-system, open this database in WAL
38277 ** mode. Otherwise, the following function call is a no-op.
38278 */
38279 rc = pagerOpenWalIfPresent(pPager);
 
 
 
 
 
 
 
 
 
 
38280 }
38281
38282 failed:
38283 if( rc!=SQLITE_OK ){
38284 /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
38285 pager_unlock(pPager);
 
 
 
38286 }
38287 return rc;
38288 }
38289
38290 /*
@@ -38294,13 +38959,11 @@
38294 ** Except, in locking_mode=EXCLUSIVE when there is nothing to in
38295 ** the rollback journal, the unlock is not performed and there is
38296 ** nothing to rollback, so this routine is a no-op.
38297 */
38298 static void pagerUnlockIfUnused(Pager *pPager){
38299 if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
38300 && (!pPager->exclusiveMode || pPager->journalOff>0)
38301 ){
38302 pagerUnlockAndRollback(pPager);
38303 }
38304 }
38305
38306 /*
@@ -38360,20 +39023,20 @@
38360 int noContent /* Do not bother reading content from disk if true */
38361 ){
38362 int rc;
38363 PgHdr *pPg;
38364
 
38365 assert( assert_pager_state(pPager) );
38366 assert( pPager->state>PAGER_UNLOCK );
38367
38368 if( pgno==0 ){
38369 return SQLITE_CORRUPT_BKPT;
38370 }
38371
38372 /* If the pager is in the error state, return an error immediately.
38373 ** Otherwise, request the page from the PCache layer. */
38374 if( pPager->errCode!=SQLITE_OK && pPager->errCode!=SQLITE_FULL ){
38375 rc = pPager->errCode;
38376 }else{
38377 rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
38378 }
38379
@@ -38395,11 +39058,10 @@
38395 return SQLITE_OK;
38396
38397 }else{
38398 /* The pager cache has created a new page. Its content needs to
38399 ** be initialized. */
38400 int nMax;
38401
38402 PAGER_INCR(pPager->nMiss);
38403 pPg = *ppPage;
38404 pPg->pPager = pPager;
38405
@@ -38408,16 +39070,11 @@
38408 if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
38409 rc = SQLITE_CORRUPT_BKPT;
38410 goto pager_acquire_err;
38411 }
38412
38413 rc = sqlite3PagerPagecount(pPager, &nMax);
38414 if( rc!=SQLITE_OK ){
38415 goto pager_acquire_err;
38416 }
38417
38418 if( MEMDB || nMax<(int)pgno || noContent || !isOpen(pPager->fd) ){
38419 if( pgno>pPager->mxPgno ){
38420 rc = SQLITE_FULL;
38421 goto pager_acquire_err;
38422 }
38423 if( noContent ){
@@ -38443,13 +39100,11 @@
38443 rc = readDbPage(pPg);
38444 if( rc!=SQLITE_OK ){
38445 goto pager_acquire_err;
38446 }
38447 }
38448 #ifdef SQLITE_CHECK_PAGES
38449 pPg->pageHash = pager_pagehash(pPg);
38450 #endif
38451 }
38452
38453 return SQLITE_OK;
38454
38455 pager_acquire_err:
@@ -38464,13 +39119,11 @@
38464 }
38465
38466 /*
38467 ** Acquire a page if it is already in the in-memory cache. Do
38468 ** not read the page from disk. Return a pointer to the page,
38469 ** or 0 if the page is not in cache. Also, return 0 if the
38470 ** pager is in PAGER_UNLOCK state when this function is called,
38471 ** or if the pager is in an error state other than SQLITE_FULL.
38472 **
38473 ** See also sqlite3PagerGet(). The difference between this routine
38474 ** and sqlite3PagerGet() is that _get() will go to the disk and read
38475 ** in the page if the page is not already in cache. This routine
38476 ** returns NULL if the page is not in cache or if a disk I/O error
@@ -38479,11 +39132,11 @@
38479 SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
38480 PgHdr *pPg = 0;
38481 assert( pPager!=0 );
38482 assert( pgno!=0 );
38483 assert( pPager->pPCache!=0 );
38484 assert( pPager->state > PAGER_UNLOCK );
38485 sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
38486 return pPg;
38487 }
38488
38489 /*
@@ -38524,73 +39177,71 @@
38524 ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
38525 ** an IO error code if opening or writing the journal file fails.
38526 */
38527 static int pager_open_journal(Pager *pPager){
38528 int rc = SQLITE_OK; /* Return code */
38529 int nPage; /* Size of database file */
38530 sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */
38531
38532 assert( pPager->state>=PAGER_RESERVED );
38533 assert( pPager->useJournal );
38534 assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF );
38535 assert( pPager->pInJournal==0 );
38536
38537 /* If already in the error state, this function is a no-op. But on
38538 ** the other hand, this routine is never called if we are already in
38539 ** an error state. */
38540 if( NEVER(pPager->errCode) ) return pPager->errCode;
38541
38542 testcase( pPager->dbSizeValid==0 );
38543 rc = sqlite3PagerPagecount(pPager, &nPage);
38544 if( rc ) return rc;
38545 pPager->pInJournal = sqlite3BitvecCreate(nPage);
38546 if( pPager->pInJournal==0 ){
38547 return SQLITE_NOMEM;
38548 }
38549
38550 /* Open the journal file if it is not already open. */
38551 if( !isOpen(pPager->jfd) ){
38552 if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
38553 sqlite3MemJournalOpen(pPager->jfd);
38554 }else{
38555 const int flags = /* VFS flags to open journal file */
38556 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
38557 (pPager->tempFile ?
38558 (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
38559 (SQLITE_OPEN_MAIN_JOURNAL)
38560 );
38561 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
38562 rc = sqlite3JournalOpen(
38563 pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
38564 );
38565 #else
38566 rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
38567 #endif
38568 }
38569 assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38570 }
38571
38572
38573 /* Write the first journal header to the journal file and open
38574 ** the sub-journal if necessary.
38575 */
38576 if( rc==SQLITE_OK ){
38577 /* TODO: Check if all of these are really required. */
38578 pPager->dbOrigSize = pPager->dbSize;
38579 pPager->journalStarted = 0;
38580 pPager->needSync = 0;
38581 pPager->nRec = 0;
38582 pPager->journalOff = 0;
38583 pPager->setMaster = 0;
38584 pPager->journalHdr = 0;
38585 rc = writeJournalHdr(pPager);
38586 }
38587
38588 if( rc!=SQLITE_OK ){
38589 sqlite3BitvecDestroy(pPager->pInJournal);
38590 pPager->pInJournal = 0;
 
 
 
38591 }
 
38592 return rc;
38593 }
38594
38595 /*
38596 ** Begin a write-transaction on the specified pager object. If a
@@ -38599,18 +39250,10 @@
38599 ** If the exFlag argument is false, then acquire at least a RESERVED
38600 ** lock on the database file. If exFlag is true, then acquire at least
38601 ** an EXCLUSIVE lock. If such a lock is already held, no locking
38602 ** functions need be called.
38603 **
38604 ** If this is not a temporary or in-memory file and, the journal file is
38605 ** opened if it has not been already. For a temporary file, the opening
38606 ** of the journal file is deferred until there is an actual need to
38607 ** write to the journal. TODO: Why handle temporary files differently?
38608 **
38609 ** If the journal file is opened (or if it is already open), then a
38610 ** journal-header is written to the start of it.
38611 **
38612 ** If the subjInMemory argument is non-zero, then any sub-journal opened
38613 ** within this transaction will be opened as an in-memory file. This
38614 ** has no effect if the sub-journal is already opened (as it may be when
38615 ** running in exclusive mode) or if the transaction does not require a
38616 ** sub-journal. If the subjInMemory argument is zero, then any required
@@ -38617,24 +39260,24 @@
38617 ** sub-journal is implemented in-memory if pPager is an in-memory database,
38618 ** or using a temporary file otherwise.
38619 */
38620 SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
38621 int rc = SQLITE_OK;
38622 assert( pPager->state!=PAGER_UNLOCK );
 
 
38623 pPager->subjInMemory = (u8)subjInMemory;
38624
38625 if( pPager->state==PAGER_SHARED ){
38626 assert( pPager->pInJournal==0 );
38627 assert( !MEMDB && !pPager->tempFile );
38628
38629 if( pagerUseWal(pPager) ){
38630 /* If the pager is configured to use locking_mode=exclusive, and an
38631 ** exclusive lock on the database is not already held, obtain it now.
38632 */
38633 if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
38634 rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
38635 pPager->state = PAGER_SHARED;
38636 if( rc!=SQLITE_OK ){
38637 return rc;
38638 }
38639 sqlite3WalExclusiveMode(pPager->pWal, 1);
38640 }
@@ -38641,56 +39284,44 @@
38641
38642 /* Grab the write lock on the log file. If successful, upgrade to
38643 ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
38644 ** The busy-handler is not invoked if another connection already
38645 ** holds the write-lock. If possible, the upper layer will call it.
38646 **
38647 ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open
38648 ** transaction, but never to PAGER_EXCLUSIVE. This is because in
38649 ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions
38650 ** may copy data from the sub-journal into the database file as well
38651 ** as into the page cache. Which would be incorrect in WAL mode.
38652 */
38653 rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
38654 if( rc==SQLITE_OK ){
38655 pPager->dbOrigSize = pPager->dbSize;
38656 pPager->state = PAGER_RESERVED;
38657 pPager->journalOff = 0;
38658 }
38659
38660 assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED );
38661 assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED );
38662 }else{
38663 /* Obtain a RESERVED lock on the database file. If the exFlag parameter
38664 ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
38665 ** busy-handler callback can be used when upgrading to the EXCLUSIVE
38666 ** lock, but not when obtaining the RESERVED lock.
38667 */
38668 rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
38669 if( rc==SQLITE_OK ){
38670 pPager->state = PAGER_RESERVED;
38671 if( exFlag ){
38672 rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
38673 }
38674 }
38675 }
38676
38677 /* No need to open the journal file at this time. It will be
38678 ** opened before it is written to. If we defer opening the journal,
38679 ** we might save the work of creating a file if the transaction
38680 ** ends up being a no-op.
38681 */
38682
38683 if( rc!=SQLITE_OK ){
38684 assert( !pPager->dbModified );
38685 /* Ignore any IO error that occurs within pager_end_transaction(). The
38686 ** purpose of this call is to reset the internal state of the pager
38687 ** sub-system. It doesn't matter if the journal-file is not properly
38688 ** finalized at this point (since it is not a valid journal file anyway).
38689 */
38690 pager_end_transaction(pPager, 0);
38691 }
 
 
38692 }
38693
38694 PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
38695 return rc;
38696 }
@@ -38705,110 +39336,98 @@
38705 static int pager_write(PgHdr *pPg){
38706 void *pData = pPg->pData;
38707 Pager *pPager = pPg->pPager;
38708 int rc = SQLITE_OK;
38709
38710 /* This routine is not called unless a transaction has already been
38711 ** started.
 
38712 */
38713 assert( pPager->state>=PAGER_RESERVED );
 
 
 
 
38714
38715 /* If an error has been previously detected, report the same error
38716 ** again.
38717 */
38718 if( NEVER(pPager->errCode) ) return pPager->errCode;
38719
38720 /* Higher-level routines never call this function if database is not
38721 ** writable. But check anyway, just for robustness. */
38722 if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
38723
38724 assert( !pPager->setMaster );
38725
38726 CHECK_PAGE(pPg);
38727
38728 /* Mark the page as dirty. If the page has already been written
38729 ** to the journal then we can return right away.
38730 */
38731 sqlite3PcacheMakeDirty(pPg);
38732 if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
38733 assert( !pagerUseWal(pPager) );
38734 pPager->dbModified = 1;
38735 }else{
38736
38737 /* If we get this far, it means that the page needs to be
38738 ** written to the transaction journal or the ckeckpoint journal
38739 ** or both.
38740 **
38741 ** Higher level routines should have already started a transaction,
38742 ** which means they have acquired the necessary locks but the rollback
38743 ** journal might not yet be open.
38744 */
38745 assert( pPager->state>=RESERVED_LOCK );
38746 if( pPager->pInJournal==0
38747 && pPager->journalMode!=PAGER_JOURNALMODE_OFF
38748 && !pagerUseWal(pPager)
38749 ){
38750 assert( pPager->useJournal );
38751 rc = pager_open_journal(pPager);
38752 if( rc!=SQLITE_OK ) return rc;
38753 }
38754 pPager->dbModified = 1;
 
38755
38756 /* The transaction journal now exists and we have a RESERVED or an
38757 ** EXCLUSIVE lock on the main database file. Write the current page to
38758 ** the transaction journal if it is not there already.
38759 */
38760 if( !pageInJournal(pPg) && isOpen(pPager->jfd) ){
38761 assert( !pagerUseWal(pPager) );
38762 if( pPg->pgno<=pPager->dbOrigSize ){
38763 u32 cksum;
38764 char *pData2;
 
38765
38766 /* We should never write to the journal file the page that
38767 ** contains the database locks. The following assert verifies
38768 ** that we do not. */
38769 assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
38770
38771 assert( pPager->journalHdr <= pPager->journalOff );
38772 CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
38773 cksum = pager_cksum(pPager, (u8*)pData2);
38774 rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno);
38775 if( rc==SQLITE_OK ){
38776 rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize,
38777 pPager->journalOff + 4);
38778 pPager->journalOff += pPager->pageSize+4;
38779 }
38780 if( rc==SQLITE_OK ){
38781 rc = write32bits(pPager->jfd, pPager->journalOff, cksum);
38782 pPager->journalOff += 4;
38783 }
 
 
 
 
 
 
 
38784 IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
38785 pPager->journalOff, pPager->pageSize));
38786 PAGER_INCR(sqlite3_pager_writej_count);
38787 PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
38788 PAGERID(pPager), pPg->pgno,
38789 ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
38790
38791 /* Even if an IO or diskfull error occurred while journalling the
38792 ** page in the block above, set the need-sync flag for the page.
38793 ** Otherwise, when the transaction is rolled back, the logic in
38794 ** playback_one_page() will think that the page needs to be restored
38795 ** in the database file. And if an IO error occurs while doing so,
38796 ** then corruption may follow.
38797 */
38798 if( !pPager->noSync ){
38799 pPg->flags |= PGHDR_NEED_SYNC;
38800 pPager->needSync = 1;
38801 }
38802
38803 /* An error has occurred writing to the journal file. The
38804 ** transaction will be rolled back by the layer above.
38805 */
38806 if( rc!=SQLITE_OK ){
38807 return rc;
38808 }
38809
38810 pPager->nRec++;
38811 assert( pPager->pInJournal!=0 );
38812 rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
38813 testcase( rc==SQLITE_NOMEM );
38814 assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
@@ -38816,13 +39435,12 @@
38816 if( rc!=SQLITE_OK ){
38817 assert( rc==SQLITE_NOMEM );
38818 return rc;
38819 }
38820 }else{
38821 if( !pPager->journalStarted && !pPager->noSync ){
38822 pPg->flags |= PGHDR_NEED_SYNC;
38823 pPager->needSync = 1;
38824 }
38825 PAGERTRACE(("APPEND %d page %d needSync=%d\n",
38826 PAGERID(pPager), pPg->pgno,
38827 ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
38828 }
@@ -38838,11 +39456,10 @@
38838 }
38839 }
38840
38841 /* Update the database size and return.
38842 */
38843 assert( pPager->state>=PAGER_SHARED );
38844 if( pPager->dbSize<pPg->pgno ){
38845 pPager->dbSize = pPg->pgno;
38846 }
38847 return rc;
38848 }
@@ -38866,10 +39483,14 @@
38866
38867 PgHdr *pPg = pDbPage;
38868 Pager *pPager = pPg->pPager;
38869 Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
38870
 
 
 
 
38871 if( nPagePerSector>1 ){
38872 Pgno nPageCount; /* Total number of pages in database file */
38873 Pgno pg1; /* First page of the sector pPg is located on. */
38874 int nPage = 0; /* Number of pages starting at pg1 to journal */
38875 int ii; /* Loop counter */
@@ -38887,23 +39508,21 @@
38887 ** an integer power of 2. It sets variable pg1 to the identifier
38888 ** of the first page of the sector pPg is located on.
38889 */
38890 pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
38891
38892 rc = sqlite3PagerPagecount(pPager, (int *)&nPageCount);
38893 if( rc==SQLITE_OK ){
38894 if( pPg->pgno>nPageCount ){
38895 nPage = (pPg->pgno - pg1)+1;
38896 }else if( (pg1+nPagePerSector-1)>nPageCount ){
38897 nPage = nPageCount+1-pg1;
38898 }else{
38899 nPage = nPagePerSector;
38900 }
38901 assert(nPage>0);
38902 assert(pg1<=pPg->pgno);
38903 assert((pg1+nPage)>pPg->pgno);
38904 }
38905
38906 for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
38907 Pgno pg = pg1+ii;
38908 PgHdr *pPage;
38909 if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
@@ -38911,11 +39530,10 @@
38911 rc = sqlite3PagerGet(pPager, pg, &pPage);
38912 if( rc==SQLITE_OK ){
38913 rc = pager_write(pPage);
38914 if( pPage->flags&PGHDR_NEED_SYNC ){
38915 needSync = 1;
38916 assert(pPager->needSync);
38917 }
38918 sqlite3PagerUnref(pPage);
38919 }
38920 }
38921 }else if( (pPage = pager_lookup(pPager, pg))!=0 ){
@@ -38931,19 +39549,18 @@
38931 ** writing to any of these nPage pages may damage the others, the
38932 ** journal file must contain sync()ed copies of all of them
38933 ** before any of them can be written out to the database file.
38934 */
38935 if( rc==SQLITE_OK && needSync ){
38936 assert( !MEMDB && pPager->noSync==0 );
38937 for(ii=0; ii<nPage; ii++){
38938 PgHdr *pPage = pager_lookup(pPager, pg1+ii);
38939 if( pPage ){
38940 pPage->flags |= PGHDR_NEED_SYNC;
38941 sqlite3PagerUnref(pPage);
38942 }
38943 }
38944 assert(pPager->needSync);
38945 }
38946
38947 assert( pPager->doNotSyncSpill==1 );
38948 pPager->doNotSyncSpill--;
38949 }else{
@@ -38981,13 +39598,11 @@
38981 Pager *pPager = pPg->pPager;
38982 if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
38983 PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
38984 IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
38985 pPg->flags |= PGHDR_DONT_WRITE;
38986 #ifdef SQLITE_CHECK_PAGES
38987 pPg->pageHash = pager_pagehash(pPg);
38988 #endif
38989 }
38990 }
38991
38992 /*
38993 ** This routine is called to increment the value of the database file
@@ -39005,10 +39620,15 @@
39005 ** by writing an updated version of page 1 using a call to the
39006 ** sqlite3OsWrite() function.
39007 */
39008 static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
39009 int rc = SQLITE_OK;
 
 
 
 
 
39010
39011 /* Declare and initialize constant integer 'isDirect'. If the
39012 ** atomic-write optimization is enabled in this build, then isDirect
39013 ** is initialized to the value passed as the isDirectMode parameter
39014 ** to this function. Otherwise, it is always set to zero.
@@ -39024,11 +39644,10 @@
39024 UNUSED_PARAMETER(isDirectMode);
39025 #else
39026 # define DIRECT_MODE isDirectMode
39027 #endif
39028
39029 assert( pPager->state>=PAGER_RESERVED );
39030 if( !pPager->changeCountDone && pPager->dbSize>0 ){
39031 PgHdr *pPgHdr; /* Reference to page 1 */
39032 u32 change_counter; /* Initial value of change-counter field */
39033
39034 assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -39109,13 +39728,17 @@
39109 ** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
39110 ** returned.
39111 */
39112 SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
39113 int rc = SQLITE_OK;
39114 assert( pPager->state>=PAGER_RESERVED );
 
 
 
 
39115 if( 0==pagerUseWal(pPager) ){
39116 rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE);
39117 }
39118 return rc;
39119 }
39120
39121 /*
@@ -39149,26 +39772,33 @@
39149 const char *zMaster, /* If not NULL, the master journal name */
39150 int noSync /* True to omit the xSync on the db file */
39151 ){
39152 int rc = SQLITE_OK; /* Return code */
39153
39154 /* The dbOrigSize is never set if journal_mode=OFF */
39155 assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 );
 
 
 
 
39156
39157 /* If a prior error occurred, report that error again. */
39158 if( pPager->errCode ) return pPager->errCode;
39159
39160 PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
39161 pPager->zFilename, zMaster, pPager->dbSize));
39162
39163 if( MEMDB && pPager->dbModified ){
 
 
 
39164 /* If this is an in-memory db, or no pages have been written to, or this
39165 ** function has already been called, it is mostly a no-op. However, any
39166 ** backup in progress needs to be restarted.
39167 */
39168 sqlite3BackupRestart(pPager->pBackup);
39169 }else if( pPager->dbModified ){
39170 if( pagerUseWal(pPager) ){
39171 PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
39172 if( pList ){
39173 rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
39174 (pPager->fullSync ? pPager->sync_flags : 0)
@@ -39207,11 +39837,11 @@
39207 || pPager->journalMode==PAGER_JOURNALMODE_OFF
39208 || pPager->journalMode==PAGER_JOURNALMODE_WAL
39209 );
39210 if( !zMaster && isOpen(pPager->jfd)
39211 && pPager->journalOff==jrnlBufferSize(pPager)
39212 && pPager->dbSize>=pPager->dbFileSize
39213 && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
39214 ){
39215 /* Update the db file change counter via the direct-write method. The
39216 ** following call will modify the in-memory representation of page 1
39217 ** to include the updated change counter and then write page 1
@@ -39237,17 +39867,14 @@
39237 ** Before reading the pages with page numbers larger than the
39238 ** current value of Pager.dbSize, set dbSize back to the value
39239 ** that it took at the start of the transaction. Otherwise, the
39240 ** calls to sqlite3PagerGet() return zeroed pages instead of
39241 ** reading data from the database file.
39242 **
39243 ** When journal_mode==OFF the dbOrigSize is always zero, so this
39244 ** block never runs if journal_mode=OFF.
39245 */
39246 #ifndef SQLITE_OMIT_AUTOVACUUM
39247 if( pPager->dbSize<pPager->dbOrigSize
39248 && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF)
39249 ){
39250 Pgno i; /* Iterator variable */
39251 const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
39252 const Pgno dbSize = pPager->dbSize; /* Database image size */
39253 pPager->dbSize = pPager->dbOrigSize;
@@ -39270,18 +39897,24 @@
39270 ** or if zMaster is NULL (no master journal), then this call is a no-op.
39271 */
39272 rc = writeMasterJournal(pPager, zMaster);
39273 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39274
39275 /* Sync the journal file. If the atomic-update optimization is being
39276 ** used, this call will not create the journal file or perform any
39277 ** real IO.
 
 
 
 
 
 
 
39278 */
39279 rc = syncJournal(pPager);
39280 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39281
39282 /* Write all dirty pages to the database file. */
39283 rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
39284 if( rc!=SQLITE_OK ){
39285 assert( rc!=SQLITE_IOERR_BLOCKED );
39286 goto commit_phase_one_exit;
39287 }
@@ -39290,11 +39923,11 @@
39290 /* If the file on disk is not the same size as the database image,
39291 ** then use pager_truncate to grow or shrink the file here.
39292 */
39293 if( pPager->dbSize!=pPager->dbFileSize ){
39294 Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
39295 assert( pPager->state>=PAGER_EXCLUSIVE );
39296 rc = pager_truncate(pPager, nNew);
39297 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39298 }
39299
39300 /* Finally, sync the database file. */
@@ -39301,16 +39934,16 @@
39301 if( !pPager->noSync && !noSync ){
39302 rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
39303 }
39304 IOTRACE(("DBSYNC %p\n", pPager))
39305 }
39306
39307 assert( pPager->state!=PAGER_SYNCED );
39308 pPager->state = PAGER_SYNCED;
39309 }
39310
39311 commit_phase_one_exit:
 
 
 
39312 return rc;
39313 }
39314
39315
39316 /*
@@ -39334,16 +39967,15 @@
39334 /* This routine should not be called if a prior error has occurred.
39335 ** But if (due to a coding error elsewhere in the system) it does get
39336 ** called, just return the same error code without doing anything. */
39337 if( NEVER(pPager->errCode) ) return pPager->errCode;
39338
39339 /* This function should not be called if the pager is not in at least
39340 ** PAGER_RESERVED state. **FIXME**: Make it so that this test always
39341 ** fails - make it so that we never reach this point if we do not hold
39342 ** all necessary locks.
39343 */
39344 if( NEVER(pPager->state<PAGER_RESERVED) ) return SQLITE_ERROR;
39345
39346 /* An optimization. If the database was not actually modified during
39347 ** this transaction, the pager is running in exclusive-mode and is
39348 ** using persistent journals, then this function is a no-op.
39349 **
@@ -39352,106 +39984,80 @@
39352 ** a hot-journal during hot-journal rollback, 0 changes will be made
39353 ** to the database file. So there is no need to zero the journal
39354 ** header. Since the pager is in exclusive mode, there is no need
39355 ** to drop any locks either.
39356 */
39357 if( pPager->dbModified==0 && pPager->exclusiveMode
 
39358 && pPager->journalMode==PAGER_JOURNALMODE_PERSIST
39359 ){
39360 assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
 
39361 return SQLITE_OK;
39362 }
39363
39364 PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
39365 assert( pPager->state==PAGER_SYNCED || MEMDB || !pPager->dbModified );
39366 rc = pager_end_transaction(pPager, pPager->setMaster);
39367 return pager_error(pPager, rc);
39368 }
39369
39370 /*
39371 ** Rollback all changes. The database falls back to PAGER_SHARED mode.
 
 
 
39372 **
39373 ** This function performs two tasks:
 
 
 
39374 **
39375 ** 1) It rolls back the journal file, restoring all database file and
39376 ** in-memory cache pages to the state they were in when the transaction
39377 ** was opened, and
 
39378 ** 2) It finalizes the journal file, so that it is not used for hot
39379 ** rollback at any point in the future.
39380 **
39381 ** subject to the following qualifications:
39382 **
39383 ** * If the journal file is not yet open when this function is called,
39384 ** then only (2) is performed. In this case there is no journal file
39385 ** to roll back.
39386 **
39387 ** * If in an error state other than SQLITE_FULL, then task (1) is
39388 ** performed. If successful, task (2). Regardless of the outcome
39389 ** of either, the error state error code is returned to the caller
39390 ** (i.e. either SQLITE_IOERR or SQLITE_CORRUPT).
39391 **
39392 ** * If the pager is in PAGER_RESERVED state, then attempt (1). Whether
39393 ** or not (1) is successful, also attempt (2). If successful, return
39394 ** SQLITE_OK. Otherwise, enter the error state and return the first
39395 ** error code encountered.
39396 **
39397 ** In this case there is no chance that the database was written to.
39398 ** So is safe to finalize the journal file even if the playback
39399 ** (operation 1) failed. However the pager must enter the error state
39400 ** as the contents of the in-memory cache are now suspect.
39401 **
39402 ** * Finally, if in PAGER_EXCLUSIVE state, then attempt (1). Only
39403 ** attempt (2) if (1) is successful. Return SQLITE_OK if successful,
39404 ** otherwise enter the error state and return the error code from the
39405 ** failing operation.
39406 **
39407 ** In this case the database file may have been written to. So if the
39408 ** playback operation did not succeed it would not be safe to finalize
39409 ** the journal file. It needs to be left in the file-system so that
39410 ** some other process can use it to restore the database state (by
39411 ** hot-journal rollback).
39412 */
39413 SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
39414 int rc = SQLITE_OK; /* Return code */
39415 PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
 
 
 
 
 
 
 
 
 
39416 if( pagerUseWal(pPager) ){
39417 int rc2;
39418
39419 rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
39420 rc2 = pager_end_transaction(pPager, pPager->setMaster);
39421 if( rc==SQLITE_OK ) rc = rc2;
39422 rc = pager_error(pPager, rc);
39423 }else if( !pPager->dbModified || !isOpen(pPager->jfd) ){
39424 rc = pager_end_transaction(pPager, pPager->setMaster);
39425 }else if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
39426 if( pPager->state>=PAGER_EXCLUSIVE ){
39427 pager_playback(pPager, 0);
39428 }
39429 rc = pPager->errCode;
39430 }else{
39431 if( pPager->state==PAGER_RESERVED ){
39432 int rc2;
39433 rc = pager_playback(pPager, 0);
39434 rc2 = pager_end_transaction(pPager, pPager->setMaster);
39435 if( rc==SQLITE_OK ){
39436 rc = rc2;
39437 }
39438 }else{
39439 rc = pager_playback(pPager, 0);
39440 }
39441
39442 if( !MEMDB ){
39443 pPager->dbSizeValid = 0;
39444 }
39445
39446 /* If an error occurs during a ROLLBACK, we can no longer trust the pager
39447 ** cache. So call pager_error() on the way out to make any error
39448 ** persistent.
39449 */
39450 rc = pager_error(pPager, rc);
39451 }
39452 return rc;
39453 }
39454
39455 /*
39456 ** Return TRUE if the database file is opened read-only. Return FALSE
39457 ** if the database is (in theory) writable.
@@ -39493,12 +40099,12 @@
39493 SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
39494 static int a[11];
39495 a[0] = sqlite3PcacheRefCount(pPager->pPCache);
39496 a[1] = sqlite3PcachePagecount(pPager->pPCache);
39497 a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
39498 a[3] = pPager->dbSizeValid ? (int) pPager->dbSize : -1;
39499 a[4] = pPager->state;
39500 a[5] = pPager->errCode;
39501 a[6] = pPager->nHit;
39502 a[7] = pPager->nMiss;
39503 a[8] = 0; /* Used to be pPager->nOvfl */
39504 a[9] = pPager->nRead;
@@ -39525,18 +40131,17 @@
39525 ** returned. Otherwise, SQLITE_OK.
39526 */
39527 SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
39528 int rc = SQLITE_OK; /* Return code */
39529 int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
 
 
 
39530
39531 if( nSavepoint>nCurrent && pPager->useJournal ){
39532 int ii; /* Iterator variable */
39533 PagerSavepoint *aNew; /* New Pager.aSavepoint array */
39534 int nPage; /* Size of database file */
39535
39536 rc = sqlite3PagerPagecount(pPager, &nPage);
39537 if( rc ) return rc;
39538
39539 /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
39540 ** if the allocation fails. Otherwise, zero the new portion in case a
39541 ** malloc failure occurs while populating it in the for(...) loop below.
39542 */
@@ -39549,18 +40154,18 @@
39549 memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
39550 pPager->aSavepoint = aNew;
39551
39552 /* Populate the PagerSavepoint structures just allocated. */
39553 for(ii=nCurrent; ii<nSavepoint; ii++){
39554 aNew[ii].nOrig = nPage;
39555 if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
39556 aNew[ii].iOffset = pPager->journalOff;
39557 }else{
39558 aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
39559 }
39560 aNew[ii].iSubRec = pPager->nSubRec;
39561 aNew[ii].pInSavepoint = sqlite3BitvecCreate(nPage);
39562 if( !aNew[ii].pInSavepoint ){
39563 return SQLITE_NOMEM;
39564 }
39565 if( pagerUseWal(pPager) ){
39566 sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -39603,16 +40208,16 @@
39603 ** This function may return SQLITE_NOMEM if a memory allocation fails,
39604 ** or an IO error code if an IO error occurs while rolling back a
39605 ** savepoint. If no errors occur, SQLITE_OK is returned.
39606 */
39607 SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
39608 int rc = SQLITE_OK;
39609
39610 assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
39611 assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
39612
39613 if( iSavepoint<pPager->nSavepoint ){
39614 int ii; /* Iterator variable */
39615 int nNew; /* Number of remaining savepoints after this op. */
39616
39617 /* Figure out how many savepoints will still be active after this
39618 ** operation. Store this value in nNew. Then free resources associated
@@ -39644,12 +40249,12 @@
39644 else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
39645 PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
39646 rc = pagerPlaybackSavepoint(pPager, pSavepoint);
39647 assert(rc!=SQLITE_DONE);
39648 }
39649
39650 }
 
39651 return rc;
39652 }
39653
39654 /*
39655 ** Return the full pathname of the database file.
@@ -39743,10 +40348,14 @@
39743 Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
39744 int rc; /* Return code */
39745 Pgno origPgno; /* The original page number */
39746
39747 assert( pPg->nRef>0 );
 
 
 
 
39748
39749 /* In order to be able to rollback, an in-memory database must journal
39750 ** the page we are moving from.
39751 */
39752 if( MEMDB ){
@@ -39792,15 +40401,14 @@
39792 */
39793 if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
39794 needSyncPgno = pPg->pgno;
39795 assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
39796 assert( pPg->flags&PGHDR_DIRTY );
39797 assert( pPager->needSync );
39798 }
39799
39800 /* If the cache contains a page with page-number pgno, remove it
39801 ** from its hash chain. Also, if the PgHdr.needSync was set for
39802 ** page pgno before the 'move' operation, it needs to be retained
39803 ** for the page moved there.
39804 */
39805 pPg->flags &= ~PGHDR_NEED_SYNC;
39806 pPgOld = pager_lookup(pPager, pgno);
@@ -39808,67 +40416,59 @@
39808 if( pPgOld ){
39809 pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
39810 if( MEMDB ){
39811 /* Do not discard pages from an in-memory database since we might
39812 ** need to rollback later. Just move the page out of the way. */
39813 assert( pPager->dbSizeValid );
39814 sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
39815 }else{
39816 sqlite3PcacheDrop(pPgOld);
39817 }
39818 }
39819
39820 origPgno = pPg->pgno;
39821 sqlite3PcacheMove(pPg, pgno);
39822 sqlite3PcacheMakeDirty(pPg);
39823 pPager->dbModified = 1;
 
 
 
 
 
 
 
 
 
39824
39825 if( needSyncPgno ){
39826 /* If needSyncPgno is non-zero, then the journal file needs to be
39827 ** sync()ed before any data is written to database file page needSyncPgno.
39828 ** Currently, no such page exists in the page-cache and the
39829 ** "is journaled" bitvec flag has been set. This needs to be remedied by
39830 ** loading the page into the pager-cache and setting the PgHdr.needSync
39831 ** flag.
39832 **
39833 ** If the attempt to load the page into the page-cache fails, (due
39834 ** to a malloc() or IO failure), clear the bit in the pInJournal[]
39835 ** array. Otherwise, if the page is loaded and written again in
39836 ** this transaction, it may be written to the database file before
39837 ** it is synced into the journal file. This way, it may end up in
39838 ** the journal file twice, but that is not a problem.
39839 **
39840 ** The sqlite3PagerGet() call may cause the journal to sync. So make
39841 ** sure the Pager.needSync flag is set too.
39842 */
39843 PgHdr *pPgHdr;
39844 assert( pPager->needSync );
39845 rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
39846 if( rc!=SQLITE_OK ){
39847 if( needSyncPgno<=pPager->dbOrigSize ){
39848 assert( pPager->pTmpSpace!=0 );
39849 sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace);
39850 }
39851 return rc;
39852 }
39853 pPager->needSync = 1;
39854 assert( pPager->noSync==0 && !MEMDB );
39855 pPgHdr->flags |= PGHDR_NEED_SYNC;
39856 sqlite3PcacheMakeDirty(pPgHdr);
39857 sqlite3PagerUnref(pPgHdr);
39858 }
39859
39860 /*
39861 ** For an in-memory database, make sure the original page continues
39862 ** to exist, in case the transaction needs to roll back. Use pPgOld
39863 ** as the original page since it has already been allocated.
39864 */
39865 if( MEMDB ){
39866 sqlite3PcacheMove(pPgOld, origPgno);
39867 sqlite3PagerUnref(pPgOld);
39868 }
39869
39870 return SQLITE_OK;
39871 }
39872 #endif
39873
39874 /*
@@ -39929,10 +40529,17 @@
39929 **
39930 ** The returned indicate the current (possibly updated) journal-mode.
39931 */
39932 SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
39933 u8 eOld = pPager->journalMode; /* Prior journalmode */
 
 
 
 
 
 
 
39934
39935 /* The eMode parameter is always valid */
39936 assert( eMode==PAGER_JOURNALMODE_DELETE
39937 || eMode==PAGER_JOURNALMODE_TRUNCATE
39938 || eMode==PAGER_JOURNALMODE_PERSIST
@@ -39955,24 +40562,17 @@
39955 eMode = eOld;
39956 }
39957 }
39958
39959 if( eMode!=eOld ){
39960 /* When changing between rollback modes, close the journal file prior
39961 ** to the change. But when changing from a rollback mode to WAL, keep
39962 ** the journal open since there is a rollback-style transaction in play
39963 ** used to convert the version numbers in the btree header.
39964 */
39965 if( isOpen(pPager->jfd) && eMode!=PAGER_JOURNALMODE_WAL ){
39966 sqlite3OsClose(pPager->jfd);
39967 }
39968
39969 /* Change the journal mode. */
 
39970 pPager->journalMode = (u8)eMode;
39971
39972 /* When transistioning from TRUNCATE or PERSIST to any other journal
39973 ** mode except WAL (and we are not in locking_mode=EXCLUSIVE) then
39974 ** delete the journal file.
39975 */
39976 assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
39977 assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
39978 assert( (PAGER_JOURNALMODE_DELETE & 5)==0 );
@@ -39989,28 +40589,34 @@
39989 **
39990 ** Before deleting the journal file, obtain a RESERVED lock on the
39991 ** database file. This ensures that the journal file is not deleted
39992 ** while it is in use by some other client.
39993 */
39994 int rc = SQLITE_OK;
39995 int state = pPager->state;
39996 if( state<PAGER_SHARED ){
39997 rc = sqlite3PagerSharedLock(pPager);
39998 }
39999 if( pPager->state==PAGER_SHARED ){
40000 assert( rc==SQLITE_OK );
40001 rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
40002 }
40003 if( rc==SQLITE_OK ){
40004 sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40005 }
40006 if( rc==SQLITE_OK && state==PAGER_SHARED ){
40007 sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
40008 }else if( state==PAGER_UNLOCK ){
40009 pager_unlock(pPager);
40010 }
40011 assert( state==pPager->state );
40012 }
40013 }
40014
40015 /* Return the new journal mode */
40016 return (int)pPager->journalMode;
@@ -40027,11 +40633,12 @@
40027 ** Return TRUE if the pager is in a state where it is OK to change the
40028 ** journalmode. Journalmode changes can only happen when the database
40029 ** is unmodified.
40030 */
40031 SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
40032 if( pPager->dbModified ) return 0;
 
40033 if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
40034 return 1;
40035 }
40036
40037 /*
@@ -40092,39 +40699,46 @@
40092 **
40093 ** If the pager passed as the first argument is open on a real database
40094 ** file (not a temp file or an in-memory database), and the WAL file
40095 ** is not already open, make an attempt to open it now. If successful,
40096 ** return SQLITE_OK. If an error occurs or the VFS used by the pager does
40097 ** not support the xShmXXX() methods, return an error code. *pisOpen is
40098 ** not modified in either case.
40099 **
40100 ** If the pager is open on a temp-file (or in-memory database), or if
40101 ** the WAL file is already open, set *pisOpen to 1 and return SQLITE_OK
40102 ** without doing anything.
40103 */
40104 SQLITE_PRIVATE int sqlite3PagerOpenWal(
40105 Pager *pPager, /* Pager object */
40106 int *pisOpen /* OUT: Set to true if call is a no-op */
40107 ){
40108 int rc = SQLITE_OK; /* Return code */
40109
40110 assert( pPager->state>=PAGER_SHARED );
40111 assert( (pisOpen==0 && !pPager->tempFile && !pPager->pWal) || *pisOpen==0 );
 
 
 
40112
40113 if( !pPager->tempFile && !pPager->pWal ){
40114 if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;
 
 
 
40115
40116 /* Open the connection to the log file. If this operation fails,
40117 ** (e.g. due to malloc() failure), unlock the database file and
40118 ** return an error code.
40119 */
40120 rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
40121 if( rc==SQLITE_OK ){
40122 pPager->journalMode = PAGER_JOURNALMODE_WAL;
 
40123 }
40124 }else{
40125 *pisOpen = 1;
40126 }
40127
40128 return rc;
40129 }
40130
@@ -40146,11 +40760,11 @@
40146 ** it may need to be checkpointed before the connection can switch to
40147 ** rollback mode. Open it now so this can happen.
40148 */
40149 if( !pPager->pWal ){
40150 int logexists = 0;
40151 rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED);
40152 if( rc==SQLITE_OK ){
40153 rc = sqlite3OsAccess(
40154 pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
40155 );
40156 }
@@ -40162,21 +40776,21 @@
40162
40163 /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
40164 ** the database file, the log and log-summary files will be deleted.
40165 */
40166 if( rc==SQLITE_OK && pPager->pWal ){
40167 rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE);
40168 if( rc==SQLITE_OK ){
40169 rc = sqlite3WalClose(pPager->pWal,
40170 (pPager->noSync ? 0 : pPager->sync_flags),
40171 pPager->pageSize, (u8*)pPager->pTmpSpace
40172 );
40173 pPager->pWal = 0;
40174 }else{
40175 /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
40176 ** that we did get back to SHARED. */
40177 sqlite3OsUnlock(pPager->fd, SQLITE_LOCK_SHARED);
40178 }
40179 }
40180 return rc;
40181 }
40182
@@ -40492,18 +41106,22 @@
40492 /*
40493 ** The following object holds a copy of the wal-index header content.
40494 **
40495 ** The actual header in the wal-index consists of two copies of this
40496 ** object.
 
 
 
 
40497 */
40498 struct WalIndexHdr {
40499 u32 iVersion; /* Wal-index version */
40500 u32 unused; /* Unused (padding) field */
40501 u32 iChange; /* Counter incremented each transaction */
40502 u8 isInit; /* 1 when initialized */
40503 u8 bigEndCksum; /* True if checksums in WAL are big-endian */
40504 u16 szPage; /* Database page size in bytes */
40505 u32 mxFrame; /* Index of last valid frame in the WAL */
40506 u32 nPage; /* Size of database in pages */
40507 u32 aFrameCksum[2]; /* Checksum of last frame in log */
40508 u32 aSalt[2]; /* Two salt values copied from WAL header */
40509 u32 aCksum[2]; /* Checksum over all prior fields */
@@ -40610,11 +41228,11 @@
40610 sqlite3_file *pDbFd; /* File handle for the database file */
40611 sqlite3_file *pWalFd; /* File handle for WAL file */
40612 u32 iCallback; /* Value to pass to log callback (or 0) */
40613 int nWiData; /* Size of array apWiData */
40614 volatile u32 **apWiData; /* Pointer to wal-index content in memory */
40615 u16 szPage; /* Database page size */
40616 i16 readLock; /* Which read lock is being held. -1 for none */
40617 u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
40618 u8 writeLock; /* True if in a write transaction */
40619 u8 ckptLock; /* True if holding a checkpoint lock */
40620 u8 readOnly; /* True if the WAL file is open read-only */
@@ -41281,11 +41899,11 @@
41281 || szPage<512
41282 ){
41283 goto finished;
41284 }
41285 pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
41286 pWal->szPage = (u16)szPage;
41287 pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
41288 memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
41289
41290 /* Verify that the WAL header checksum is correct */
41291 walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
@@ -41331,11 +41949,13 @@
41331
41332 /* If nTruncate is non-zero, this is a commit record. */
41333 if( nTruncate ){
41334 pWal->hdr.mxFrame = iFrame;
41335 pWal->hdr.nPage = nTruncate;
41336 pWal->hdr.szPage = (u16)szPage;
 
 
41337 aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
41338 aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
41339 }
41340 }
41341
@@ -41356,10 +41976,21 @@
41356 */
41357 pInfo = walCkptInfo(pWal);
41358 pInfo->nBackfill = 0;
41359 pInfo->aReadMark[0] = 0;
41360 for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
 
 
 
 
 
 
 
 
 
 
 
41361 }
41362
41363 recovery_error:
41364 WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
41365 walUnlockExclusive(pWal, iLock, nLock);
@@ -41716,18 +42347,22 @@
41716 int sync_flags, /* Flags for OsSync() (or 0) */
41717 int nBuf, /* Size of zBuf in bytes */
41718 u8 *zBuf /* Temporary buffer to use */
41719 ){
41720 int rc; /* Return code */
41721 int szPage = pWal->hdr.szPage; /* Database page-size */
41722 WalIterator *pIter = 0; /* Wal iterator context */
41723 u32 iDbpage = 0; /* Next database page to write */
41724 u32 iFrame = 0; /* Wal frame containing data for iDbpage */
41725 u32 mxSafeFrame; /* Max frame that can be backfilled */
 
41726 int i; /* Loop counter */
41727 volatile WalCkptInfo *pInfo; /* The checkpoint status information */
41728
 
 
 
41729 if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
41730
41731 /* Allocate the iterator */
41732 rc = walIteratorInit(pWal, &pIter);
41733 if( rc!=SQLITE_OK ){
@@ -41734,11 +42369,11 @@
41734 return rc;
41735 }
41736 assert( pIter );
41737
41738 /*** TODO: Move this test out to the caller. Make it an assert() here ***/
41739 if( pWal->hdr.szPage!=nBuf ){
41740 rc = SQLITE_CORRUPT_BKPT;
41741 goto walcheckpoint_out;
41742 }
41743
41744 /* Compute in mxSafeFrame the index of the last frame of the WAL that is
@@ -41745,10 +42380,11 @@
41745 ** safe to write into the database. Frames beyond mxSafeFrame might
41746 ** overwrite database pages that are in use by active readers and thus
41747 ** cannot be backfilled from the WAL.
41748 */
41749 mxSafeFrame = pWal->hdr.mxFrame;
 
41750 pInfo = walCkptInfo(pWal);
41751 for(i=1; i<WAL_NREADER; i++){
41752 u32 y = pInfo->aReadMark[i];
41753 if( mxSafeFrame>=y ){
41754 assert( y<=pWal->hdr.mxFrame );
@@ -41765,22 +42401,34 @@
41765 }
41766
41767 if( pInfo->nBackfill<mxSafeFrame
41768 && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
41769 ){
 
41770 u32 nBackfill = pInfo->nBackfill;
41771
41772 /* Sync the WAL to disk */
41773 if( sync_flags ){
41774 rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
41775 }
 
 
 
 
 
 
 
 
 
 
 
41776
41777 /* Iterate through the contents of the WAL, copying data to the db file. */
41778 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
41779 i64 iOffset;
41780 assert( walFramePgno(pWal, iFrame)==iDbpage );
41781 if( iFrame<=nBackfill || iFrame>mxSafeFrame ) continue;
41782 iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
41783 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
41784 rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
41785 if( rc!=SQLITE_OK ) break;
41786 iOffset = (iDbpage-1)*(i64)szPage;
@@ -41883,11 +42531,11 @@
41883 WalIndexHdr volatile *aHdr; /* Header in shared memory */
41884
41885 /* The first page of the wal-index must be mapped at this point. */
41886 assert( pWal->nWiData>0 && pWal->apWiData[0] );
41887
41888 /* Read the header. This might happen currently with a write to the
41889 ** same area of shared memory on a different CPU in a SMP,
41890 ** meaning it is possible that an inconsistent snapshot is read
41891 ** from the file. If this happens, return non-zero.
41892 **
41893 ** There are two copies of the header at the beginning of the wal-index.
@@ -41912,11 +42560,13 @@
41912 }
41913
41914 if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
41915 *pChanged = 1;
41916 memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
41917 pWal->szPage = pWal->hdr.szPage;
 
 
41918 }
41919
41920 /* The header was successfully read. Return zero. */
41921 return 0;
41922 }
@@ -42231,10 +42881,11 @@
42231 /*
42232 ** Finish with a read transaction. All this does is release the
42233 ** read-lock.
42234 */
42235 SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
 
42236 if( pWal->readLock>=0 ){
42237 walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
42238 pWal->readLock = -1;
42239 }
42240 }
@@ -42341,11 +42992,17 @@
42341
42342 /* If iRead is non-zero, then it is the log frame number that contains the
42343 ** required page. Read and return data from the log file.
42344 */
42345 if( iRead ){
42346 i64 iOffset = walFrameOffset(iRead, pWal->hdr.szPage) + WAL_FRAME_HDRSIZE;
 
 
 
 
 
 
42347 *pInWal = 1;
42348 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
42349 return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
42350 }
42351
@@ -42353,15 +43010,17 @@
42353 return SQLITE_OK;
42354 }
42355
42356
42357 /*
42358 ** Set *pPgno to the size of the database file (or zero, if unknown).
42359 */
42360 SQLITE_PRIVATE void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno){
42361 assert( pWal->readLock>=0 || pWal->lockError );
42362 *pPgno = pWal->hdr.nPage;
 
 
42363 }
42364
42365
42366 /*
42367 ** This function starts a write transaction on the WAL.
@@ -42433,11 +43092,11 @@
42433 ** Otherwise, if the callback function does not return an error, this
42434 ** function returns SQLITE_OK.
42435 */
42436 SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
42437 int rc = SQLITE_OK;
42438 if( pWal->writeLock ){
42439 Pgno iMax = pWal->hdr.mxFrame;
42440 Pgno iFrame;
42441
42442 /* Restore the clients cache of the wal-index header to the state it
42443 ** was in before the client began writing to the database.
@@ -42622,11 +43281,11 @@
42622 memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
42623 walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
42624 sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
42625 sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
42626
42627 pWal->szPage = (u16)szPage;
42628 pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
42629 pWal->hdr.aFrameCksum[0] = aCksum[0];
42630 pWal->hdr.aFrameCksum[1] = aCksum[1];
42631
42632 rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
@@ -42717,11 +43376,13 @@
42717 rc = walIndexAppend(pWal, iFrame, pLast->pgno);
42718 }
42719
42720 if( rc==SQLITE_OK ){
42721 /* Update the private copy of the header. */
42722 pWal->hdr.szPage = (u16)szPage;
 
 
42723 pWal->hdr.mxFrame = iFrame;
42724 if( isCommit ){
42725 pWal->hdr.iChange++;
42726 pWal->hdr.nPage = nTruncate;
42727 }
@@ -42929,11 +43590,11 @@
42929 **
42930 ** FORMAT DETAILS
42931 **
42932 ** The file is divided into pages. The first page is called page 1,
42933 ** the second is page 2, and so forth. A page number of zero indicates
42934 ** "no such page". The page size can be any power of 2 between 512 and 32768.
42935 ** Each page can be either a btree page, a freelist page, an overflow
42936 ** page, or a pointer-map page.
42937 **
42938 ** The first page is always a btree page. The first 100 bytes of the first
42939 ** page contain a special header (the "file header") that describes the file.
@@ -43295,18 +43956,18 @@
43295 u8 initiallyEmpty; /* Database is empty at start of transaction */
43296 #ifndef SQLITE_OMIT_AUTOVACUUM
43297 u8 autoVacuum; /* True if auto-vacuum is enabled */
43298 u8 incrVacuum; /* True if incr-vacuum is enabled */
43299 #endif
43300 u16 pageSize; /* Total number of bytes on a page */
43301 u16 usableSize; /* Number of usable bytes on each page */
43302 u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
43303 u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
43304 u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
43305 u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
43306 u8 inTransaction; /* Transaction state */
43307 u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
 
 
43308 int nTransaction; /* Number of open transactions (read + write) */
43309 u32 nPage; /* Number of pages in the database */
43310 void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
43311 void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
43312 sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
@@ -43901,11 +44562,20 @@
43901 # define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
43902 #else
43903 # define TRACE(X)
43904 #endif
43905
43906
 
 
 
 
 
 
 
 
 
43907
43908 #ifndef SQLITE_OMIT_SHARED_CACHE
43909 /*
43910 ** A list of BtShared objects that are eligible for participation
43911 ** in shared cache. This variable has file scope during normal builds,
@@ -44590,15 +45260,20 @@
44590 #ifndef SQLITE_OMIT_AUTOVACUUM
44591 /*
44592 ** Given a page number of a regular database page, return the page
44593 ** number for the pointer-map page that contains the entry for the
44594 ** input page number.
 
 
 
 
44595 */
44596 static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
44597 int nPagesPerMapPage;
44598 Pgno iPtrMap, ret;
44599 assert( sqlite3_mutex_held(pBt->mutex) );
 
44600 nPagesPerMapPage = (pBt->usableSize/5)+1;
44601 iPtrMap = (pgno-2)/nPagesPerMapPage;
44602 ret = (iPtrMap*nPagesPerMapPage) + 2;
44603 if( ret==PENDING_BYTE_PAGE(pBt) ){
44604 ret++;
@@ -45023,21 +45698,21 @@
45023 assert( nByte < usableSize-8 );
45024
45025 nFrag = data[hdr+7];
45026 assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
45027 gap = pPage->cellOffset + 2*pPage->nCell;
45028 top = get2byte(&data[hdr+5]);
45029 if( gap>top ) return SQLITE_CORRUPT_BKPT;
45030 testcase( gap+2==top );
45031 testcase( gap+1==top );
45032 testcase( gap==top );
45033
45034 if( nFrag>=60 ){
45035 /* Always defragment highly fragmented pages */
45036 rc = defragmentPage(pPage);
45037 if( rc ) return rc;
45038 top = get2byte(&data[hdr+5]);
45039 }else if( gap+2<=top ){
45040 /* Search the freelist looking for a free slot big enough to satisfy
45041 ** the request. The allocation is made from the first free slot in
45042 ** the list that is large enough to accomadate it.
45043 */
@@ -45075,11 +45750,11 @@
45075 */
45076 testcase( gap+2+nByte==top );
45077 if( gap+2+nByte>top ){
45078 rc = defragmentPage(pPage);
45079 if( rc ) return rc;
45080 top = get2byte(&data[hdr+5]);
45081 assert( gap+nByte<=top );
45082 }
45083
45084
45085 /* Allocate memory from the gap in between the cell pointer array
@@ -45241,28 +45916,28 @@
45241 if( !pPage->isInit ){
45242 u16 pc; /* Address of a freeblock within pPage->aData[] */
45243 u8 hdr; /* Offset to beginning of page header */
45244 u8 *data; /* Equal to pPage->aData */
45245 BtShared *pBt; /* The main btree structure */
45246 u16 usableSize; /* Amount of usable space on each page */
45247 u16 cellOffset; /* Offset from start of page to first cell pointer */
45248 u16 nFree; /* Number of unused bytes on the page */
45249 u16 top; /* First byte of the cell content area */
45250 int iCellFirst; /* First allowable cell or freeblock offset */
45251 int iCellLast; /* Last possible cell or freeblock offset */
45252
45253 pBt = pPage->pBt;
45254
45255 hdr = pPage->hdrOffset;
45256 data = pPage->aData;
45257 if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
45258 assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
45259 pPage->maskPage = pBt->pageSize - 1;
45260 pPage->nOverflow = 0;
45261 usableSize = pBt->usableSize;
45262 pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
45263 top = get2byte(&data[hdr+5]);
45264 pPage->nCell = get2byte(&data[hdr+3]);
45265 if( pPage->nCell>MX_CELL(pBt) ){
45266 /* To many cells for a single page. The page must be corrupt */
45267 return SQLITE_CORRUPT_BKPT;
45268 }
@@ -45357,17 +46032,17 @@
45357 data[hdr] = (char)flags;
45358 first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
45359 memset(&data[hdr+1], 0, 4);
45360 data[hdr+7] = 0;
45361 put2byte(&data[hdr+5], pBt->usableSize);
45362 pPage->nFree = pBt->usableSize - first;
45363 decodeFlags(pPage, flags);
45364 pPage->hdrOffset = hdr;
45365 pPage->cellOffset = first;
45366 pPage->nOverflow = 0;
45367 assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
45368 pPage->maskPage = pBt->pageSize - 1;
45369 pPage->nCell = 0;
45370 pPage->isInit = 1;
45371 }
45372
45373
@@ -45671,11 +46346,11 @@
45671 pBt->pPage1 = 0;
45672 pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
45673 #ifdef SQLITE_SECURE_DELETE
45674 pBt->secureDelete = 1;
45675 #endif
45676 pBt->pageSize = get2byte(&zDbHeader[16]);
45677 if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
45678 || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
45679 pBt->pageSize = 0;
45680 #ifndef SQLITE_OMIT_AUTOVACUUM
45681 /* If the magic name ":memory:" will create an in-memory database, then
@@ -45985,11 +46660,11 @@
45985 assert( nReserve>=0 && nReserve<=255 );
45986 if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
45987 ((pageSize-1)&pageSize)==0 ){
45988 assert( (pageSize & 7)==0 );
45989 assert( !pBt->pPage1 && !pBt->pCursor );
45990 pBt->pageSize = (u16)pageSize;
45991 freeTempSpace(pBt);
45992 }
45993 rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
45994 pBt->usableSize = pBt->pageSize - (u16)nReserve;
45995 if( iFix ) pBt->pageSizeFixed = 1;
@@ -46120,19 +46795,17 @@
46120
46121 /* Do some checking to help insure the file we opened really is
46122 ** a valid database file.
46123 */
46124 nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
46125 if( (rc = sqlite3PagerPagecount(pBt->pPager, &nPageFile))!=SQLITE_OK ){;
46126 goto page1_init_failed;
46127 }
46128 if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
46129 nPage = nPageFile;
46130 }
46131 if( nPage>0 ){
46132 int pageSize;
46133 int usableSize;
46134 u8 *page1 = pPage1->aData;
46135 rc = SQLITE_NOTADB;
46136 if( memcmp(page1, zMagicHeader, 16)!=0 ){
46137 goto page1_init_failed;
46138 }
@@ -46179,28 +46852,29 @@
46179 ** version 3.6.0, we require them to be fixed.
46180 */
46181 if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
46182 goto page1_init_failed;
46183 }
46184 pageSize = get2byte(&page1[16]);
46185 if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ||
46186 (SQLITE_MAX_PAGE_SIZE<32768 && pageSize>SQLITE_MAX_PAGE_SIZE)
 
46187 ){
46188 goto page1_init_failed;
46189 }
46190 assert( (pageSize & 7)==0 );
46191 usableSize = pageSize - page1[20];
46192 if( pageSize!=pBt->pageSize ){
46193 /* After reading the first page of the database assuming a page size
46194 ** of BtShared.pageSize, we have discovered that the page-size is
46195 ** actually pageSize. Unlock the database, leave pBt->pPage1 at
46196 ** zero and return SQLITE_OK. The caller will call this function
46197 ** again with the correct page-size.
46198 */
46199 releasePage(pPage1);
46200 pBt->usableSize = (u16)usableSize;
46201 pBt->pageSize = (u16)pageSize;
46202 freeTempSpace(pBt);
46203 rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
46204 pageSize-usableSize);
46205 return rc;
46206 }
@@ -46209,12 +46883,12 @@
46209 goto page1_init_failed;
46210 }
46211 if( usableSize<480 ){
46212 goto page1_init_failed;
46213 }
46214 pBt->pageSize = (u16)pageSize;
46215 pBt->usableSize = (u16)usableSize;
46216 #ifndef SQLITE_OMIT_AUTOVACUUM
46217 pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
46218 pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
46219 #endif
46220 }
@@ -46226,18 +46900,18 @@
46226 ** 2-byte pointer to the cell
46227 ** 4-byte child pointer
46228 ** 9-byte nKey value
46229 ** 4-byte nData value
46230 ** 4-byte overflow page pointer
46231 ** So a cell consists of a 2-byte poiner, a header which is as much as
46232 ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
46233 ** page pointer.
46234 */
46235 pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23;
46236 pBt->minLocal = (pBt->usableSize-12)*32/255 - 23;
46237 pBt->maxLeaf = pBt->usableSize - 35;
46238 pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23;
46239 assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
46240 pBt->pPage1 = pPage1;
46241 pBt->nPage = nPage;
46242 return SQLITE_OK;
46243
@@ -46286,11 +46960,12 @@
46286 data = pP1->aData;
46287 rc = sqlite3PagerWrite(pP1->pDbPage);
46288 if( rc ) return rc;
46289 memcpy(data, zMagicHeader, sizeof(zMagicHeader));
46290 assert( sizeof(zMagicHeader)==16 );
46291 put2byte(&data[16], pBt->pageSize);
 
46292 data[18] = 1;
46293 data[19] = 1;
46294 assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize);
46295 data[20] = (u8)(pBt->pageSize - pBt->usableSize);
46296 data[21] = 64;
@@ -48297,13 +48972,13 @@
48297 c = +1;
48298 }
48299 pCur->validNKey = 1;
48300 pCur->info.nKey = nCellKey;
48301 }else{
48302 /* The maximum supported page-size is 32768 bytes. This means that
48303 ** the maximum number of record bytes stored on an index B-Tree
48304 ** page is at most 8198 bytes, which may be stored as a 2-byte
48305 ** varint. This information is used to attempt to avoid parsing
48306 ** the entire cell by checking for the cases where the record is
48307 ** stored entirely within the b-tree page by inspecting the first
48308 ** 2 bytes of the cell.
48309 */
@@ -48662,10 +49337,14 @@
48662 }
48663 if( k==0 ){
48664 if( !pPrevTrunk ){
48665 memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
48666 }else{
 
 
 
 
48667 memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
48668 }
48669 }else{
48670 /* The trunk page is required by the caller but it contains
48671 ** pointers to free-list leaves. The first leaf becomes a trunk
@@ -48968,11 +49647,11 @@
48968 BtShared *pBt = pPage->pBt;
48969 CellInfo info;
48970 Pgno ovflPgno;
48971 int rc;
48972 int nOvfl;
48973 u16 ovflPageSize;
48974
48975 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
48976 btreeParseCellPtr(pPage, pCell, &info);
48977 if( info.iOverflow==0 ){
48978 return SQLITE_OK; /* No overflow pages. Return without doing anything */
@@ -49193,11 +49872,11 @@
49193 **
49194 ** "sz" must be the number of bytes in the cell.
49195 */
49196 static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
49197 int i; /* Loop counter */
49198 int pc; /* Offset to cell content of cell being deleted */
49199 u8 *data; /* pPage->aData */
49200 u8 *ptr; /* Used to move bytes around within data[] */
49201 int rc; /* The return code */
49202 int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
49203
@@ -49211,11 +49890,11 @@
49211 ptr = &data[pPage->cellOffset + 2*idx];
49212 pc = get2byte(ptr);
49213 hdr = pPage->hdrOffset;
49214 testcase( pc==get2byte(&data[hdr+5]) );
49215 testcase( pc+sz==pPage->pBt->usableSize );
49216 if( pc < get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
49217 *pRC = SQLITE_CORRUPT_BKPT;
49218 return;
49219 }
49220 rc = freeSpace(pPage, pc, sz);
49221 if( rc ){
@@ -49268,11 +49947,11 @@
49268 int nSkip = (iChild ? 4 : 0);
49269
49270 if( *pRC ) return;
49271
49272 assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
49273 assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
49274 assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
49275 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49276 /* The cell should normally be sized correctly. However, when moving a
49277 ** malformed cell from a leaf page to an interior page, if the cell size
49278 ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
@@ -49348,16 +50027,16 @@
49348 const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
49349 const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
49350
49351 assert( pPage->nOverflow==0 );
49352 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49353 assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
49354 assert( sqlite3PagerIswriteable(pPage->pDbPage) );
49355
49356 /* Check that the page has just been zeroed by zeroPage() */
49357 assert( pPage->nCell==0 );
49358 assert( get2byte(&data[hdr+5])==nUsable );
49359
49360 pCellptr = &data[pPage->cellOffset + nCell*2];
49361 cellbody = nUsable;
49362 for(i=nCell-1; i>=0; i--){
49363 pCellptr -= 2;
@@ -49419,10 +50098,11 @@
49419
49420 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49421 assert( sqlite3PagerIswriteable(pParent->pDbPage) );
49422 assert( pPage->nOverflow==1 );
49423
 
49424 if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
49425
49426 /* Allocate a new page. This page will become the right-sibling of
49427 ** pPage. Make the parent page writable, so that the new divider cell
49428 ** may be inserted. If both these operations are successful, proceed.
@@ -49748,11 +50428,11 @@
49748 ** In this case, temporarily copy the cell into the aOvflSpace[]
49749 ** buffer. It will be copied out again as soon as the aSpace[] buffer
49750 ** is allocated. */
49751 if( pBt->secureDelete ){
49752 int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
49753 if( (iOff+szNew[i])>pBt->usableSize ){
49754 rc = SQLITE_CORRUPT_BKPT;
49755 memset(apOld, 0, (i+1)*sizeof(MemPage*));
49756 goto balance_cleanup;
49757 }else{
49758 memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
@@ -49827,11 +50507,11 @@
49827 u8 *pTemp;
49828 assert( nCell<nMaxCells );
49829 szCell[nCell] = sz;
49830 pTemp = &aSpace1[iSpace1];
49831 iSpace1 += sz;
49832 assert( sz<=pBt->pageSize/4 );
49833 assert( iSpace1<=pBt->pageSize );
49834 memcpy(pTemp, apDiv[i], sz);
49835 apCell[nCell] = pTemp+leafCorrection;
49836 assert( leafCorrection==0 || leafCorrection==4 );
49837 szCell[nCell] = szCell[nCell] - leafCorrection;
@@ -50073,11 +50753,11 @@
50073 assert(leafCorrection==4);
50074 sz = cellSizePtr(pParent, pCell);
50075 }
50076 }
50077 iOvflSpace += sz;
50078 assert( sz<=pBt->pageSize/4 );
50079 assert( iOvflSpace<=pBt->pageSize );
50080 insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
50081 if( rc!=SQLITE_OK ) goto balance_cleanup;
50082 assert( sqlite3PagerIswriteable(pParent->pDbPage) );
50083
@@ -51317,11 +51997,11 @@
51317 #ifndef SQLITE_OMIT_AUTOVACUUM
51318 if( pCheck->pBt->autoVacuum ){
51319 checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
51320 }
51321 #endif
51322 if( n>pCheck->pBt->usableSize/4-2 ){
51323 checkAppendMsg(pCheck, zContext,
51324 "freelist leaf count too big on page %d", iPage);
51325 N--;
51326 }else{
51327 for(i=0; i<n; i++){
@@ -51528,24 +52208,24 @@
51528 hdr = pPage->hdrOffset;
51529 hit = sqlite3PageMalloc( pBt->pageSize );
51530 if( hit==0 ){
51531 pCheck->mallocFailed = 1;
51532 }else{
51533 u16 contentOffset = get2byte(&data[hdr+5]);
51534 assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
51535 memset(hit+contentOffset, 0, usableSize-contentOffset);
51536 memset(hit, 1, contentOffset);
51537 nCell = get2byte(&data[hdr+3]);
51538 cellStart = hdr + 12 - 4*pPage->leaf;
51539 for(i=0; i<nCell; i++){
51540 int pc = get2byte(&data[cellStart+i*2]);
51541 u16 size = 1024;
51542 int j;
51543 if( pc<=usableSize-4 ){
51544 size = cellSizePtr(pPage, &data[pc]);
51545 }
51546 if( (pc+size-1)>=usableSize ){
51547 checkAppendMsg(pCheck, 0,
51548 "Corruption detected in cell %d on page %d",i,iPage);
51549 }else{
51550 for(j=pc+size-1; j>=pc; j--) hit[j]++;
51551 }
@@ -52164,10 +52844,19 @@
52164 ** page sizes of the source and destination differ.
52165 */
52166 if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
52167 rc = SQLITE_READONLY;
52168 }
 
 
 
 
 
 
 
 
 
52169
52170 /* This loop runs once for each destination page spanned by the source
52171 ** page. For each iteration, variable iOff is set to the byte offset
52172 ** of the destination page.
52173 */
@@ -55769,12 +56458,21 @@
55769 mrc = p->rc & 0xff;
55770 assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
55771 isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
55772 || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
55773 if( isSpecialError ){
55774 /* If the query was read-only, we need do no rollback at all. Otherwise,
55775 ** proceed with the special handling.
 
 
 
 
 
 
 
 
 
55776 */
55777 if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
55778 if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
55779 eStatementOp = SAVEPOINT_ROLLBACK;
55780 }else{
@@ -63200,11 +63898,11 @@
63200 ** If P5 is non-zero then the key value is increased by an epsilon
63201 ** prior to the comparison. This make the opcode work like IdxGT except
63202 ** that if the key from register P3 is a prefix of the key in the cursor,
63203 ** the result is false whereas it would be true with IdxGT.
63204 */
63205 /* Opcode: IdxLT P1 P2 P3 * P5
63206 **
63207 ** The P4 register values beginning with P3 form an unpacked index
63208 ** key that omits the ROWID. Compare this key value against the index
63209 ** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
63210 **
@@ -67547,22 +68245,23 @@
67547 assert( z[0]=='?' );
67548 pExpr->iColumn = (ynVar)(++pParse->nVar);
67549 }else if( z[0]=='?' ){
67550 /* Wildcard of the form "?nnn". Convert "nnn" to an integer and
67551 ** use it as the variable number */
67552 int i = atoi((char*)&z[1]);
 
67553 pExpr->iColumn = (ynVar)i;
67554 testcase( i==0 );
67555 testcase( i==1 );
67556 testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
67557 testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
67558 if( i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
67559 sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
67560 db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
67561 }
67562 if( i>pParse->nVar ){
67563 pParse->nVar = i;
67564 }
67565 }else{
67566 /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
67567 ** number as the prior appearance of the same name, or if the name
67568 ** has never appeared before, reuse the same variable number
@@ -72092,10 +72791,11 @@
72092 }
72093 }
72094 sqlite3DbFree(db, pIdx->aSample);
72095 }
72096 #else
 
72097 UNUSED_PARAMETER(pIdx);
72098 #endif
72099 }
72100
72101 /*
@@ -87738,11 +88438,11 @@
87738 ** be sent.
87739 **
87740 ** regReturn is the number of the register holding the subroutine
87741 ** return address.
87742 **
87743 ** If regPrev>0 then it is a the first register in a vector that
87744 ** records the previous output. mem[regPrev] is a flag that is false
87745 ** if there has been no previous output. If regPrev>0 then code is
87746 ** generated to suppress duplicates. pKeyInfo is used for comparing
87747 ** keys.
87748 **
@@ -88435,16 +89135,17 @@
88435 ** (1) The subquery and the outer query do not both use aggregates.
88436 **
88437 ** (2) The subquery is not an aggregate or the outer query is not a join.
88438 **
88439 ** (3) The subquery is not the right operand of a left outer join
88440 ** (Originally ticket #306. Strenghtened by ticket #3300)
88441 **
88442 ** (4) The subquery is not DISTINCT or the outer query is not a join.
88443 **
88444 ** (5) The subquery is not DISTINCT or the outer query does not use
88445 ** aggregates.
 
88446 **
88447 ** (6) The subquery does not use aggregates or the outer query is not
88448 ** DISTINCT.
88449 **
88450 ** (7) The subquery has a FROM clause.
@@ -88460,13 +89161,13 @@
88460 ** (11) The subquery and the outer query do not both have ORDER BY clauses.
88461 **
88462 ** (**) Not implemented. Subsumed into restriction (3). Was previously
88463 ** a separate restriction deriving from ticket #350.
88464 **
88465 ** (13) The subquery and outer query do not both use LIMIT
88466 **
88467 ** (14) The subquery does not use OFFSET
88468 **
88469 ** (15) The outer query is not part of a compound select or the
88470 ** subquery does not have a LIMIT clause.
88471 ** (See ticket #2339 and ticket [02a8e81d44]).
88472 **
@@ -88553,13 +89254,13 @@
88553 if( pSub->pOffset ) return 0; /* Restriction (14) */
88554 if( p->pRightmost && pSub->pLimit ){
88555 return 0; /* Restriction (15) */
88556 }
88557 if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
88558 if( ((pSub->selFlags & SF_Distinct)!=0 || pSub->pLimit)
88559 && (pSrc->nSrc>1 || isAgg) ){ /* Restrictions (4)(5)(8)(9) */
88560 return 0;
88561 }
88562 if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
88563 return 0; /* Restriction (6) */
88564 }
88565 if( p->pOrderBy && pSub->pOrderBy ){
@@ -93225,11 +93926,11 @@
93225 pParse->pNewTable->aCol = 0;
93226 }
93227 db->pVTab = 0;
93228 }else{
93229 sqlite3Error(db, SQLITE_ERROR, zErr);
93230 sqlite3_free(zErr);
93231 rc = SQLITE_ERROR;
93232 }
93233 pParse->declareVtab = 0;
93234
93235 if( pParse->pVdbe ){
@@ -96859,39 +97560,39 @@
96859 **
96860 ** This case is also used when there are no WHERE clause
96861 ** constraints but an index is selected anyway, in order
96862 ** to force the output order to conform to an ORDER BY.
96863 */
96864 int aStartOp[] = {
96865 0,
96866 0,
96867 OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
96868 OP_Last, /* 3: (!start_constraints && startEq && bRev) */
96869 OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
96870 OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
96871 OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
96872 OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
96873 };
96874 int aEndOp[] = {
96875 OP_Noop, /* 0: (!end_constraints) */
96876 OP_IdxGE, /* 1: (end_constraints && !bRev) */
96877 OP_IdxLT /* 2: (end_constraints && bRev) */
96878 };
96879 int nEq = pLevel->plan.nEq;
96880 int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
96881 int regBase; /* Base register holding constraint values */
96882 int r1; /* Temp register */
96883 WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
96884 WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
96885 int startEq; /* True if range start uses ==, >= or <= */
96886 int endEq; /* True if range end uses ==, >= or <= */
96887 int start_constraints; /* Start of range is constrained */
96888 int nConstraint; /* Number of constraint terms */
96889 Index *pIdx; /* The index we will be using */
96890 int iIdxCur; /* The VDBE cursor for the index */
96891 int nExtraReg = 0; /* Number of extra registers needed */
96892 int op; /* Instruction opcode */
96893 char *zStartAff; /* Affinity for start of range constraint */
96894 char *zEndAff; /* Affinity for end of range constraint */
96895
96896 pIdx = pLevel->plan.u.pIdx;
96897 iIdxCur = pLevel->iIdxCur;
@@ -108720,11 +109421,11 @@
108720 ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following
108721 ** two forward declarations are for functions declared in these files
108722 ** used to retrieve the respective implementations.
108723 **
108724 ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
108725 ** to by the argument to point a the "simple" tokenizer implementation.
108726 ** Function ...PorterTokenizerModule() sets *pModule to point to the
108727 ** porter tokenizer/stemmer implementation.
108728 */
108729 SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
108730 SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
@@ -108922,11 +109623,11 @@
108922 ** is defined to accept an argument of type char, and always returns 0 for
108923 ** any values that fall outside of the range of the unsigned char type (i.e.
108924 ** negative values).
108925 */
108926 static int fts3isspace(char c){
108927 return (c&0x80)==0 ? isspace(c) : 0;
108928 }
108929
108930 /*
108931 ** Extract the next token from buffer z (length n) using the tokenizer
108932 ** and other information (column names etc.) in pParse. Create an Fts3Expr
@@ -111309,10 +112010,13 @@
111309
111310
111311 static int simpleDelim(simple_tokenizer *t, unsigned char c){
111312 return c<0x80 && t->delim[c];
111313 }
 
 
 
111314
111315 /*
111316 ** Create a new tokenizer instance.
111317 */
111318 static int simpleCreate(
@@ -111343,11 +112047,11 @@
111343 }
111344 } else {
111345 /* Mark non-alphanumeric ASCII characters as delimiters */
111346 int i;
111347 for(i=1; i<0x80; i++){
111348 t->delim[i] = !isalnum(i) ? -1 : 0;
111349 }
111350 }
111351
111352 *ppTokenizer = &t->base;
111353 return SQLITE_OK;
@@ -111449,11 +112153,11 @@
111449 for(i=0; i<n; i++){
111450 /* TODO(shess) This needs expansion to handle UTF-8
111451 ** case-insensitivity.
111452 */
111453 unsigned char ch = p[iStartOffset+i];
111454 c->pToken[i] = (char)(ch<0x80 ? tolower(ch) : ch);
111455 }
111456 *ppToken = c->pToken;
111457 *pnBytes = n;
111458 *piStartOffset = iStartOffset;
111459 *piEndOffset = c->iOffset;
@@ -116355,15 +117059,14 @@
116355 ** least desirable):
116356 **
116357 ** idxNum idxStr Strategy
116358 ** ------------------------------------------------
116359 ** 1 Unused Direct lookup by rowid.
116360 ** 2 See below R-tree query.
116361 ** 3 Unused Full table scan.
116362 ** ------------------------------------------------
116363 **
116364 ** If strategy 1 or 3 is used, then idxStr is not meaningful. If strategy
116365 ** 2 is used, idxStr is formatted to contain 2 bytes for each
116366 ** constraint used. The first two bytes of idxStr correspond to
116367 ** the constraint in sqlite3_index_info.aConstraintUsage[] with
116368 ** (argvIndex==1) etc.
116369 **
116370
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.7.2. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a one translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% are more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -213,24 +213,25 @@
213 */
214 #ifndef SQLITE_MAX_VARIABLE_NUMBER
215 # define SQLITE_MAX_VARIABLE_NUMBER 999
216 #endif
217
218 /* Maximum page size. The upper bound on this value is 65536. This a limit
219 ** imposed by the use of 16-bit offsets within each page.
 
220 **
221 ** Earlier versions of SQLite allowed the user to change this value at
222 ** compile time. This is no longer permitted, on the grounds that it creates
223 ** a library that is technically incompatible with an SQLite library
224 ** compiled with a different limit. If a process operating on a database
225 ** with a page-size of 65536 bytes crashes, then an instance of SQLite
226 ** compiled with the default page-size limit will not be able to rollback
227 ** the aborted transaction. This could lead to database corruption.
228 */
229 #ifdef SQLITE_MAX_PAGE_SIZE
230 # undef SQLITE_MAX_PAGE_SIZE
231 #endif
232 #define SQLITE_MAX_PAGE_SIZE 65536
233
234
235 /*
236 ** The default size of a database page.
237 */
@@ -631,23 +632,23 @@
632 ** be held constant and Z will be incremented or else Y will be incremented
633 ** and Z will be reset to zero.
634 **
635 ** Since version 3.6.18, SQLite source code has been stored in the
636 ** <a href="http://www.fossil-scm.org/">Fossil configuration management
637 ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
638 ** a string which identifies a particular check-in of SQLite
639 ** within its configuration management system. ^The SQLITE_SOURCE_ID
640 ** string contains the date and time of the check-in (UTC) and an SHA1
641 ** hash of the entire source tree.
642 **
643 ** See also: [sqlite3_libversion()],
644 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
645 ** [sqlite_version()] and [sqlite_source_id()].
646 */
647 #define SQLITE_VERSION "3.7.2"
648 #define SQLITE_VERSION_NUMBER 3007002
649 #define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3"
650
651 /*
652 ** CAPI3REF: Run-Time Library Version Numbers
653 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
654 **
@@ -688,19 +689,19 @@
689 ** ^The sqlite3_compileoption_used() function returns 0 or 1
690 ** indicating whether the specified option was defined at
691 ** compile time. ^The SQLITE_ prefix may be omitted from the
692 ** option name passed to sqlite3_compileoption_used().
693 **
694 ** ^The sqlite3_compileoption_get() function allows iterating
695 ** over the list of options that were defined at compile time by
696 ** returning the N-th compile time option string. ^If N is out of range,
697 ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
698 ** prefix is omitted from any strings returned by
699 ** sqlite3_compileoption_get().
700 **
701 ** ^Support for the diagnostic functions sqlite3_compileoption_used()
702 ** and sqlite3_compileoption_get() may be omitted by specifying the
703 ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
704 **
705 ** See also: SQL functions [sqlite_compileoption_used()] and
706 ** [sqlite_compileoption_get()] and the [compile_options pragma].
707 */
@@ -802,11 +803,11 @@
803 /*
804 ** CAPI3REF: Closing A Database Connection
805 **
806 ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
807 ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
808 ** successfully destroyed and all associated resources are deallocated.
809 **
810 ** Applications must [sqlite3_finalize | finalize] all [prepared statements]
811 ** and [sqlite3_blob_close | close] all [BLOB handles] associated with
812 ** the [sqlite3] object prior to attempting to close the object. ^If
813 ** sqlite3_close() is called on a [database connection] that still has
@@ -1229,16 +1230,25 @@
1230 ** layer a hint of how large the database file will grow to be during the
1231 ** current transaction. This hint is not guaranteed to be accurate but it
1232 ** is often close. The underlying VFS might choose to preallocate database
1233 ** file space based on this hint in order to help writes to the database
1234 ** file run faster.
1235 **
1236 ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
1237 ** extends and truncates the database file in chunks of a size specified
1238 ** by the user. The fourth argument to [sqlite3_file_control()] should
1239 ** point to an integer (type int) containing the new chunk-size to use
1240 ** for the nominated database. Allocating database file space in large
1241 ** chunks (say 1MB at a time), may reduce file-system fragmentation and
1242 ** improve performance on some systems.
1243 */
1244 #define SQLITE_FCNTL_LOCKSTATE 1
1245 #define SQLITE_GET_LOCKPROXYFILE 2
1246 #define SQLITE_SET_LOCKPROXYFILE 3
1247 #define SQLITE_LAST_ERRNO 4
1248 #define SQLITE_FCNTL_SIZE_HINT 5
1249 #define SQLITE_FCNTL_CHUNK_SIZE 6
1250
1251 /*
1252 ** CAPI3REF: Mutex Handle
1253 **
1254 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -3197,11 +3207,11 @@
3207 ** <li> @VVV
3208 ** <li> $VVV
3209 ** </ul>
3210 **
3211 ** In the templates above, NNN represents an integer literal,
3212 ** and VVV represents an alphanumeric identifier.)^ ^The values of these
3213 ** parameters (also called "host parameter names" or "SQL parameters")
3214 ** can be set using the sqlite3_bind_*() routines defined here.
3215 **
3216 ** ^The first argument to the sqlite3_bind_*() routines is always
3217 ** a pointer to the [sqlite3_stmt] object returned from
@@ -3976,11 +3986,11 @@
3986 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
3987
3988 /*
3989 ** CAPI3REF: Obtain Aggregate Function Context
3990 **
3991 ** Implementations of aggregate SQL functions use this
3992 ** routine to allocate memory for storing their state.
3993 **
3994 ** ^The first time the sqlite3_aggregate_context(C,N) routine is called
3995 ** for a particular aggregate function, SQLite
3996 ** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -4248,11 +4258,11 @@
4258 ** the routine expects pointers to 16-bit word aligned strings
4259 ** of UTF-16 in the native byte order.
4260 **
4261 ** A pointer to the user supplied routine must be passed as the fifth
4262 ** argument. ^If it is NULL, this is the same as deleting the collation
4263 ** sequence (so that SQLite cannot call it any more).
4264 ** ^Each time the application supplied function is invoked, it is passed
4265 ** as its first parameter a copy of the void* passed as the fourth argument
4266 ** to sqlite3_create_collation() or sqlite3_create_collation16().
4267 **
4268 ** ^The remaining arguments to the application-supplied routine are two strings,
@@ -5466,11 +5476,11 @@
5476 ** of passing a NULL pointer instead of a valid mutex handle are undefined
5477 ** (i.e. it is acceptable to provide an implementation that segfaults if
5478 ** it is passed a NULL pointer).
5479 **
5480 ** The xMutexInit() method must be threadsafe. ^It must be harmless to
5481 ** invoke xMutexInit() multiple times within the same process and without
5482 ** intervening calls to xMutexEnd(). Second and subsequent calls to
5483 ** xMutexInit() must be no-ops.
5484 **
5485 ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
5486 ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5636,11 +5646,11 @@
5646
5647 /*
5648 ** CAPI3REF: SQLite Runtime Status
5649 **
5650 ** ^This interface is used to retrieve runtime status information
5651 ** about the performance of SQLite, and optionally to reset various
5652 ** highwater marks. ^The first argument is an integer code for
5653 ** the specific parameter to measure. ^(Recognized integer codes
5654 ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
5655 ** ^The current value of the parameter is returned into *pCurrent.
5656 ** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5762,11 +5772,11 @@
5772 ** ^This interface is used to retrieve runtime status information
5773 ** about a single [database connection]. ^The first argument is the
5774 ** database connection object to be interrogated. ^The second argument
5775 ** is an integer constant, taken from the set of
5776 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5777 ** determines the parameter to interrogate. The set of
5778 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
5779 ** to grow in future releases of SQLite.
5780 **
5781 ** ^The current value of the requested parameter is written into *pCur
5782 ** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -6184,11 +6194,11 @@
6194 **
6195 ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
6196 **
6197 ** ^Each call to sqlite3_backup_step() sets two values inside
6198 ** the [sqlite3_backup] object: the number of pages still to be backed
6199 ** up and the total number of pages in the source database file.
6200 ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
6201 ** retrieve these two values, respectively.
6202 **
6203 ** ^The values returned by these functions are only updated by
6204 ** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -6280,11 +6290,11 @@
6290 ** ^(There may be at most one unlock-notify callback registered by a
6291 ** blocked connection. If sqlite3_unlock_notify() is called when the
6292 ** blocked connection already has a registered unlock-notify callback,
6293 ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
6294 ** called with a NULL pointer as its second argument, then any existing
6295 ** unlock-notify callback is canceled. ^The blocked connections
6296 ** unlock-notify callback may also be canceled by closing the blocked
6297 ** connection using [sqlite3_close()].
6298 **
6299 ** The unlock-notify callback is not reentrant. If an application invokes
6300 ** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -6362,11 +6372,11 @@
6372 /*
6373 ** CAPI3REF: String Comparison
6374 **
6375 ** ^The [sqlite3_strnicmp()] API allows applications and extensions to
6376 ** compare the contents of two buffers containing UTF-8 strings in a
6377 ** case-independent fashion, using the same definition of case independence
6378 ** that SQLite uses internally when comparing identifiers.
6379 */
6380 SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
6381
6382 /*
@@ -7868,11 +7878,11 @@
7878 SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager);
7879 SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
7880
7881 /* Functions used to configure a Pager object. */
7882 SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
7883 SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
7884 SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
7885 SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
7886 SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int);
7887 SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
7888 SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
@@ -7895,11 +7905,11 @@
7905 SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*);
7906 SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *);
7907 SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *);
7908
7909 /* Functions used to manage pager transactions and savepoints. */
7910 SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*);
7911 SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
7912 SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
7913 SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
7914 SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager);
7915 SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
@@ -9183,13 +9193,13 @@
9193 ** argument to sqlite3VdbeKeyCompare and is used to control the
9194 ** comparison of the two index keys.
9195 */
9196 struct KeyInfo {
9197 sqlite3 *db; /* The database connection */
9198 u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
9199 u16 nField; /* Number of entries in aColl[] */
9200 u8 *aSortOrder; /* Sort order for each column. May be NULL */
9201 CollSeq *aColl[1]; /* Collating sequence for each term of the key */
9202 };
9203
9204 /*
9205 ** An instance of the following structure holds information about a
@@ -14331,11 +14341,11 @@
14341
14342 /*
14343 ** Set the "type" of an allocation.
14344 */
14345 SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
14346 if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
14347 struct MemBlockHdr *pHdr;
14348 pHdr = sqlite3MemsysGetHeader(p);
14349 assert( pHdr->iForeGuard==FOREGUARD );
14350 pHdr->eType = eType;
14351 }
@@ -14350,11 +14360,11 @@
14360 **
14361 ** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
14362 */
14363 SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
14364 int rc = 1;
14365 if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
14366 struct MemBlockHdr *pHdr;
14367 pHdr = sqlite3MemsysGetHeader(p);
14368 assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
14369 if( (pHdr->eType&eType)==0 ){
14370 rc = 0;
@@ -14372,11 +14382,11 @@
14382 **
14383 ** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
14384 */
14385 SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
14386 int rc = 1;
14387 if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
14388 struct MemBlockHdr *pHdr;
14389 pHdr = sqlite3MemsysGetHeader(p);
14390 assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
14391 if( (pHdr->eType&eType)!=0 ){
14392 rc = 0;
@@ -16624,10 +16634,11 @@
16634 #else
16635 /* Use the built-in recursive mutexes if they are available.
16636 */
16637 pthread_mutex_lock(&p->mutex);
16638 #if SQLITE_MUTEX_NREF
16639 assert( p->nRef>0 || p->owner==0 );
16640 p->owner = pthread_self();
16641 p->nRef++;
16642 #endif
16643 #endif
16644
@@ -16696,10 +16707,11 @@
16707 */
16708 static void pthreadMutexLeave(sqlite3_mutex *p){
16709 assert( pthreadMutexHeld(p) );
16710 #if SQLITE_MUTEX_NREF
16711 p->nRef--;
16712 if( p->nRef==0 ) p->owner = 0;
16713 #endif
16714 assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
16715
16716 #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
16717 if( p->nRef==0 ){
@@ -16960,11 +16972,11 @@
16972 ** allocated mutex. SQLite is careful to deallocate every
16973 ** mutex that it allocates.
16974 */
16975 static void winMutexFree(sqlite3_mutex *p){
16976 assert( p );
16977 assert( p->nRef==0 && p->owner==0 );
16978 assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
16979 DeleteCriticalSection(&p->mutex);
16980 sqlite3_free(p);
16981 }
16982
@@ -16984,10 +16996,11 @@
16996 DWORD tid = GetCurrentThreadId();
16997 assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
16998 #endif
16999 EnterCriticalSection(&p->mutex);
17000 #ifdef SQLITE_DEBUG
17001 assert( p->nRef>0 || p->owner==0 );
17002 p->owner = tid;
17003 p->nRef++;
17004 if( p->trace ){
17005 printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
17006 }
@@ -17037,10 +17050,11 @@
17050 #ifndef NDEBUG
17051 DWORD tid = GetCurrentThreadId();
17052 assert( p->nRef>0 );
17053 assert( p->owner==tid );
17054 p->nRef--;
17055 if( p->nRef==0 ) p->owner = 0;
17056 assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
17057 #endif
17058 LeaveCriticalSection(&p->mutex);
17059 #ifdef SQLITE_DEBUG
17060 if( p->trace ){
@@ -22609,10 +22623,11 @@
22623 void *lockingContext; /* Locking style specific state */
22624 UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
22625 int fileFlags; /* Miscellanous flags */
22626 const char *zPath; /* Name of the file */
22627 unixShm *pShm; /* Shared memory segment information */
22628 int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
22629 #if SQLITE_ENABLE_LOCKING_STYLE
22630 int openFlags; /* The flags specified at open() */
22631 #endif
22632 #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
22633 unsigned fsFlags; /* cached details from statfs() */
@@ -25367,19 +25382,21 @@
25382 offset += wrote;
25383 pBuf = &((char*)pBuf)[wrote];
25384 }
25385 SimulateIOError(( wrote=(-1), amt=1 ));
25386 SimulateDiskfullError(( wrote=0, amt=1 ));
25387
25388 if( amt>0 ){
25389 if( wrote<0 ){
25390 /* lastErrno set by seekAndWrite */
25391 return SQLITE_IOERR_WRITE;
25392 }else{
25393 pFile->lastErrno = 0; /* not a system error */
25394 return SQLITE_FULL;
25395 }
25396 }
25397
25398 return SQLITE_OK;
25399 }
25400
25401 #ifdef SQLITE_TEST
25402 /*
@@ -25577,16 +25594,27 @@
25594
25595 /*
25596 ** Truncate an open file to a specified size
25597 */
25598 static int unixTruncate(sqlite3_file *id, i64 nByte){
25599 unixFile *pFile = (unixFile *)id;
25600 int rc;
25601 assert( pFile );
25602 SimulateIOError( return SQLITE_IOERR_TRUNCATE );
25603
25604 /* If the user has configured a chunk-size for this file, truncate the
25605 ** file so that it consists of an integer number of chunks (i.e. the
25606 ** actual file size after the operation may be larger than the requested
25607 ** size).
25608 */
25609 if( pFile->szChunk ){
25610 nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
25611 }
25612
25613 rc = ftruncate(pFile->h, (off_t)nByte);
25614 if( rc ){
25615 pFile->lastErrno = errno;
25616 return SQLITE_IOERR_TRUNCATE;
25617 }else{
25618 #ifndef NDEBUG
25619 /* If we are doing a normal write to a database file (as opposed to
25620 ** doing a hot-journal rollback or a write to some file other than a
@@ -25593,12 +25621,12 @@
25621 ** normal database file) and we truncate the file to zero length,
25622 ** that effectively updates the change counter. This might happen
25623 ** when restoring a database using the backup API from a zero-length
25624 ** source.
25625 */
25626 if( pFile->inNormalWrite && nByte==0 ){
25627 pFile->transCntrChng = 1;
25628 }
25629 #endif
25630
25631 return SQLITE_OK;
25632 }
@@ -25637,10 +25665,58 @@
25665 ** proxying locking division.
25666 */
25667 static int proxyFileControl(sqlite3_file*,int,void*);
25668 #endif
25669
25670 /*
25671 ** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
25672 ** file-control operation.
25673 **
25674 ** If the user has configured a chunk-size for this file, it could be
25675 ** that the file needs to be extended at this point. Otherwise, the
25676 ** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
25677 */
25678 static int fcntlSizeHint(unixFile *pFile, i64 nByte){
25679 if( pFile->szChunk ){
25680 i64 nSize; /* Required file size */
25681 struct stat buf; /* Used to hold return values of fstat() */
25682
25683 if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
25684
25685 nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
25686 if( nSize>(i64)buf.st_size ){
25687 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
25688 if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){
25689 return SQLITE_IOERR_WRITE;
25690 }
25691 #else
25692 /* If the OS does not have posix_fallocate(), fake it. First use
25693 ** ftruncate() to set the file size, then write a single byte to
25694 ** the last byte in each block within the extended region. This
25695 ** is the same technique used by glibc to implement posix_fallocate()
25696 ** on systems that do not have a real fallocate() system call.
25697 */
25698 int nBlk = buf.st_blksize; /* File-system block size */
25699 i64 iWrite; /* Next offset to write to */
25700 int nWrite; /* Return value from seekAndWrite() */
25701
25702 if( ftruncate(pFile->h, nSize) ){
25703 pFile->lastErrno = errno;
25704 return SQLITE_IOERR_TRUNCATE;
25705 }
25706 iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
25707 do {
25708 nWrite = seekAndWrite(pFile, iWrite, "", 1);
25709 iWrite += nBlk;
25710 } while( nWrite==1 && iWrite<nSize );
25711 if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
25712 #endif
25713 }
25714 }
25715
25716 return SQLITE_OK;
25717 }
25718
25719 /*
25720 ** Information and control of an open file handle.
25721 */
25722 static int unixFileControl(sqlite3_file *id, int op, void *pArg){
@@ -25650,18 +25726,17 @@
25726 return SQLITE_OK;
25727 }
25728 case SQLITE_LAST_ERRNO: {
25729 *(int*)pArg = ((unixFile*)id)->lastErrno;
25730 return SQLITE_OK;
25731 }
25732 case SQLITE_FCNTL_CHUNK_SIZE: {
25733 ((unixFile*)id)->szChunk = *(int *)pArg;
25734 return SQLITE_OK;
25735 }
25736 case SQLITE_FCNTL_SIZE_HINT: {
25737 return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
 
 
 
 
 
25738 }
25739 #ifndef NDEBUG
25740 /* The pager calls this method to signal that it has done
25741 ** a rollback and that the database is therefore unchanged and
25742 ** it hence it is OK for the transaction change counter to be
@@ -28522,11 +28597,11 @@
28597 }else{
28598 if( pCtx->conchFile ){
28599 pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
28600 sqlite3_free(pCtx->conchFile);
28601 }
28602 sqlite3DbFree(0, pCtx->lockProxyPath);
28603 sqlite3_free(pCtx->conchFilePath);
28604 sqlite3_free(pCtx);
28605 }
28606 OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
28607 (rc==SQLITE_OK ? "ok" : "failed")));
@@ -28713,13 +28788,13 @@
28788 }
28789 rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
28790 if( rc ) return rc;
28791 sqlite3_free(conchFile);
28792 }
28793 sqlite3DbFree(0, pCtx->lockProxyPath);
28794 sqlite3_free(pCtx->conchFilePath);
28795 sqlite3DbFree(0, pCtx->dbPath);
28796 /* restore the original locking context and pMethod then close it */
28797 pFile->lockingContext = pCtx->oldLockingContext;
28798 pFile->pMethod = pCtx->pOldMethod;
28799 sqlite3_free(pCtx);
28800 return pFile->pMethod->xClose(id);
@@ -29161,10 +29236,11 @@
29236 short sharedLockByte; /* Randomly chosen byte used as a shared lock */
29237 DWORD lastErrno; /* The Windows errno from the last I/O error */
29238 DWORD sectorSize; /* Sector size of the device file is on */
29239 winShm *pShm; /* Instance of shared memory on this file */
29240 const char *zPath; /* Full pathname of this file */
29241 int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
29242 #if SQLITE_OS_WINCE
29243 WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
29244 HANDLE hMutex; /* Mutex used to control access to shared lock */
29245 HANDLE hShared; /* Shared memory segment used for locking */
29246 winceLock local; /* Locks obtained by this instance of winFile */
@@ -29671,10 +29747,46 @@
29747
29748 /*****************************************************************************
29749 ** The next group of routines implement the I/O methods specified
29750 ** by the sqlite3_io_methods object.
29751 ******************************************************************************/
29752
29753 /*
29754 ** Some microsoft compilers lack this definition.
29755 */
29756 #ifndef INVALID_SET_FILE_POINTER
29757 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
29758 #endif
29759
29760 /*
29761 ** Move the current position of the file handle passed as the first
29762 ** argument to offset iOffset within the file. If successful, return 0.
29763 ** Otherwise, set pFile->lastErrno and return non-zero.
29764 */
29765 static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
29766 LONG upperBits; /* Most sig. 32 bits of new offset */
29767 LONG lowerBits; /* Least sig. 32 bits of new offset */
29768 DWORD dwRet; /* Value returned by SetFilePointer() */
29769
29770 upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
29771 lowerBits = (LONG)(iOffset & 0xffffffff);
29772
29773 /* API oddity: If successful, SetFilePointer() returns a dword
29774 ** containing the lower 32-bits of the new file-offset. Or, if it fails,
29775 ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
29776 ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
29777 ** whether an error has actually occured, it is also necessary to call
29778 ** GetLastError().
29779 */
29780 dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
29781 if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
29782 pFile->lastErrno = GetLastError();
29783 return 1;
29784 }
29785
29786 return 0;
29787 }
29788
29789 /*
29790 ** Close a file.
29791 **
29792 ** It is reported that an attempt to close a handle might sometimes
@@ -29714,17 +29826,10 @@
29826 OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
29827 OpenCounter(-1);
29828 return rc ? SQLITE_OK : SQLITE_IOERR;
29829 }
29830
 
 
 
 
 
 
 
29831 /*
29832 ** Read data from a file into a buffer. Return SQLITE_OK if all
29833 ** bytes were read successfully and SQLITE_IOERR if anything goes
29834 ** wrong.
29835 */
@@ -29732,112 +29837,108 @@
29837 sqlite3_file *id, /* File to read from */
29838 void *pBuf, /* Write content into this buffer */
29839 int amt, /* Number of bytes to read */
29840 sqlite3_int64 offset /* Begin reading at this offset */
29841 ){
29842 winFile *pFile = (winFile*)id; /* file handle */
29843 DWORD nRead; /* Number of bytes actually read from file */
 
 
 
 
29844
29845 assert( id!=0 );
29846 SimulateIOError(return SQLITE_IOERR_READ);
29847 OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
29848
29849 if( seekWinFile(pFile, offset) ){
 
29850 return SQLITE_FULL;
29851 }
29852 if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
29853 pFile->lastErrno = GetLastError();
29854 return SQLITE_IOERR_READ;
29855 }
29856 if( nRead<(DWORD)amt ){
 
 
29857 /* Unread parts of the buffer must be zero-filled */
29858 memset(&((char*)pBuf)[nRead], 0, amt-nRead);
29859 return SQLITE_IOERR_SHORT_READ;
29860 }
29861
29862 return SQLITE_OK;
29863 }
29864
29865 /*
29866 ** Write data from a buffer into a file. Return SQLITE_OK on success
29867 ** or some other error code on failure.
29868 */
29869 static int winWrite(
29870 sqlite3_file *id, /* File to write into */
29871 const void *pBuf, /* The bytes to be written */
29872 int amt, /* Number of bytes to write */
29873 sqlite3_int64 offset /* Offset into the file to begin writing at */
29874 ){
29875 int rc; /* True if error has occured, else false */
29876 winFile *pFile = (winFile*)id; /* File handle */
29877
29878 assert( amt>0 );
29879 assert( pFile );
 
 
 
29880 SimulateIOError(return SQLITE_IOERR_WRITE);
29881 SimulateDiskfullError(return SQLITE_FULL);
29882
29883 OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
29884
29885 rc = seekWinFile(pFile, offset);
29886 if( rc==0 ){
29887 u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
29888 int nRem = amt; /* Number of bytes yet to be written */
29889 DWORD nWrite; /* Bytes written by each WriteFile() call */
29890
29891 while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
29892 aRem += nWrite;
29893 nRem -= nWrite;
29894 }
29895 if( nRem>0 ){
29896 pFile->lastErrno = GetLastError();
29897 rc = 1;
29898 }
29899 }
29900
29901 if( rc ){
29902 if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){
29903 return SQLITE_FULL;
29904 }
29905 return SQLITE_IOERR_WRITE;
 
 
29906 }
29907 return SQLITE_OK;
29908 }
29909
29910 /*
29911 ** Truncate an open file to a specified size
29912 */
29913 static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
29914 winFile *pFile = (winFile*)id; /* File handle object */
29915 int rc = SQLITE_OK; /* Return code for this function */
29916
29917 assert( pFile );
29918
 
 
 
29919 OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
29920 SimulateIOError(return SQLITE_IOERR_TRUNCATE);
29921
29922 /* If the user has configured a chunk-size for this file, truncate the
29923 ** file so that it consists of an integer number of chunks (i.e. the
29924 ** actual file size after the operation may be larger than the requested
29925 ** size).
29926 */
29927 if( pFile->szChunk ){
29928 nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
29929 }
29930
29931 /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
29932 if( seekWinFile(pFile, nByte) ){
29933 rc = SQLITE_IOERR_TRUNCATE;
29934 }else if( 0==SetEndOfFile(pFile->h) ){
 
29935 pFile->lastErrno = GetLastError();
29936 rc = SQLITE_IOERR_TRUNCATE;
29937 }
29938
29939 OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
29940 return rc;
29941 }
29942
29943 #ifdef SQLITE_TEST
29944 /*
@@ -30197,10 +30298,14 @@
30298 return SQLITE_OK;
30299 }
30300 case SQLITE_LAST_ERRNO: {
30301 *(int*)pArg = (int)((winFile*)id)->lastErrno;
30302 return SQLITE_OK;
30303 }
30304 case SQLITE_FCNTL_CHUNK_SIZE: {
30305 ((winFile*)id)->szChunk = *(int *)pArg;
30306 return SQLITE_OK;
30307 }
30308 case SQLITE_FCNTL_SIZE_HINT: {
30309 sqlite3_int64 sz = *(sqlite3_int64*)pArg;
30310 SimulateIOErrorBenign(1);
30311 winTruncate(id, sz);
@@ -31749,11 +31854,11 @@
31854 ** start of a transaction, and is thus usually less than a few thousand,
31855 ** but can be as large as 2 billion for a really big database.
31856 */
31857
31858 /* Size of the Bitvec structure in bytes. */
31859 #define BITVEC_SZ 512
31860
31861 /* Round the union size down to the nearest pointer boundary, since that's how
31862 ** it will be aligned within the Bitvec struct. */
31863 #define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
31864
@@ -32907,10 +33012,29 @@
33012 sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
33013 sqlite3_free(p);
33014 }
33015 }
33016
33017 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
33018 /*
33019 ** Return the size of a pache allocation
33020 */
33021 static int pcache1MemSize(void *p){
33022 assert( sqlite3_mutex_held(pcache1.mutex) );
33023 if( p>=pcache1.pStart && p<pcache1.pEnd ){
33024 return pcache1.szSlot;
33025 }else{
33026 int iSize;
33027 assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
33028 sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
33029 iSize = sqlite3MallocSize(p);
33030 sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
33031 return iSize;
33032 }
33033 }
33034 #endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
33035
33036 /*
33037 ** Allocate a new page object initially associated with cache pCache.
33038 */
33039 static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
33040 int nByte = sizeof(PgHdr1) + pCache->szPage;
@@ -33455,11 +33579,11 @@
33579 int nFree = 0;
33580 if( pcache1.pStart==0 ){
33581 PgHdr1 *p;
33582 pcache1EnterMutex();
33583 while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
33584 nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
33585 pcache1PinPage(p);
33586 pcache1RemoveFromHash(p);
33587 pcache1FreePage(p);
33588 }
33589 pcache1LeaveMutex();
@@ -33964,11 +34088,11 @@
34088 # define sqlite3WalOpen(x,y,z) 0
34089 # define sqlite3WalClose(w,x,y,z) 0
34090 # define sqlite3WalBeginReadTransaction(y,z) 0
34091 # define sqlite3WalEndReadTransaction(z)
34092 # define sqlite3WalRead(v,w,x,y,z) 0
34093 # define sqlite3WalDbsize(y) 0
34094 # define sqlite3WalBeginWriteTransaction(y) 0
34095 # define sqlite3WalEndWriteTransaction(x) 0
34096 # define sqlite3WalUndo(x,y,z) 0
34097 # define sqlite3WalSavepoint(y,z)
34098 # define sqlite3WalSavepointUndo(y,z) 0
@@ -34000,13 +34124,12 @@
34124 SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal);
34125
34126 /* Read a page from the write-ahead log, if it is present. */
34127 SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
34128
34129 /* If the WAL is not empty, return the size of the database. */
34130 SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal);
 
34131
34132 /* Obtain or release the WRITER lock. */
34133 SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal);
34134 SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal);
34135
@@ -34048,12 +34171,16 @@
34171 #endif /* _WAL_H_ */
34172
34173 /************** End of wal.h *************************************************/
34174 /************** Continuing where we left off in pager.c **********************/
34175
34176
34177 /******************* NOTES ON THE DESIGN OF THE PAGER ************************
34178 **
34179 ** This comment block describes invariants that hold when using a rollback
34180 ** journal. These invariants do not apply for journal_mode=WAL,
34181 ** journal_mode=MEMORY, or journal_mode=OFF.
34182 **
34183 ** Within this comment block, a page is deemed to have been synced
34184 ** automatically as soon as it is written when PRAGMA synchronous=OFF.
34185 ** Otherwise, the page is not synced until the xSync method of the VFS
34186 ** is called successfully on the file containing the page.
@@ -34083,11 +34210,11 @@
34210 ** both the content in the database when the rollback journal was written
34211 ** and the content in the database at the beginning of the current
34212 ** transaction.
34213 **
34214 ** (3) Writes to the database file are an integer multiple of the page size
34215 ** in length and are aligned on a page boundary.
34216 **
34217 ** (4) Reads from the database file are either aligned on a page boundary and
34218 ** an integer multiple of the page size in length or are taken from the
34219 ** first 100 bytes of the database file.
34220 **
@@ -34114,11 +34241,12 @@
34241 ** method is a no-op, but that does not change the fact the SQLite will
34242 ** invoke it.)
34243 **
34244 ** (9) Whenever the database file is modified, at least one bit in the range
34245 ** of bytes from 24 through 39 inclusive will be changed prior to releasing
34246 ** the EXCLUSIVE lock, thus signaling other connections on the same
34247 ** database to flush their caches.
34248 **
34249 ** (10) The pattern of bits in bytes 24 through 39 shall not repeat in less
34250 ** than one billion transactions.
34251 **
34252 ** (11) A database file is well-formed at the beginning and at the conclusion
@@ -34127,11 +34255,12 @@
34255 ** (12) An EXCLUSIVE lock is held on the database file when writing to
34256 ** the database file.
34257 **
34258 ** (13) A SHARED lock is held on the database file while reading any
34259 ** content out of the database file.
34260 **
34261 ******************************************************************************/
34262
34263 /*
34264 ** Macros for troubleshooting. Normally turned off
34265 */
34266 #if 0
@@ -34152,62 +34281,283 @@
34281 */
34282 #define PAGERID(p) ((int)(p->fd))
34283 #define FILEHANDLEID(fd) ((int)fd)
34284
34285 /*
34286 ** The Pager.eState variable stores the current 'state' of a pager. A
34287 ** pager may be in any one of the seven states shown in the following
34288 ** state diagram.
34289 **
34290 ** OPEN <------+------+
34291 ** | | |
34292 ** V | |
34293 ** +---------> READER-------+ |
34294 ** | | |
34295 ** | V |
34296 ** |<-------WRITER_LOCKED------> ERROR
34297 ** | | ^
34298 ** | V |
34299 ** |<------WRITER_CACHEMOD-------->|
34300 ** | | |
34301 ** | V |
34302 ** |<-------WRITER_DBMOD---------->|
34303 ** | | |
34304 ** | V |
34305 ** +<------WRITER_FINISHED-------->+
34306 **
34307 **
34308 ** List of state transitions and the C [function] that performs each:
34309 **
34310 ** OPEN -> READER [sqlite3PagerSharedLock]
34311 ** READER -> OPEN [pager_unlock]
34312 **
34313 ** READER -> WRITER_LOCKED [sqlite3PagerBegin]
34314 ** WRITER_LOCKED -> WRITER_CACHEMOD [pager_open_journal]
34315 ** WRITER_CACHEMOD -> WRITER_DBMOD [syncJournal]
34316 ** WRITER_DBMOD -> WRITER_FINISHED [sqlite3PagerCommitPhaseOne]
34317 ** WRITER_*** -> READER [pager_end_transaction]
34318 **
34319 ** WRITER_*** -> ERROR [pager_error]
34320 ** ERROR -> OPEN [pager_unlock]
34321 **
34322 **
34323 ** OPEN:
34324 **
34325 ** The pager starts up in this state. Nothing is guaranteed in this
34326 ** state - the file may or may not be locked and the database size is
34327 ** unknown. The database may not be read or written.
34328 **
34329 ** * No read or write transaction is active.
34330 ** * Any lock, or no lock at all, may be held on the database file.
34331 ** * The dbSize, dbOrigSize and dbFileSize variables may not be trusted.
34332 **
34333 ** READER:
34334 **
34335 ** In this state all the requirements for reading the database in
34336 ** rollback (non-WAL) mode are met. Unless the pager is (or recently
34337 ** was) in exclusive-locking mode, a user-level read transaction is
34338 ** open. The database size is known in this state.
34339 **
34340 ** A connection running with locking_mode=normal enters this state when
34341 ** it opens a read-transaction on the database and returns to state
34342 ** OPEN after the read-transaction is completed. However a connection
34343 ** running in locking_mode=exclusive (including temp databases) remains in
34344 ** this state even after the read-transaction is closed. The only way
34345 ** a locking_mode=exclusive connection can transition from READER to OPEN
34346 ** is via the ERROR state (see below).
34347 **
34348 ** * A read transaction may be active (but a write-transaction cannot).
34349 ** * A SHARED or greater lock is held on the database file.
34350 ** * The dbSize variable may be trusted (even if a user-level read
34351 ** transaction is not active). The dbOrigSize and dbFileSize variables
34352 ** may not be trusted at this point.
34353 ** * If the database is a WAL database, then the WAL connection is open.
34354 ** * Even if a read-transaction is not open, it is guaranteed that
34355 ** there is no hot-journal in the file-system.
34356 **
34357 ** WRITER_LOCKED:
34358 **
34359 ** The pager moves to this state from READER when a write-transaction
34360 ** is first opened on the database. In WRITER_LOCKED state, all locks
34361 ** required to start a write-transaction are held, but no actual
34362 ** modifications to the cache or database have taken place.
34363 **
34364 ** In rollback mode, a RESERVED or (if the transaction was opened with
34365 ** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when
34366 ** moving to this state, but the journal file is not written to or opened
34367 ** to in this state. If the transaction is committed or rolled back while
34368 ** in WRITER_LOCKED state, all that is required is to unlock the database
34369 ** file.
34370 **
34371 ** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file.
34372 ** If the connection is running with locking_mode=exclusive, an attempt
34373 ** is made to obtain an EXCLUSIVE lock on the database file.
34374 **
34375 ** * A write transaction is active.
34376 ** * If the connection is open in rollback-mode, a RESERVED or greater
34377 ** lock is held on the database file.
34378 ** * If the connection is open in WAL-mode, a WAL write transaction
34379 ** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully
34380 ** called).
34381 ** * The dbSize, dbOrigSize and dbFileSize variables are all valid.
34382 ** * The contents of the pager cache have not been modified.
34383 ** * The journal file may or may not be open.
34384 ** * Nothing (not even the first header) has been written to the journal.
34385 **
34386 ** WRITER_CACHEMOD:
34387 **
34388 ** A pager moves from WRITER_LOCKED state to this state when a page is
34389 ** first modified by the upper layer. In rollback mode the journal file
34390 ** is opened (if it is not already open) and a header written to the
34391 ** start of it. The database file on disk has not been modified.
34392 **
34393 ** * A write transaction is active.
34394 ** * A RESERVED or greater lock is held on the database file.
34395 ** * The journal file is open and the first header has been written
34396 ** to it, but the header has not been synced to disk.
34397 ** * The contents of the page cache have been modified.
34398 **
34399 ** WRITER_DBMOD:
34400 **
34401 ** The pager transitions from WRITER_CACHEMOD into WRITER_DBMOD state
34402 ** when it modifies the contents of the database file. WAL connections
34403 ** never enter this state (since they do not modify the database file,
34404 ** just the log file).
34405 **
34406 ** * A write transaction is active.
34407 ** * An EXCLUSIVE or greater lock is held on the database file.
34408 ** * The journal file is open and the first header has been written
34409 ** and synced to disk.
34410 ** * The contents of the page cache have been modified (and possibly
34411 ** written to disk).
34412 **
34413 ** WRITER_FINISHED:
34414 **
34415 ** It is not possible for a WAL connection to enter this state.
34416 **
34417 ** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
34418 ** state after the entire transaction has been successfully written into the
34419 ** database file. In this state the transaction may be committed simply
34420 ** by finalizing the journal file. Once in WRITER_FINISHED state, it is
34421 ** not possible to modify the database further. At this point, the upper
34422 ** layer must either commit or rollback the transaction.
34423 **
34424 ** * A write transaction is active.
34425 ** * An EXCLUSIVE or greater lock is held on the database file.
34426 ** * All writing and syncing of journal and database data has finished.
34427 ** If no error occured, all that remains is to finalize the journal to
34428 ** commit the transaction. If an error did occur, the caller will need
34429 ** to rollback the transaction.
34430 **
34431 ** ERROR:
34432 **
34433 ** The ERROR state is entered when an IO or disk-full error (including
34434 ** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it
34435 ** difficult to be sure that the in-memory pager state (cache contents,
34436 ** db size etc.) are consistent with the contents of the file-system.
34437 **
34438 ** Temporary pager files may enter the ERROR state, but in-memory pagers
34439 ** cannot.
34440 **
34441 ** For example, if an IO error occurs while performing a rollback,
34442 ** the contents of the page-cache may be left in an inconsistent state.
34443 ** At this point it would be dangerous to change back to READER state
34444 ** (as usually happens after a rollback). Any subsequent readers might
34445 ** report database corruption (due to the inconsistent cache), and if
34446 ** they upgrade to writers, they may inadvertently corrupt the database
34447 ** file. To avoid this hazard, the pager switches into the ERROR state
34448 ** instead of READER following such an error.
34449 **
34450 ** Once it has entered the ERROR state, any attempt to use the pager
34451 ** to read or write data returns an error. Eventually, once all
34452 ** outstanding transactions have been abandoned, the pager is able to
34453 ** transition back to OPEN state, discarding the contents of the
34454 ** page-cache and any other in-memory state at the same time. Everything
34455 ** is reloaded from disk (and, if necessary, hot-journal rollback peformed)
34456 ** when a read-transaction is next opened on the pager (transitioning
34457 ** the pager into READER state). At that point the system has recovered
34458 ** from the error.
34459 **
34460 ** Specifically, the pager jumps into the ERROR state if:
34461 **
34462 ** 1. An error occurs while attempting a rollback. This happens in
34463 ** function sqlite3PagerRollback().
34464 **
34465 ** 2. An error occurs while attempting to finalize a journal file
34466 ** following a commit in function sqlite3PagerCommitPhaseTwo().
34467 **
34468 ** 3. An error occurs while attempting to write to the journal or
34469 ** database file in function pagerStress() in order to free up
34470 ** memory.
34471 **
34472 ** In other cases, the error is returned to the b-tree layer. The b-tree
34473 ** layer then attempts a rollback operation. If the error condition
34474 ** persists, the pager enters the ERROR state via condition (1) above.
34475 **
34476 ** Condition (3) is necessary because it can be triggered by a read-only
34477 ** statement executed within a transaction. In this case, if the error
34478 ** code were simply returned to the user, the b-tree layer would not
34479 ** automatically attempt a rollback, as it assumes that an error in a
34480 ** read-only statement cannot leave the pager in an internally inconsistent
34481 ** state.
34482 **
34483 ** * The Pager.errCode variable is set to something other than SQLITE_OK.
34484 ** * There are one or more outstanding references to pages (after the
34485 ** last reference is dropped the pager should move back to OPEN state).
34486 ** * The pager is not an in-memory pager.
34487 **
34488 **
34489 ** Notes:
34490 **
34491 ** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
34492 ** connection is open in WAL mode. A WAL connection is always in one
34493 ** of the first four states.
34494 **
34495 ** * Normally, a connection open in exclusive mode is never in PAGER_OPEN
34496 ** state. There are two exceptions: immediately after exclusive-mode has
34497 ** been turned on (and before any read or write transactions are
34498 ** executed), and when the pager is leaving the "error state".
34499 **
34500 ** * See also: assert_pager_state().
34501 */
34502 #define PAGER_OPEN 0
34503 #define PAGER_READER 1
34504 #define PAGER_WRITER_LOCKED 2
34505 #define PAGER_WRITER_CACHEMOD 3
34506 #define PAGER_WRITER_DBMOD 4
34507 #define PAGER_WRITER_FINISHED 5
34508 #define PAGER_ERROR 6
34509
34510 /*
34511 ** The Pager.eLock variable is almost always set to one of the
34512 ** following locking-states, according to the lock currently held on
34513 ** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
34514 ** This variable is kept up to date as locks are taken and released by
34515 ** the pagerLockDb() and pagerUnlockDb() wrappers.
34516 **
34517 ** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY
34518 ** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not
34519 ** the operation was successful. In these circumstances pagerLockDb() and
34520 ** pagerUnlockDb() take a conservative approach - eLock is always updated
34521 ** when unlocking the file, and only updated when locking the file if the
34522 ** VFS call is successful. This way, the Pager.eLock variable may be set
34523 ** to a less exclusive (lower) value than the lock that is actually held
34524 ** at the system level, but it is never set to a more exclusive value.
34525 **
34526 ** This is usually safe. If an xUnlock fails or appears to fail, there may
34527 ** be a few redundant xLock() calls or a lock may be held for longer than
34528 ** required, but nothing really goes wrong.
34529 **
34530 ** The exception is when the database file is unlocked as the pager moves
34531 ** from ERROR to OPEN state. At this point there may be a hot-journal file
34532 ** in the file-system that needs to be rolled back (as part of a OPEN->SHARED
34533 ** transition, by the same pager or any other). If the call to xUnlock()
34534 ** fails at this point and the pager is left holding an EXCLUSIVE lock, this
34535 ** can confuse the call to xCheckReservedLock() call made later as part
34536 ** of hot-journal detection.
34537 **
34538 ** xCheckReservedLock() is defined as returning true "if there is a RESERVED
34539 ** lock held by this process or any others". So xCheckReservedLock may
34540 ** return true because the caller itself is holding an EXCLUSIVE lock (but
34541 ** doesn't know it because of a previous error in xUnlock). If this happens
34542 ** a hot-journal may be mistaken for a journal being created by an active
34543 ** transaction in another process, causing SQLite to read from the database
34544 ** without rolling it back.
34545 **
34546 ** To work around this, if a call to xUnlock() fails when unlocking the
34547 ** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It
34548 ** is only changed back to a real locking state after a successful call
34549 ** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition
34550 ** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK
34551 ** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE
34552 ** lock on the database file before attempting to roll it back. See function
34553 ** PagerSharedLock() for more detail.
34554 **
34555 ** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in
34556 ** PAGER_OPEN state.
34557 */
34558 #define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1)
34559
34560 /*
34561 ** A macro used for invoking the codec if there is one
34562 */
34563 #ifdef SQLITE_HAS_CODEC
@@ -34253,37 +34603,32 @@
34603 u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
34604 #endif
34605 };
34606
34607 /*
34608 ** A open page cache is an instance of struct Pager. A description of
34609 ** some of the more important member variables follows:
34610 **
34611 ** eState
34612 **
34613 ** The current 'state' of the pager object. See the comment and state
34614 ** diagram above for a description of the pager state.
34615 **
34616 ** eLock
34617 **
34618 ** For a real on-disk database, the current lock held on the database file -
34619 ** NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
34620 **
34621 ** For a temporary or in-memory database (neither of which require any
34622 ** locks), this variable is always set to EXCLUSIVE_LOCK. Since such
34623 ** databases always have Pager.exclusiveMode==1, this tricks the pager
34624 ** logic into thinking that it already has all the locks it will ever
34625 ** need (and no reason to release them).
34626 **
34627 ** In some (obscure) circumstances, this variable may also be set to
34628 ** UNKNOWN_LOCK. See the comment above the #define of UNKNOWN_LOCK for
34629 ** details.
 
 
 
 
 
34630 **
34631 ** changeCountDone
34632 **
34633 ** This boolean variable is used to make sure that the change-counter
34634 ** (the 4-byte header field at byte offset 24 of the database file) is
@@ -34298,28 +34643,10 @@
34643 **
34644 ** This mechanism means that when running in exclusive mode, a connection
34645 ** need only update the change-counter once, for the first transaction
34646 ** committed.
34647 **
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34648 ** setMaster
34649 **
34650 ** When PagerCommitPhaseOne() is called to commit a transaction, it may
34651 ** (or may not) specify a master-journal name to be written into the
34652 ** journal file before it is synced to disk.
@@ -34326,84 +34653,146 @@
34653 **
34654 ** Whether or not a journal file contains a master-journal pointer affects
34655 ** the way in which the journal file is finalized after the transaction is
34656 ** committed or rolled back when running in "journal_mode=PERSIST" mode.
34657 ** If a journal file does not contain a master-journal pointer, it is
34658 ** finalized by overwriting the first journal header with zeroes. If
34659 ** it does contain a master-journal pointer the journal file is finalized
34660 ** by truncating it to zero bytes, just as if the connection were
34661 ** running in "journal_mode=truncate" mode.
34662 **
34663 ** Journal files that contain master journal pointers cannot be finalized
34664 ** simply by overwriting the first journal-header with zeroes, as the
34665 ** master journal pointer could interfere with hot-journal rollback of any
34666 ** subsequently interrupted transaction that reuses the journal file.
34667 **
34668 ** The flag is cleared as soon as the journal file is finalized (either
34669 ** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the
34670 ** journal file from being successfully finalized, the setMaster flag
34671 ** is cleared anyway (and the pager will move to ERROR state).
34672 **
34673 ** doNotSpill, doNotSyncSpill
34674 **
34675 ** These two boolean variables control the behaviour of cache-spills
34676 ** (calls made by the pcache module to the pagerStress() routine to
34677 ** write cached data to the file-system in order to free up memory).
34678 **
34679 ** When doNotSpill is non-zero, writing to the database from pagerStress()
34680 ** is disabled altogether. This is done in a very obscure case that
34681 ** comes up during savepoint rollback that requires the pcache module
34682 ** to allocate a new page to prevent the journal file from being written
34683 ** while it is being traversed by code in pager_playback().
34684 **
34685 ** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
34686 ** is permitted, but syncing the journal file is not. This flag is set
34687 ** by sqlite3PagerWrite() when the file-system sector-size is larger than
34688 ** the database page-size in order to prevent a journal sync from happening
34689 ** in between the journalling of two pages on the same sector.
34690 **
34691 ** subjInMemory
34692 **
34693 ** This is a boolean variable. If true, then any required sub-journal
34694 ** is opened as an in-memory journal file. If false, then in-memory
34695 ** sub-journals are only used for in-memory pager files.
34696 **
34697 ** This variable is updated by the upper layer each time a new
34698 ** write-transaction is opened.
34699 **
34700 ** dbSize, dbOrigSize, dbFileSize
34701 **
34702 ** Variable dbSize is set to the number of pages in the database file.
34703 ** It is valid in PAGER_READER and higher states (all states except for
34704 ** OPEN and ERROR).
34705 **
34706 ** dbSize is set based on the size of the database file, which may be
34707 ** larger than the size of the database (the value stored at offset
34708 ** 28 of the database header by the btree). If the size of the file
34709 ** is not an integer multiple of the page-size, the value stored in
34710 ** dbSize is rounded down (i.e. a 5KB file with 2K page-size has dbSize==2).
34711 ** Except, any file that is greater than 0 bytes in size is considered
34712 ** to have at least one page. (i.e. a 1KB file with 2K page-size leads
34713 ** to dbSize==1).
34714 **
34715 ** During a write-transaction, if pages with page-numbers greater than
34716 ** dbSize are modified in the cache, dbSize is updated accordingly.
34717 ** Similarly, if the database is truncated using PagerTruncateImage(),
34718 ** dbSize is updated.
34719 **
34720 ** Variables dbOrigSize and dbFileSize are valid in states
34721 ** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize
34722 ** variable at the start of the transaction. It is used during rollback,
34723 ** and to determine whether or not pages need to be journalled before
34724 ** being modified.
34725 **
34726 ** Throughout a write-transaction, dbFileSize contains the size of
34727 ** the file on disk in pages. It is set to a copy of dbSize when the
34728 ** write-transaction is first opened, and updated when VFS calls are made
34729 ** to write or truncate the database file on disk.
34730 **
34731 ** The only reason the dbFileSize variable is required is to suppress
34732 ** unnecessary calls to xTruncate() after committing a transaction. If,
34733 ** when a transaction is committed, the dbFileSize variable indicates
34734 ** that the database file is larger than the database image (Pager.dbSize),
34735 ** pager_truncate() is called. The pager_truncate() call uses xFilesize()
34736 ** to measure the database file on disk, and then truncates it if required.
34737 ** dbFileSize is not used when rolling back a transaction. In this case
34738 ** pager_truncate() is called unconditionally (which means there may be
34739 ** a call to xFilesize() that is not strictly required). In either case,
34740 ** pager_truncate() may cause the file to become smaller or larger.
34741 **
34742 ** dbHintSize
34743 **
34744 ** The dbHintSize variable is used to limit the number of calls made to
34745 ** the VFS xFileControl(FCNTL_SIZE_HINT) method.
34746 **
34747 ** dbHintSize is set to a copy of the dbSize variable when a
34748 ** write-transaction is opened (at the same time as dbFileSize and
34749 ** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called,
34750 ** dbHintSize is increased to the number of pages that correspond to the
34751 ** size-hint passed to the method call. See pager_write_pagelist() for
34752 ** details.
34753 **
34754 ** errCode
34755 **
34756 ** The Pager.errCode variable is only ever used in PAGER_ERROR state. It
34757 ** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
34758 ** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
34759 ** sub-codes.
34760 */
34761 struct Pager {
34762 sqlite3_vfs *pVfs; /* OS functions to use for IO */
34763 u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
34764 u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
34765 u8 useJournal; /* Use a rollback journal on this file */
34766 u8 noReadlock; /* Do not bother to obtain readlocks */
34767 u8 noSync; /* Do not sync the journal if true */
34768 u8 fullSync; /* Do extra syncs of the journal for robustness */
34769 u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */
34770 u8 tempFile; /* zFilename is a temporary file */
34771 u8 readOnly; /* True for a read-only database */
34772 u8 memDb; /* True to inhibit all file I/O */
34773
34774 /**************************************************************************
34775 ** The following block contains those class members that change during
34776 ** routine opertion. Class members not in this block are either fixed
34777 ** when the pager is first created or else only change when there is a
34778 ** significant mode change (such as changing the page_size, locking_mode,
34779 ** or the journal_mode). From another view, these class members describe
34780 ** the "state" of the pager, while other class members describe the
34781 ** "configuration" of the pager.
 
 
34782 */
34783 u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */
34784 u8 eLock; /* Current lock held on database file */
 
 
34785 u8 changeCountDone; /* Set after incrementing the change-counter */
34786 u8 setMaster; /* True if a m-j name has been written to jrnl */
34787 u8 doNotSpill; /* Do not spill the cache when non-zero */
34788 u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
 
34789 u8 subjInMemory; /* True to use in-memory sub-journals */
34790 Pgno dbSize; /* Number of pages in the database */
34791 Pgno dbOrigSize; /* dbSize before the current transaction */
34792 Pgno dbFileSize; /* Number of pages in the database file */
34793 Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */
34794 int errCode; /* One of several kinds of errors */
34795 int nRec; /* Pages journalled since last j-header written */
34796 u32 cksumInit; /* Quasi-random value added to every checksum */
34797 u32 nSubRec; /* Number of records written to sub-journal */
34798 Bitvec *pInJournal; /* One bit for each page in the database file */
@@ -34410,21 +34799,25 @@
34799 sqlite3_file *fd; /* File descriptor for database */
34800 sqlite3_file *jfd; /* File descriptor for main journal */
34801 sqlite3_file *sjfd; /* File descriptor for sub-journal */
34802 i64 journalOff; /* Current write offset in the journal file */
34803 i64 journalHdr; /* Byte offset to previous journal header */
34804 sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
34805 PagerSavepoint *aSavepoint; /* Array of active savepoints */
34806 int nSavepoint; /* Number of elements in aSavepoint[] */
34807 char dbFileVers[16]; /* Changes whenever database file changes */
34808 /*
34809 ** End of the routinely-changing class members
34810 ***************************************************************************/
34811
34812 u16 nExtra; /* Add this many bytes to each in-memory page */
34813 i16 nReserve; /* Number of unused bytes at end of each page */
34814 u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
34815 u32 sectorSize; /* Assumed sector size during rollback */
34816 int pageSize; /* Number of bytes in a page */
34817 Pgno mxPgno; /* Maximum allowed size of the database */
34818 i64 journalSizeLimit; /* Size limit for persistent journal files */
34819 char *zFilename; /* Name of the database file */
34820 char *zJournal; /* Name of the journal file */
34821 int (*xBusyHandler)(void*); /* Function to call when busy */
34822 void *pBusyHandlerArg; /* Context argument for xBusyHandler */
34823 #ifdef SQLITE_TEST
@@ -34438,11 +34831,10 @@
34831 void (*xCodecFree)(void*); /* Destructor for the codec */
34832 void *pCodec; /* First argument to xCodec... methods */
34833 #endif
34834 char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
34835 PCache *pPCache; /* Pointer to page cache object */
 
34836 #ifndef SQLITE_OMIT_WAL
34837 Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
34838 char *zWal; /* File name for write-ahead log */
34839 #endif
34840 };
@@ -34517,26 +34909,225 @@
34909 /*
34910 ** The maximum legal page number is (2^31 - 1).
34911 */
34912 #define PAGER_MAX_PGNO 2147483647
34913
34914 /*
34915 ** The argument to this macro is a file descriptor (type sqlite3_file*).
34916 ** Return 0 if it is not open, or non-zero (but not 1) if it is.
34917 **
34918 ** This is so that expressions can be written as:
34919 **
34920 ** if( isOpen(pPager->jfd) ){ ...
34921 **
34922 ** instead of
34923 **
34924 ** if( pPager->jfd->pMethods ){ ...
34925 */
34926 #define isOpen(pFd) ((pFd)->pMethods)
34927
34928 /*
34929 ** Return true if this pager uses a write-ahead log instead of the usual
34930 ** rollback journal. Otherwise false.
34931 */
34932 #ifndef SQLITE_OMIT_WAL
34933 static int pagerUseWal(Pager *pPager){
34934 return (pPager->pWal!=0);
34935 }
34936 #else
34937 # define pagerUseWal(x) 0
34938 # define pagerRollbackWal(x) 0
34939 # define pagerWalFrames(v,w,x,y,z) 0
34940 # define pagerOpenWalIfPresent(z) SQLITE_OK
34941 # define pagerBeginReadTransaction(z) SQLITE_OK
34942 #endif
34943
34944 #ifndef NDEBUG
34945 /*
34946 ** Usage:
34947 **
34948 ** assert( assert_pager_state(pPager) );
34949 **
34950 ** This function runs many asserts to try to find inconsistencies in
34951 ** the internal state of the Pager object.
34952 */
34953 static int assert_pager_state(Pager *p){
34954 Pager *pPager = p;
34955
34956 /* State must be valid. */
34957 assert( p->eState==PAGER_OPEN
34958 || p->eState==PAGER_READER
34959 || p->eState==PAGER_WRITER_LOCKED
34960 || p->eState==PAGER_WRITER_CACHEMOD
34961 || p->eState==PAGER_WRITER_DBMOD
34962 || p->eState==PAGER_WRITER_FINISHED
34963 || p->eState==PAGER_ERROR
34964 );
34965
34966 /* Regardless of the current state, a temp-file connection always behaves
34967 ** as if it has an exclusive lock on the database file. It never updates
34968 ** the change-counter field, so the changeCountDone flag is always set.
34969 */
34970 assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK );
34971 assert( p->tempFile==0 || pPager->changeCountDone );
34972
34973 /* If the useJournal flag is clear, the journal-mode must be "OFF".
34974 ** And if the journal-mode is "OFF", the journal file must not be open.
34975 */
34976 assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
34977 assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
34978
34979 /* Check that MEMDB implies noSync. And an in-memory journal. Since
34980 ** this means an in-memory pager performs no IO at all, it cannot encounter
34981 ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing
34982 ** a journal file. (although the in-memory journal implementation may
34983 ** return SQLITE_IOERR_NOMEM while the journal file is being written). It
34984 ** is therefore not possible for an in-memory pager to enter the ERROR
34985 ** state.
34986 */
34987 if( MEMDB ){
34988 assert( p->noSync );
34989 assert( p->journalMode==PAGER_JOURNALMODE_OFF
34990 || p->journalMode==PAGER_JOURNALMODE_MEMORY
34991 );
34992 assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN );
34993 assert( pagerUseWal(p)==0 );
34994 }
34995
34996 /* If changeCountDone is set, a RESERVED lock or greater must be held
34997 ** on the file.
34998 */
34999 assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );
35000 assert( p->eLock!=PENDING_LOCK );
35001
35002 switch( p->eState ){
35003 case PAGER_OPEN:
35004 assert( !MEMDB );
35005 assert( pPager->errCode==SQLITE_OK );
35006 assert( sqlite3PcacheRefCount(pPager->pPCache)==0 || pPager->tempFile );
35007 break;
35008
35009 case PAGER_READER:
35010 assert( pPager->errCode==SQLITE_OK );
35011 assert( p->eLock!=UNKNOWN_LOCK );
35012 assert( p->eLock>=SHARED_LOCK || p->noReadlock );
35013 break;
35014
35015 case PAGER_WRITER_LOCKED:
35016 assert( p->eLock!=UNKNOWN_LOCK );
35017 assert( pPager->errCode==SQLITE_OK );
35018 if( !pagerUseWal(pPager) ){
35019 assert( p->eLock>=RESERVED_LOCK );
35020 }
35021 assert( pPager->dbSize==pPager->dbOrigSize );
35022 assert( pPager->dbOrigSize==pPager->dbFileSize );
35023 assert( pPager->dbOrigSize==pPager->dbHintSize );
35024 assert( pPager->setMaster==0 );
35025 break;
35026
35027 case PAGER_WRITER_CACHEMOD:
35028 assert( p->eLock!=UNKNOWN_LOCK );
35029 assert( pPager->errCode==SQLITE_OK );
35030 if( !pagerUseWal(pPager) ){
35031 /* It is possible that if journal_mode=wal here that neither the
35032 ** journal file nor the WAL file are open. This happens during
35033 ** a rollback transaction that switches from journal_mode=off
35034 ** to journal_mode=wal.
35035 */
35036 assert( p->eLock>=RESERVED_LOCK );
35037 assert( isOpen(p->jfd)
35038 || p->journalMode==PAGER_JOURNALMODE_OFF
35039 || p->journalMode==PAGER_JOURNALMODE_WAL
35040 );
35041 }
35042 assert( pPager->dbOrigSize==pPager->dbFileSize );
35043 assert( pPager->dbOrigSize==pPager->dbHintSize );
35044 break;
35045
35046 case PAGER_WRITER_DBMOD:
35047 assert( p->eLock==EXCLUSIVE_LOCK );
35048 assert( pPager->errCode==SQLITE_OK );
35049 assert( !pagerUseWal(pPager) );
35050 assert( p->eLock>=EXCLUSIVE_LOCK );
35051 assert( isOpen(p->jfd)
35052 || p->journalMode==PAGER_JOURNALMODE_OFF
35053 || p->journalMode==PAGER_JOURNALMODE_WAL
35054 );
35055 assert( pPager->dbOrigSize<=pPager->dbHintSize );
35056 break;
35057
35058 case PAGER_WRITER_FINISHED:
35059 assert( p->eLock==EXCLUSIVE_LOCK );
35060 assert( pPager->errCode==SQLITE_OK );
35061 assert( !pagerUseWal(pPager) );
35062 assert( isOpen(p->jfd)
35063 || p->journalMode==PAGER_JOURNALMODE_OFF
35064 || p->journalMode==PAGER_JOURNALMODE_WAL
35065 );
35066 break;
35067
35068 case PAGER_ERROR:
35069 /* There must be at least one outstanding reference to the pager if
35070 ** in ERROR state. Otherwise the pager should have already dropped
35071 ** back to OPEN state.
35072 */
35073 assert( pPager->errCode!=SQLITE_OK );
35074 assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
35075 break;
35076 }
35077
35078 return 1;
35079 }
35080
35081 /*
35082 ** Return a pointer to a human readable string in a static buffer
35083 ** containing the state of the Pager object passed as an argument. This
35084 ** is intended to be used within debuggers. For example, as an alternative
35085 ** to "print *pPager" in gdb:
35086 **
35087 ** (gdb) printf "%s", print_pager_state(pPager)
35088 */
35089 static char *print_pager_state(Pager *p){
35090 static char zRet[1024];
35091
35092 sqlite3_snprintf(1024, zRet,
35093 "Filename: %s\n"
35094 "State: %s errCode=%d\n"
35095 "Lock: %s\n"
35096 "Locking mode: locking_mode=%s\n"
35097 "Journal mode: journal_mode=%s\n"
35098 "Backing store: tempFile=%d memDb=%d useJournal=%d\n"
35099 "Journal: journalOff=%lld journalHdr=%lld\n"
35100 "Size: dbsize=%d dbOrigSize=%d dbFileSize=%d\n"
35101 , p->zFilename
35102 , p->eState==PAGER_OPEN ? "OPEN" :
35103 p->eState==PAGER_READER ? "READER" :
35104 p->eState==PAGER_WRITER_LOCKED ? "WRITER_LOCKED" :
35105 p->eState==PAGER_WRITER_CACHEMOD ? "WRITER_CACHEMOD" :
35106 p->eState==PAGER_WRITER_DBMOD ? "WRITER_DBMOD" :
35107 p->eState==PAGER_WRITER_FINISHED ? "WRITER_FINISHED" :
35108 p->eState==PAGER_ERROR ? "ERROR" : "?error?"
35109 , (int)p->errCode
35110 , p->eLock==NO_LOCK ? "NO_LOCK" :
35111 p->eLock==RESERVED_LOCK ? "RESERVED" :
35112 p->eLock==EXCLUSIVE_LOCK ? "EXCLUSIVE" :
35113 p->eLock==SHARED_LOCK ? "SHARED" :
35114 p->eLock==UNKNOWN_LOCK ? "UNKNOWN" : "?error?"
35115 , p->exclusiveMode ? "exclusive" : "normal"
35116 , p->journalMode==PAGER_JOURNALMODE_MEMORY ? "memory" :
35117 p->journalMode==PAGER_JOURNALMODE_OFF ? "off" :
35118 p->journalMode==PAGER_JOURNALMODE_DELETE ? "delete" :
35119 p->journalMode==PAGER_JOURNALMODE_PERSIST ? "persist" :
35120 p->journalMode==PAGER_JOURNALMODE_TRUNCATE ? "truncate" :
35121 p->journalMode==PAGER_JOURNALMODE_WAL ? "wal" : "?error?"
35122 , (int)p->tempFile, (int)p->memDb, (int)p->useJournal
35123 , p->journalOff, p->journalHdr
35124 , (int)p->dbSize, (int)p->dbOrigSize, (int)p->dbFileSize
35125 );
35126
35127 return zRet;
35128 }
35129 #endif
35130
35131 /*
35132 ** Return true if it is necessary to write page *pPg into the sub-journal.
35133 ** A page needs to be written into the sub-journal if there exists one
@@ -34584,10 +35175,11 @@
35175
35176 /*
35177 ** Write a 32-bit integer into a string buffer in big-endian byte order.
35178 */
35179 #define put32bits(A,B) sqlite3Put4byte((u8*)A,B)
35180
35181
35182 /*
35183 ** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
35184 ** on success or an error code is something goes wrong.
35185 */
@@ -34596,31 +35188,57 @@
35188 put32bits(ac, val);
35189 return sqlite3OsWrite(fd, ac, 4, offset);
35190 }
35191
35192 /*
35193 ** Unlock the database file to level eLock, which must be either NO_LOCK
35194 ** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
35195 ** succeeds, set the Pager.eLock variable to match the (attempted) new lock.
35196 **
35197 ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
35198 ** called, do not modify it. See the comment above the #define of
35199 ** UNKNOWN_LOCK for an explanation of this.
35200 */
35201 static int pagerUnlockDb(Pager *pPager, int eLock){
35202 int rc = SQLITE_OK;
35203
35204 assert( !pPager->exclusiveMode );
35205 assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
35206 assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
35207 if( isOpen(pPager->fd) ){
35208 assert( pPager->eLock>=eLock );
35209 rc = sqlite3OsUnlock(pPager->fd, eLock);
35210 if( pPager->eLock!=UNKNOWN_LOCK ){
35211 pPager->eLock = (u8)eLock;
35212 }
35213 IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
35214 }
35215 return rc;
35216 }
35217
35218 /*
35219 ** Lock the database file to level eLock, which must be either SHARED_LOCK,
35220 ** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
35221 ** Pager.eLock variable to the new locking state.
35222 **
35223 ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
35224 ** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
35225 ** See the comment above the #define of UNKNOWN_LOCK for an explanation
35226 ** of this.
35227 */
35228 static int pagerLockDb(Pager *pPager, int eLock){
35229 int rc = SQLITE_OK;
35230
35231 assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
35232 if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
35233 rc = sqlite3OsLock(pPager->fd, eLock);
35234 if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
35235 pPager->eLock = (u8)eLock;
35236 IOTRACE(("LOCK %p %d\n", pPager, eLock))
35237 }
35238 }
35239 return rc;
35240 }
35241
35242 /*
35243 ** This function determines whether or not the atomic-write optimization
35244 ** can be used with this pager. The optimization can be used if:
@@ -34692,17 +35310,18 @@
35310 ** that the page is either dirty or still matches the calculated page-hash.
35311 */
35312 #define CHECK_PAGE(x) checkPage(x)
35313 static void checkPage(PgHdr *pPg){
35314 Pager *pPager = pPg->pPager;
35315 assert( pPager->eState!=PAGER_ERROR );
35316 assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) );
35317 }
35318
35319 #else
35320 #define pager_datahash(X,Y) 0
35321 #define pager_pagehash(X) 0
35322 #define pager_set_pagehash(X)
35323 #define CHECK_PAGE(x)
35324 #endif /* SQLITE_CHECK_PAGES */
35325
35326 /*
35327 ** When this is called the journal file for pager pPager must be open.
@@ -34865,11 +35484,11 @@
35484 ** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
35485 */
35486 static int writeJournalHdr(Pager *pPager){
35487 int rc = SQLITE_OK; /* Return code */
35488 char *zHeader = pPager->pTmpSpace; /* Temporary space used to build header */
35489 u32 nHeader = (u32)pPager->pageSize;/* Size of buffer pointed to by zHeader */
35490 u32 nWrite; /* Bytes of header sector written */
35491 int ii; /* Loop counter */
35492
35493 assert( isOpen(pPager->jfd) ); /* Journal file must be open. */
35494
@@ -34908,11 +35527,11 @@
35527 **
35528 ** * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
35529 ** that garbage data is never appended to the journal file.
35530 */
35531 assert( isOpen(pPager->fd) || pPager->noSync );
35532 if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
35533 || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
35534 ){
35535 memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
35536 put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
35537 }else{
@@ -35032,18 +35651,25 @@
35651 }
35652
35653 if( pPager->journalOff==0 ){
35654 u32 iPageSize; /* Page-size field of journal header */
35655 u32 iSectorSize; /* Sector-size field of journal header */
 
35656
35657 /* Read the page-size and sector-size journal header fields. */
35658 if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+20, &iSectorSize))
35659 || SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+24, &iPageSize))
35660 ){
35661 return rc;
35662 }
35663
35664 /* Versions of SQLite prior to 3.5.8 set the page-size field of the
35665 ** journal header to zero. In this case, assume that the Pager.pageSize
35666 ** variable is already set to the correct page size.
35667 */
35668 if( iPageSize==0 ){
35669 iPageSize = pPager->pageSize;
35670 }
35671
35672 /* Check that the values read from the page-size and sector-size fields
35673 ** are within range. To be 'in range', both values need to be a power
35674 ** of two greater than or equal to 512 or 32, and not greater than their
35675 ** respective compile time maximum limits.
@@ -35062,14 +35688,12 @@
35688
35689 /* Update the page-size to match the value read from the journal.
35690 ** Use a testcase() macro to make sure that malloc failure within
35691 ** PagerSetPagesize() is tested.
35692 */
35693 rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1);
 
35694 testcase( rc!=SQLITE_OK );
 
35695
35696 /* Update the assumed sector-size to match the value used by
35697 ** the process that created this journal. If this journal was
35698 ** created by a process other than this one, then this routine
35699 ** is being called from within pager_playback(). The local value
@@ -35108,10 +35732,12 @@
35732 i64 iHdrOff; /* Offset of header in journal file */
35733 i64 jrnlSize; /* Size of journal file on disk */
35734 u32 cksum = 0; /* Checksum of string zMaster */
35735
35736 assert( pPager->setMaster==0 );
35737 assert( !pagerUseWal(pPager) );
35738
35739 if( !zMaster
35740 || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
35741 || pPager->journalMode==PAGER_JOURNALMODE_OFF
35742 ){
35743 return SQLITE_OK;
@@ -35144,11 +35770,10 @@
35770 || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
35771 ){
35772 return rc;
35773 }
35774 pPager->journalOff += (nMaster+20);
 
35775
35776 /* If the pager is in peristent-journal mode, then the physical
35777 ** journal-file may extend past the end of the master-journal name
35778 ** and 8 bytes of magic data just written to the file. This is
35779 ** dangerous because the code to rollback a hot-journal file
@@ -35180,21 +35805,15 @@
35805 (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
35806 return p;
35807 }
35808
35809 /*
35810 ** Discard the entire contents of the in-memory page-cache.
 
 
 
35811 */
35812 static void pager_reset(Pager *pPager){
35813 sqlite3BackupRestart(pPager->pBackup);
35814 sqlite3PcacheClear(pPager->pPCache);
 
 
 
35815 }
35816
35817 /*
35818 ** Free all structures in the Pager.aSavepoint[] array and set both
35819 ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
@@ -35233,38 +35852,43 @@
35852 }
35853 return rc;
35854 }
35855
35856 /*
35857 ** This function is a no-op if the pager is in exclusive mode and not
35858 ** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN
35859 ** state.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35860 **
35861 ** If the pager is not in exclusive-access mode, the database file is
35862 ** completely unlocked. If the file is unlocked and the file-system does
35863 ** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is
35864 ** closed (if it is open).
35865 **
35866 ** If the pager is in ERROR state when this function is called, the
35867 ** contents of the pager cache are discarded before switching back to
35868 ** the OPEN state. Regardless of whether the pager is in exclusive-mode
35869 ** or not, any journal file left in the file-system will be treated
35870 ** as a hot-journal and rolled back the next time a read-transaction
35871 ** is opened (by this or by any other connection).
35872 */
35873 static void pager_unlock(Pager *pPager){
35874
35875 assert( pPager->eState==PAGER_READER
35876 || pPager->eState==PAGER_OPEN
35877 || pPager->eState==PAGER_ERROR
35878 );
35879
35880 sqlite3BitvecDestroy(pPager->pInJournal);
35881 pPager->pInJournal = 0;
35882 releaseAllSavepoints(pPager);
35883
35884 if( pagerUseWal(pPager) ){
35885 assert( !isOpen(pPager->jfd) );
35886 sqlite3WalEndReadTransaction(pPager->pWal);
35887 pPager->eState = PAGER_OPEN;
35888 }else if( !pPager->exclusiveMode ){
35889 int rc; /* Error code returned by pagerUnlockDb() */
35890 int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
35891
35892 /* If the operating system support deletion of open files, then
35893 ** close the journal file when dropping the database lock. Otherwise
35894 ** another connection with journal_mode=delete might delete the file
@@ -35280,62 +35904,60 @@
35904 || 1!=(pPager->journalMode & 5)
35905 ){
35906 sqlite3OsClose(pPager->jfd);
35907 }
35908
35909 /* If the pager is in the ERROR state and the call to unlock the database
35910 ** file fails, set the current lock to UNKNOWN_LOCK. See the comment
35911 ** above the #define for UNKNOWN_LOCK for an explanation of why this
35912 ** is necessary.
35913 */
35914 rc = pagerUnlockDb(pPager, NO_LOCK);
35915 if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){
35916 pPager->eLock = UNKNOWN_LOCK;
35917 }
35918
35919 /* The pager state may be changed from PAGER_ERROR to PAGER_OPEN here
35920 ** without clearing the error code. This is intentional - the error
35921 ** code is cleared and the cache reset in the block below.
35922 */
35923 assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35924 pPager->changeCountDone = 0;
35925 pPager->eState = PAGER_OPEN;
 
35926 }
35927
35928 /* If Pager.errCode is set, the contents of the pager cache cannot be
35929 ** trusted. Now that there are no outstanding references to the pager,
35930 ** it can safely move back to PAGER_OPEN state. This happens in both
35931 ** normal and exclusive-locking mode.
35932 */
35933 if( pPager->errCode ){
35934 assert( !MEMDB );
35935 pager_reset(pPager);
35936 pPager->changeCountDone = pPager->tempFile;
35937 pPager->eState = PAGER_OPEN;
35938 pPager->errCode = SQLITE_OK;
35939 }
35940
35941 pPager->journalOff = 0;
35942 pPager->journalHdr = 0;
35943 pPager->setMaster = 0;
35944 }
35945
35946 /*
35947 ** This function is called whenever an IOERR or FULL error that requires
35948 ** the pager to transition into the ERROR state may ahve occurred.
35949 ** The first argument is a pointer to the pager structure, the second
35950 ** the error-code about to be returned by a pager API function. The
35951 ** value returned is a copy of the second argument to this function.
35952 **
35953 ** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the
35954 ** IOERR sub-codes, the pager enters the ERROR state and the error code
35955 ** is stored in Pager.errCode. While the pager remains in the ERROR state,
35956 ** all major API calls on the Pager will immediately return Pager.errCode.
35957 **
35958 ** The ERROR state indicates that the contents of the pager-cache
35959 ** cannot be trusted. This state can be cleared by completely discarding
35960 ** the contents of the pager-cache. If a transaction was active when
35961 ** the persistent error occurred, then the rollback journal may need
35962 ** to be replayed to restore the contents of the database file (as if
35963 ** it were a hot-journal).
@@ -35348,49 +35970,25 @@
35970 pPager->errCode==SQLITE_OK ||
35971 (pPager->errCode & 0xff)==SQLITE_IOERR
35972 );
35973 if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
35974 pPager->errCode = rc;
35975 pPager->eState = PAGER_ERROR;
35976 }
35977 return rc;
35978 }
35979
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35980 /*
35981 ** This routine ends a transaction. A transaction is usually ended by
35982 ** either a COMMIT or a ROLLBACK operation. This routine may be called
35983 ** after rollback of a hot-journal, or if an error occurs while opening
35984 ** the journal file or writing the very first journal-header of a
35985 ** database transaction.
35986 **
35987 ** This routine is never called in PAGER_ERROR state. If it is called
35988 ** in PAGER_NONE or PAGER_SHARED state and the lock held is less
35989 ** exclusive than a RESERVED lock, it is a no-op.
35990 **
35991 ** Otherwise, any active savepoints are released.
35992 **
35993 ** If the journal file is open, then it is "finalized". Once a journal
35994 ** file has been finalized it is not possible to use it to roll back a
@@ -35417,17 +36015,13 @@
36015 ** If the pager is running in exclusive mode, this method of finalizing
36016 ** the journal file is never used. Instead, if the journalMode is
36017 ** DELETE and the pager is in exclusive mode, the method described under
36018 ** journalMode==PERSIST is used instead.
36019 **
36020 ** After the journal is finalized, the pager moves to PAGER_READER state.
36021 ** If running in non-exclusive rollback mode, the lock on the file is
36022 ** downgraded to a SHARED_LOCK.
 
 
 
 
36023 **
36024 ** SQLITE_OK is returned if no error occurs. If an error occurs during
36025 ** any of the IO operations to finalize the journal file or unlock the
36026 ** database then the IO error code is returned to the user. If the
36027 ** operation to finalize the journal file fails, then the code still
@@ -35438,15 +36032,30 @@
36032 */
36033 static int pager_end_transaction(Pager *pPager, int hasMaster){
36034 int rc = SQLITE_OK; /* Error code from journal finalization operation */
36035 int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
36036
36037 /* Do nothing if the pager does not have an open write transaction
36038 ** or at least a RESERVED lock. This function may be called when there
36039 ** is no write-transaction active but a RESERVED or greater lock is
36040 ** held under two circumstances:
36041 **
36042 ** 1. After a successful hot-journal rollback, it is called with
36043 ** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK.
36044 **
36045 ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE
36046 ** lock switches back to locking_mode=normal and then executes a
36047 ** read-transaction, this function is called with eState==PAGER_READER
36048 ** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed.
36049 */
36050 assert( assert_pager_state(pPager) );
36051 assert( pPager->eState!=PAGER_ERROR );
36052 if( pPager->eState<PAGER_WRITER_LOCKED && pPager->eLock<RESERVED_LOCK ){
36053 return SQLITE_OK;
36054 }
36055
36056 releaseAllSavepoints(pPager);
 
36057 assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
36058 if( isOpen(pPager->jfd) ){
36059 assert( !pagerUseWal(pPager) );
36060
36061 /* Finalize the journal file. */
@@ -35458,18 +36067,15 @@
36067 rc = SQLITE_OK;
36068 }else{
36069 rc = sqlite3OsTruncate(pPager->jfd, 0);
36070 }
36071 pPager->journalOff = 0;
 
36072 }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
36073 || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
36074 ){
36075 rc = zeroJournalHdr(pPager, hasMaster);
 
36076 pPager->journalOff = 0;
 
36077 }else{
36078 /* This branch may be executed with Pager.journalMode==MEMORY if
36079 ** a hot-journal was just rolled back. In this case the journal
36080 ** file should be closed and deleted. If this connection writes to
36081 ** the database file, it will do so using an in-memory journal.
@@ -35481,52 +36087,80 @@
36087 sqlite3OsClose(pPager->jfd);
36088 if( !pPager->tempFile ){
36089 rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
36090 }
36091 }
36092 }
36093
36094 #ifdef SQLITE_CHECK_PAGES
36095 sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
36096 if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){
36097 PgHdr *p = pager_lookup(pPager, 1);
36098 if( p ){
36099 p->pageHash = 0;
36100 sqlite3PagerUnref(p);
36101 }
36102 }
36103 #endif
36104
36105 sqlite3BitvecDestroy(pPager->pInJournal);
36106 pPager->pInJournal = 0;
36107 pPager->nRec = 0;
36108 sqlite3PcacheCleanAll(pPager->pPCache);
36109 sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
36110
36111 if( pagerUseWal(pPager) ){
36112 /* Drop the WAL write-lock, if any. Also, if the connection was in
36113 ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
36114 ** lock held on the database file.
36115 */
36116 rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
36117 assert( rc2==SQLITE_OK );
36118 }
36119 if( !pPager->exclusiveMode
36120 && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
36121 ){
36122 rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
 
 
 
 
 
 
36123 pPager->changeCountDone = 0;
36124 }
36125 pPager->eState = PAGER_READER;
36126 pPager->setMaster = 0;
 
 
 
 
 
 
 
 
 
 
 
36127
36128 return (rc==SQLITE_OK?rc2:rc);
36129 }
36130
36131 /*
36132 ** Execute a rollback if a transaction is active and unlock the
36133 ** database file.
36134 **
36135 ** If the pager has already entered the ERROR state, do not attempt
36136 ** the rollback at this time. Instead, pager_unlock() is called. The
36137 ** call to pager_unlock() will discard all in-memory pages, unlock
36138 ** the database file and move the pager back to OPEN state. If this
36139 ** means that there is a hot-journal left in the file-system, the next
36140 ** connection to obtain a shared lock on the pager (which may be this one)
36141 ** will roll it back.
36142 **
36143 ** If the pager has not already entered the ERROR state, but an IO or
36144 ** malloc error occurs during a rollback, then this will itself cause
36145 ** the pager to enter the ERROR state. Which will be cleared by the
36146 ** call to pager_unlock(), as described above.
36147 */
36148 static void pagerUnlockAndRollback(Pager *pPager){
36149 if( pPager->eState!=PAGER_ERROR && pPager->eState!=PAGER_OPEN ){
36150 assert( assert_pager_state(pPager) );
36151 if( pPager->eState>=PAGER_WRITER_LOCKED ){
36152 sqlite3BeginBenignMalloc();
36153 sqlite3PagerRollback(pPager);
36154 sqlite3EndBenignMalloc();
36155 }else if( !pPager->exclusiveMode ){
36156 assert( pPager->eState==PAGER_READER );
36157 pager_end_transaction(pPager, 0);
36158 }
36159 }
36160 pager_unlock(pPager);
36161 }
36162
36163 /*
36164 ** Parameter aData must point to a buffer of pPager->pageSize bytes
36165 ** of data. Compute and return a checksum based ont the contents of the
36166 ** page of data and the current value of pPager->cksumInit.
@@ -35574,13 +36208,12 @@
36208 ** Read a single page from either the journal file (if isMainJrnl==1) or
36209 ** from the sub-journal (if isMainJrnl==0) and playback that page.
36210 ** The page begins at offset *pOffset into the file. The *pOffset
36211 ** value is increased to the start of the next page in the journal.
36212 **
36213 ** The main rollback journal uses checksums - the statement journal does
36214 ** not.
 
36215 **
36216 ** If the page number of the page record read from the (sub-)journal file
36217 ** is greater than the current value of Pager.dbSize, then playback is
36218 ** skipped and SQLITE_OK is returned.
36219 **
@@ -35629,10 +36262,21 @@
36262 assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */
36263
36264 aData = pPager->pTmpSpace;
36265 assert( aData ); /* Temp storage must have already been allocated */
36266 assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) );
36267
36268 /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction
36269 ** or savepoint rollback done at the request of the caller) or this is
36270 ** a hot-journal rollback. If it is a hot-journal rollback, the pager
36271 ** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback
36272 ** only reads from the main journal, not the sub-journal.
36273 */
36274 assert( pPager->eState>=PAGER_WRITER_CACHEMOD
36275 || (pPager->eState==PAGER_OPEN && pPager->eLock==EXCLUSIVE_LOCK)
36276 );
36277 assert( pPager->eState>=PAGER_WRITER_CACHEMOD || isMainJrnl );
36278
36279 /* Read the page number and page data from the journal or sub-journal
36280 ** file. Return an error code to the caller if an IO error occurs.
36281 */
36282 jfd = isMainJrnl ? pPager->jfd : pPager->sjfd;
@@ -35666,20 +36310,19 @@
36310 ** rollback, then don't bother to play it back again.
36311 */
36312 if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
36313 return rc;
36314 }
 
36315
36316 /* When playing back page 1, restore the nReserve setting
36317 */
36318 if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){
36319 pPager->nReserve = ((u8*)aData)[20];
36320 pagerReportSize(pPager);
36321 }
36322
36323 /* If the pager is in CACHEMOD state, then there must be a copy of this
36324 ** page in the pager cache. In this case just update the pager cache,
36325 ** not the database file. The page is left marked dirty in this case.
36326 **
36327 ** An exception to the above rule: If the database is in no-sync mode
36328 ** and a page is moved during an incremental vacuum then the page may
@@ -35686,12 +36329,15 @@
36329 ** not be in the pager cache. Later: if a malloc() or IO error occurs
36330 ** during a Movepage() call, then the page may not be in the cache
36331 ** either. So the condition described in the above paragraph is not
36332 ** assert()able.
36333 **
36334 ** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the
36335 ** pager cache if it exists and the main file. The page is then marked
36336 ** not dirty. Since this code is only executed in PAGER_OPEN state for
36337 ** a hot-journal rollback, it is guaranteed that the page-cache is empty
36338 ** if the pager is in OPEN state.
36339 **
36340 ** Ticket #1171: The statement journal might contain page content that is
36341 ** different from the page content at the start of the transaction.
36342 ** This occurs when a page is changed prior to the start of a statement
36343 ** then changed again within the statement. When rolling back such a
@@ -35713,21 +36359,22 @@
36359 pPg = 0;
36360 }else{
36361 pPg = pager_lookup(pPager, pgno);
36362 }
36363 assert( pPg || !MEMDB );
36364 assert( pPager->eState!=PAGER_OPEN || pPg==0 );
36365 PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
36366 PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
36367 (isMainJrnl?"main-journal":"sub-journal")
36368 ));
36369 if( isMainJrnl ){
36370 isSynced = pPager->noSync || (*pOffset <= pPager->journalHdr);
36371 }else{
36372 isSynced = (pPg==0 || 0==(pPg->flags & PGHDR_NEED_SYNC));
36373 }
36374 if( isOpen(pPager->fd)
36375 && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
36376 && isSynced
36377 ){
36378 i64 ofst = (pgno-1)*(i64)pPager->pageSize;
36379 testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
36380 assert( !pagerUseWal(pPager) );
@@ -35799,13 +36446,12 @@
36446 ** database corruption may ensue.
36447 */
36448 assert( !pagerUseWal(pPager) );
36449 sqlite3PcacheMakeClean(pPg);
36450 }
36451 pager_set_pagehash(pPg);
36452
 
36453 /* If this was page 1, then restore the value of Pager.dbFileVers.
36454 ** Do this before any decoding. */
36455 if( pgno==1 ){
36456 memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
36457 }
@@ -35953,14 +36599,14 @@
36599 /*
36600 ** This function is used to change the actual size of the database
36601 ** file in the file-system. This only happens when committing a transaction,
36602 ** or rolling back a transaction (including rolling back a hot-journal).
36603 **
36604 ** If the main database file is not open, or the pager is not in either
36605 ** DBMOD or OPEN state, this function is a no-op. Otherwise, the size
36606 ** of the file is changed to nPage pages (nPage*pPager->pageSize bytes).
36607 ** If the file on disk is currently larger than nPage pages, then use the VFS
36608 ** xTruncate() method to truncate it.
36609 **
36610 ** Or, it might might be the case that the file on disk is smaller than
36611 ** nPage pages. Some operating system implementations can get confused if
36612 ** you try to truncate a file to some size that is larger than it
@@ -35970,12 +36616,18 @@
36616 ** If successful, return SQLITE_OK. If an IO error occurs while modifying
36617 ** the database file, return the error code to the caller.
36618 */
36619 static int pager_truncate(Pager *pPager, Pgno nPage){
36620 int rc = SQLITE_OK;
36621 assert( pPager->eState!=PAGER_ERROR );
36622 assert( pPager->eState!=PAGER_READER );
36623
36624 if( isOpen(pPager->fd)
36625 && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
36626 ){
36627 i64 currentSize, newSize;
36628 assert( pPager->eLock==EXCLUSIVE_LOCK );
36629 /* TODO: Is it safe to use Pager.dbFileSize here? */
36630 rc = sqlite3OsFileSize(pPager->fd, &currentSize);
36631 newSize = pPager->pageSize*(i64)nPage;
36632 if( rc==SQLITE_OK && currentSize!=newSize ){
36633 if( currentSize>newSize ){
@@ -36095,11 +36747,11 @@
36747 /* Figure out how many records are in the journal. Abort early if
36748 ** the journal is empty.
36749 */
36750 assert( isOpen(pPager->jfd) );
36751 rc = sqlite3OsFileSize(pPager->jfd, &szJ);
36752 if( rc!=SQLITE_OK ){
36753 goto end_playback;
36754 }
36755
36756 /* Read the master journal name from the journal, if it is present.
36757 ** If a master journal file name is specified, but the file is not
@@ -36129,11 +36781,11 @@
36781 ** occurs.
36782 */
36783 while( 1 ){
36784 /* Read the next journal header from the journal file. If there are
36785 ** not enough bytes left in the journal file for a complete header, or
36786 ** it is corrupted, then a process must have failed while writing it.
36787 ** This indicates nothing more needs to be rolled back.
36788 */
36789 rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
36790 if( rc!=SQLITE_OK ){
36791 if( rc==SQLITE_DONE ){
@@ -36243,14 +36895,13 @@
36895 if( rc==SQLITE_OK ){
36896 zMaster = pPager->pTmpSpace;
36897 rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
36898 testcase( rc!=SQLITE_OK );
36899 }
36900 if( rc==SQLITE_OK && !pPager->noSync
36901 && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
36902 ){
 
36903 rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
36904 }
36905 if( rc==SQLITE_OK ){
36906 rc = pager_end_transaction(pPager, zMaster[0]!='\0');
36907 testcase( rc!=SQLITE_OK );
@@ -36288,11 +36939,11 @@
36939 Pgno pgno = pPg->pgno; /* Page number to read */
36940 int rc = SQLITE_OK; /* Return code */
36941 int isInWal = 0; /* True if page is in log file */
36942 int pgsz = pPager->pageSize; /* Number of bytes to read */
36943
36944 assert( pPager->eState>=PAGER_READER && !MEMDB );
36945 assert( isOpen(pPager->fd) );
36946
36947 if( NEVER(!isOpen(pPager->fd)) ){
36948 assert( pPager->tempFile );
36949 memset(pPg->pData, 0, pPager->pageSize);
@@ -36435,10 +37086,18 @@
37086 PgHdr *p;
37087 for(p=pList; p; p=p->pDirty){
37088 sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
37089 }
37090 }
37091
37092 #ifdef SQLITE_CHECK_PAGES
37093 {
37094 PgHdr *p;
37095 for(p=pList; p; p=p->pDirty) pager_set_pagehash(p);
37096 }
37097 #endif
37098
37099 return rc;
37100 }
37101
37102 /*
37103 ** Begin a read transaction on the WAL.
@@ -36451,31 +37110,82 @@
37110 static int pagerBeginReadTransaction(Pager *pPager){
37111 int rc; /* Return code */
37112 int changed = 0; /* True if cache must be reset */
37113
37114 assert( pagerUseWal(pPager) );
37115 assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
37116
37117 /* sqlite3WalEndReadTransaction() was not called for the previous
37118 ** transaction in locking_mode=EXCLUSIVE. So call it now. If we
37119 ** are in locking_mode=NORMAL and EndRead() was previously called,
37120 ** the duplicate call is harmless.
37121 */
37122 sqlite3WalEndReadTransaction(pPager->pWal);
37123
37124 rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
37125 if( rc==SQLITE_OK && changed ){
37126 pager_reset(pPager);
37127 }
 
 
 
 
 
 
37128
37129 return rc;
37130 }
37131
37132 /*
37133 ** This function is called as part of the transition from PAGER_OPEN
37134 ** to PAGER_READER state to determine the size of the database file
37135 ** in pages (assuming the page size currently stored in Pager.pageSize).
37136 **
37137 ** If no error occurs, SQLITE_OK is returned and the size of the database
37138 ** in pages is stored in *pnPage. Otherwise, an error code (perhaps
37139 ** SQLITE_IOERR_FSTAT) is returned and *pnPage is left unmodified.
37140 */
37141 static int pagerPagecount(Pager *pPager, Pgno *pnPage){
37142 Pgno nPage; /* Value to return via *pnPage */
37143
37144 /* Query the WAL sub-system for the database size. The WalDbsize()
37145 ** function returns zero if the WAL is not open (i.e. Pager.pWal==0), or
37146 ** if the database size is not available. The database size is not
37147 ** available from the WAL sub-system if the log file is empty or
37148 ** contains no valid committed transactions.
37149 */
37150 assert( pPager->eState==PAGER_OPEN );
37151 assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
37152 nPage = sqlite3WalDbsize(pPager->pWal);
37153
37154 /* If the database size was not available from the WAL sub-system,
37155 ** determine it based on the size of the database file. If the size
37156 ** of the database file is not an integer multiple of the page-size,
37157 ** round down to the nearest page. Except, any file larger than 0
37158 ** bytes in size is considered to contain at least one page.
37159 */
37160 if( nPage==0 ){
37161 i64 n = 0; /* Size of db file in bytes */
37162 assert( isOpen(pPager->fd) || pPager->tempFile );
37163 if( isOpen(pPager->fd) ){
37164 int rc = sqlite3OsFileSize(pPager->fd, &n);
37165 if( rc!=SQLITE_OK ){
37166 return rc;
37167 }
37168 }
37169 nPage = (Pgno)(n / pPager->pageSize);
37170 if( nPage==0 && n>0 ){
37171 nPage = 1;
37172 }
37173 }
37174
37175 /* If the current number of pages in the file is greater than the
37176 ** configured maximum pager number, increase the allowed limit so
37177 ** that the file can be read.
37178 */
37179 if( nPage>pPager->mxPgno ){
37180 pPager->mxPgno = (Pgno)nPage;
37181 }
37182
37183 *pnPage = nPage;
37184 return SQLITE_OK;
37185 }
37186
37187
37188 /*
37189 ** Check if the *-wal file that corresponds to the database opened by pPager
37190 ** exists if the database is not empy, or verify that the *-wal file does
37191 ** not exist (by deleting it) if the database file is empty.
@@ -36485,25 +37195,26 @@
37195 ** if no error occurs, make sure Pager.journalMode is not set to
37196 ** PAGER_JOURNALMODE_WAL.
37197 **
37198 ** Return SQLITE_OK or an error code.
37199 **
 
 
37200 ** The caller must hold a SHARED lock on the database file to call this
37201 ** function. Because an EXCLUSIVE lock on the db file is required to delete
37202 ** a WAL on a none-empty database, this ensures there is no race condition
37203 ** between the xAccess() below and an xDelete() being executed by some
37204 ** other connection.
37205 */
37206 static int pagerOpenWalIfPresent(Pager *pPager){
37207 int rc = SQLITE_OK;
37208 assert( pPager->eState==PAGER_OPEN );
37209 assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
37210
37211 if( !pPager->tempFile ){
37212 int isWal; /* True if WAL file exists */
37213 Pgno nPage; /* Size of the database file */
37214
37215 rc = pagerPagecount(pPager, &nPage);
37216 if( rc ) return rc;
37217 if( nPage==0 ){
37218 rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
37219 isWal = 0;
37220 }else{
@@ -36511,15 +37222,12 @@
37222 pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &isWal
37223 );
37224 }
37225 if( rc==SQLITE_OK ){
37226 if( isWal ){
37227 testcase( sqlite3PcachePagecount(pPager->pPCache)==0 );
37228 rc = sqlite3PagerOpenWal(pPager, 0);
 
 
 
37229 }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
37230 pPager->journalMode = PAGER_JOURNALMODE_DELETE;
37231 }
37232 }
37233 }
@@ -36567,11 +37275,12 @@
37275 i64 szJ; /* Effective size of the main journal */
37276 i64 iHdrOff; /* End of first segment of main-journal records */
37277 int rc = SQLITE_OK; /* Return code */
37278 Bitvec *pDone = 0; /* Bitvec to ensure pages played back only once */
37279
37280 assert( pPager->eState!=PAGER_ERROR );
37281 assert( pPager->eState>=PAGER_WRITER_LOCKED );
37282
37283 /* Allocate a bitvec to use to store the set of pages rolled back */
37284 if( pSavepoint ){
37285 pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
37286 if( !pDone ){
@@ -36706,11 +37415,10 @@
37415 #ifndef SQLITE_OMIT_PAGER_PRAGMAS
37416 SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){
37417 pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
37418 pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
37419 pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL);
 
37420 }
37421 #endif
37422
37423 /*
37424 ** The following global variable is incremented whenever the library
@@ -36788,11 +37496,11 @@
37496 ** Change the page size used by the Pager object. The new page size
37497 ** is passed in *pPageSize.
37498 **
37499 ** If the pager is in the error state when this function is called, it
37500 ** is a no-op. The value returned is the error state error code (i.e.
37501 ** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL).
37502 **
37503 ** Otherwise, if all of the following are true:
37504 **
37505 ** * the new page size (value of *pPageSize) is valid (a power
37506 ** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and
@@ -36812,32 +37520,52 @@
37520 ** If the page size is not changed, either because one of the enumerated
37521 ** conditions above is not true, the pager was in error state when this
37522 ** function was called, or because the memory allocation attempt failed,
37523 ** then *pPageSize is set to the old, retained page size before returning.
37524 */
37525 SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
37526 int rc = SQLITE_OK;
37527
37528 /* It is not possible to do a full assert_pager_state() here, as this
37529 ** function may be called from within PagerOpen(), before the state
37530 ** of the Pager object is internally consistent.
37531 **
37532 ** At one point this function returned an error if the pager was in
37533 ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that
37534 ** there is at least one outstanding page reference, this function
37535 ** is a no-op for that case anyhow.
37536 */
37537
37538 u32 pageSize = *pPageSize;
37539 assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
37540 if( (pPager->memDb==0 || pPager->dbSize==0)
37541 && sqlite3PcacheRefCount(pPager->pPCache)==0
37542 && pageSize && pageSize!=(u32)pPager->pageSize
37543 ){
37544 char *pNew = NULL; /* New temp space */
37545 i64 nByte = 0;
37546
37547 if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){
37548 rc = sqlite3OsFileSize(pPager->fd, &nByte);
37549 }
37550 if( rc==SQLITE_OK ){
37551 pNew = (char *)sqlite3PageMalloc(pageSize);
37552 if( !pNew ) rc = SQLITE_NOMEM;
37553 }
37554
37555 if( rc==SQLITE_OK ){
37556 pager_reset(pPager);
37557 pPager->dbSize = (Pgno)(nByte/pageSize);
37558 pPager->pageSize = pageSize;
37559 sqlite3PageFree(pPager->pTmpSpace);
37560 pPager->pTmpSpace = pNew;
37561 sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
37562 }
37563 }
37564
37565 *pPageSize = pPager->pageSize;
37566 if( rc==SQLITE_OK ){
37567 if( nReserve<0 ) nReserve = pPager->nReserve;
37568 assert( nReserve>=0 && nReserve<1000 );
37569 pPager->nReserve = (i16)nReserve;
37570 pagerReportSize(pPager);
37571 }
@@ -36862,17 +37590,15 @@
37590 ** maximum page count below the current size of the database.
37591 **
37592 ** Regardless of mxPage, return the current maximum page count.
37593 */
37594 SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
 
37595 if( mxPage>0 ){
37596 pPager->mxPgno = mxPage;
37597 }
37598 if( pPager->eState!=PAGER_OPEN && pPager->mxPgno<pPager->dbSize ){
37599 pPager->mxPgno = pPager->dbSize;
 
37600 }
37601 return pPager->mxPgno;
37602 }
37603
37604 /*
@@ -36933,70 +37659,20 @@
37659 }
37660 return rc;
37661 }
37662
37663 /*
37664 ** This function may only be called when a read-transaction is open on
37665 ** the pager. It returns the total number of pages in the database.
37666 **
37667 ** However, if the file is between 1 and <page-size> bytes in size, then
37668 ** this is considered a 1 page file.
37669 */
37670 SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){
37671 assert( pPager->eState>=PAGER_READER );
37672 assert( pPager->eState!=PAGER_WRITER_FINISHED );
37673 *pnPage = (int)pPager->dbSize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37674 }
37675
37676
37677 /*
37678 ** Try to obtain a lock of type locktype on the database file. If
@@ -37013,42 +37689,23 @@
37689 ** variable to locktype before returning.
37690 */
37691 static int pager_wait_on_lock(Pager *pPager, int locktype){
37692 int rc; /* Return code */
37693
 
 
 
 
 
 
 
 
 
 
 
37694 /* Check that this is either a no-op (because the requested lock is
37695 ** already held, or one of the transistions that the busy-handler
37696 ** may be invoked during, according to the comment above
37697 ** sqlite3PagerSetBusyhandler().
37698 */
37699 assert( (pPager->eLock>=locktype)
37700 || (pPager->eLock==NO_LOCK && locktype==SHARED_LOCK)
37701 || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
37702 );
37703
37704 do {
37705 rc = pagerLockDb(pPager, locktype);
37706 }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
 
 
 
 
 
 
 
 
37707 return rc;
37708 }
37709
37710 /*
37711 ** Function assertTruncateConstraint(pPager) checks that one of the
@@ -37089,13 +37746,12 @@
37746 ** function does not actually modify the database file on disk. It
37747 ** just sets the internal state of the pager object so that the
37748 ** truncation will be done when the current transaction is committed.
37749 */
37750 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
 
37751 assert( pPager->dbSize>=nPage );
37752 assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
37753 pPager->dbSize = nPage;
37754 assertTruncateConstraint(pPager);
37755 }
37756
37757
@@ -37141,11 +37797,11 @@
37797 SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
37798 u8 *pTmp = (u8 *)pPager->pTmpSpace;
37799
37800 disable_simulated_io_errors();
37801 sqlite3BeginBenignMalloc();
37802 /* pPager->errCode = 0; */
37803 pPager->exclusiveMode = 0;
37804 #ifndef SQLITE_OMIT_WAL
37805 sqlite3WalClose(pPager->pWal,
37806 (pPager->noSync ? 0 : pPager->sync_flags),
37807 pPager->pageSize, pTmp
@@ -37154,18 +37810,23 @@
37810 #endif
37811 pager_reset(pPager);
37812 if( MEMDB ){
37813 pager_unlock(pPager);
37814 }else{
37815 /* If it is open, sync the journal file before calling UnlockAndRollback.
37816 ** If this is not done, then an unsynced portion of the open journal
37817 ** file may be played back into the database. If a power failure occurs
37818 ** while this is happening, the database could become corrupt.
37819 **
37820 ** If an error occurs while trying to sync the journal, shift the pager
37821 ** into the ERROR state. This causes UnlockAndRollback to unlock the
37822 ** database and close the journal file without attempting to roll it
37823 ** back or finalize it. The next database user will have to do hot-journal
37824 ** rollback before accessing the database file.
37825 */
37826 if( isOpen(pPager->jfd) ){
37827 pager_error(pPager, pagerSyncHotJournal(pPager));
37828 }
37829 pagerUnlockAndRollback(pPager);
37830 }
37831 sqlite3EndBenignMalloc();
37832 enable_simulated_io_errors();
@@ -37206,13 +37867,13 @@
37867 /*
37868 ** Sync the journal. In other words, make sure all the pages that have
37869 ** been written to the journal have actually reached the surface of the
37870 ** disk and can be restored in the event of a hot-journal rollback.
37871 **
37872 ** If the Pager.noSync flag is set, then this function is a no-op.
37873 ** Otherwise, the actions required depend on the journal-mode and the
37874 ** device characteristics of the the file-system, as follows:
37875 **
37876 ** * If the journal file is an in-memory journal file, no action need
37877 ** be taken.
37878 **
37879 ** * Otherwise, if the device does not support the SAFE_APPEND property,
@@ -37232,22 +37893,29 @@
37893 ** <update nRec field>
37894 ** }
37895 ** if( NOT SEQUENTIAL ) xSync(<journal file>);
37896 ** }
37897 **
 
 
 
37898 ** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
37899 ** page currently held in memory before returning SQLITE_OK. If an IO
37900 ** error is encountered, then the IO error code is returned to the caller.
37901 */
37902 static int syncJournal(Pager *pPager, int newHdr){
37903 int rc; /* Return code */
37904
37905 assert( pPager->eState==PAGER_WRITER_CACHEMOD
37906 || pPager->eState==PAGER_WRITER_DBMOD
37907 );
37908 assert( assert_pager_state(pPager) );
37909 assert( !pagerUseWal(pPager) );
37910
37911 rc = sqlite3PagerExclusiveLock(pPager);
37912 if( rc!=SQLITE_OK ) return rc;
37913
37914 if( !pPager->noSync ){
37915 assert( !pPager->tempFile );
37916 if( isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
 
37917 const int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
37918 assert( isOpen(pPager->jfd) );
37919
37920 if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
37921 /* This block deals with an obscure problem. If the last connection
@@ -37318,21 +37986,29 @@
37986 rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags|
37987 (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
37988 );
37989 if( rc!=SQLITE_OK ) return rc;
37990 }
37991
37992 pPager->journalHdr = pPager->journalOff;
37993 if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
37994 pPager->nRec = 0;
37995 rc = writeJournalHdr(pPager);
37996 if( rc!=SQLITE_OK ) return rc;
37997 }
37998 }else{
37999 pPager->journalHdr = pPager->journalOff;
38000 }
38001 }
38002
38003 /* Unless the pager is in noSync mode, the journal file was just
38004 ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on
38005 ** all pages.
38006 */
38007 sqlite3PcacheClearSyncFlags(pPager->pPCache);
38008 pPager->eState = PAGER_WRITER_DBMOD;
38009 assert( assert_pager_state(pPager) );
38010 return SQLITE_OK;
38011 }
38012
38013 /*
38014 ** The argument is the first in a linked list of dirty pages connected
@@ -37365,31 +38041,16 @@
38041 ** If everything is successful, SQLITE_OK is returned. If an IO error
38042 ** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
38043 ** be obtained, SQLITE_BUSY is returned.
38044 */
38045 static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
38046 int rc = SQLITE_OK; /* Return code */
38047
38048 /* This function is only called for rollback pagers in WRITER_DBMOD state. */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38049 assert( !pagerUseWal(pPager) );
38050 assert( pPager->eState==PAGER_WRITER_DBMOD );
38051 assert( pPager->eLock==EXCLUSIVE_LOCK );
38052
38053 /* If the file is a temp-file has not yet been opened, open it now. It
38054 ** is not possible for rc to be other than SQLITE_OK if this branch
38055 ** is taken, as pager_wait_on_lock() is a no-op for temp-files.
38056 */
@@ -37400,13 +38061,14 @@
38061
38062 /* Before the first write, give the VFS a hint of what the final
38063 ** file size will be.
38064 */
38065 assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
38066 if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
38067 sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
38068 sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
38069 pPager->dbHintSize = pPager->dbSize;
38070 }
38071
38072 while( rc==SQLITE_OK && pList ){
38073 Pgno pgno = pList->pgno;
38074
@@ -37419,10 +38081,12 @@
38081 ** set (set by sqlite3PagerDontWrite()).
38082 */
38083 if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
38084 i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */
38085 char *pData; /* Data to write */
38086
38087 assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
38088
38089 /* Encode the database */
38090 CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
38091
38092 /* Write out the page data. */
@@ -37448,13 +38112,11 @@
38112 PAGER_INCR(sqlite3_pager_writedb_count);
38113 PAGER_INCR(pPager->nWrite);
38114 }else{
38115 PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
38116 }
38117 pager_set_pagehash(pList);
 
 
38118 pList = pList->pDirty;
38119 }
38120
38121 return rc;
38122 }
@@ -37562,13 +38224,18 @@
38224 ** pages belonging to the same sector.
38225 **
38226 ** The doNotSpill flag inhibits all cache spilling regardless of whether
38227 ** or not a sync is required. This is set during a rollback.
38228 **
38229 ** Spilling is also prohibited when in an error state since that could
38230 ** lead to database corruption. In the current implementaton it
38231 ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
38232 ** while in the error state, hence it is impossible for this routine to
38233 ** be called in the error state. Nevertheless, we include a NEVER()
38234 ** test for the error state as a safeguard against future changes.
38235 */
38236 if( NEVER(pPager->errCode) ) return SQLITE_OK;
38237 if( pPager->doNotSpill ) return SQLITE_OK;
38238 if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
38239 return SQLITE_OK;
38240 }
38241
@@ -37582,20 +38249,14 @@
38249 rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
38250 }
38251 }else{
38252
38253 /* Sync the journal file if required. */
38254 if( pPg->flags&PGHDR_NEED_SYNC
38255 || pPager->eState==PAGER_WRITER_CACHEMOD
38256 ){
38257 rc = syncJournal(pPager, 1);
 
 
 
 
 
 
38258 }
38259
38260 /* If the page number of this page is larger than the current size of
38261 ** the database image, it may need to be written to the sub-journal.
38262 ** This is because the call to pager_write_pagelist() below will not
@@ -37629,10 +38290,11 @@
38290 rc = subjournalPage(pPg);
38291 }
38292
38293 /* Write the contents of the page out to the database file. */
38294 if( rc==SQLITE_OK ){
38295 assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
38296 rc = pager_write_pagelist(pPager, pPg);
38297 }
38298 }
38299
38300 /* Mark the page as clean. */
@@ -37639,11 +38301,11 @@
38301 if( rc==SQLITE_OK ){
38302 PAGERTRACE(("STRESS %d page %d\n", PAGERID(pPager), pPg->pgno));
38303 sqlite3PcacheMakeClean(pPg);
38304 }
38305
38306 return pager_error(pPager, rc);
38307 }
38308
38309
38310 /*
38311 ** Allocate and initialize a new Pager object and put a pointer to it
@@ -37694,11 +38356,11 @@
38356 char *zPathname = 0; /* Full path to database file */
38357 int nPathname = 0; /* Number of bytes in zPathname */
38358 int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
38359 int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
38360 int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
38361 u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
38362
38363 /* Figure out how much space is required for each journal file-handle
38364 ** (there are two of them, the main journal and the sub-journal). This
38365 ** is the maximum space required for an in-memory journal file handle
38366 ** and a regular journal file-handle. Note that a "regular journal-handle"
@@ -37829,11 +38491,11 @@
38491 assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
38492 if( szPageDflt<pPager->sectorSize ){
38493 if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
38494 szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
38495 }else{
38496 szPageDflt = (u32)pPager->sectorSize;
38497 }
38498 }
38499 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
38500 {
38501 int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
@@ -37857,11 +38519,12 @@
38519 ** This branch is also run for an in-memory database. An in-memory
38520 ** database is the same as a temp-file that is never written out to
38521 ** disk and uses an in-memory rollback journal.
38522 */
38523 tempFile = 1;
38524 pPager->eState = PAGER_READER;
38525 pPager->eLock = EXCLUSIVE_LOCK;
38526 readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
38527 }
38528
38529 /* The following call to PagerSetPagesize() serves to set the value of
38530 ** Pager.pageSize and to allocate the Pager.pTmpSpace buffer.
@@ -37894,27 +38557,27 @@
38557 pPager->useJournal = (u8)useJournal;
38558 pPager->noReadlock = (noReadlock && readOnly) ?1:0;
38559 /* pPager->stmtOpen = 0; */
38560 /* pPager->stmtInUse = 0; */
38561 /* pPager->nRef = 0; */
 
38562 /* pPager->stmtSize = 0; */
38563 /* pPager->stmtJSize = 0; */
38564 /* pPager->nPage = 0; */
38565 pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
38566 /* pPager->state = PAGER_UNLOCK; */
38567 #if 0
38568 assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
38569 #endif
38570 /* pPager->errMask = 0; */
38571 pPager->tempFile = (u8)tempFile;
38572 assert( tempFile==PAGER_LOCKINGMODE_NORMAL
38573 || tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
38574 assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
38575 pPager->exclusiveMode = (u8)tempFile;
38576 pPager->changeCountDone = pPager->tempFile;
38577 pPager->memDb = (u8)memDb;
38578 pPager->readOnly = (u8)readOnly;
 
38579 assert( useJournal || pPager->tempFile );
38580 pPager->noSync = pPager->tempFile;
38581 pPager->fullSync = pPager->noSync ?0:1;
38582 pPager->sync_flags = SQLITE_SYNC_NORMAL;
38583 /* pPager->pFirst = 0; */
@@ -37975,24 +38638,24 @@
38638 sqlite3_vfs * const pVfs = pPager->pVfs;
38639 int rc = SQLITE_OK; /* Return code */
38640 int exists = 1; /* True if a journal file is present */
38641 int jrnlOpen = !!isOpen(pPager->jfd);
38642
 
38643 assert( pPager->useJournal );
38644 assert( isOpen(pPager->fd) );
38645 assert( pPager->eState==PAGER_OPEN );
38646
38647 assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
38648 SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
38649 ));
38650
38651 *pExists = 0;
38652 if( !jrnlOpen ){
38653 rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
38654 }
38655 if( rc==SQLITE_OK && exists ){
38656 int locked = 0; /* True if some process holds a RESERVED lock */
38657
38658 /* Race condition here: Another process might have been holding the
38659 ** the RESERVED lock and have a journal open at the sqlite3OsAccess()
38660 ** call above, but then delete the journal and drop the lock before
38661 ** we get to the following sqlite3OsCheckReservedLock() call. If that
@@ -38000,25 +38663,25 @@
38663 ** in fact there is none. This results in a false-positive which will
38664 ** be dealt with by the playback routine. Ticket #3883.
38665 */
38666 rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
38667 if( rc==SQLITE_OK && !locked ){
38668 Pgno nPage; /* Number of pages in database file */
38669
38670 /* Check the size of the database file. If it consists of 0 pages,
38671 ** then delete the journal file. See the header comment above for
38672 ** the reasoning here. Delete the obsolete journal file under
38673 ** a RESERVED lock to avoid race conditions and to avoid violating
38674 ** [H33020].
38675 */
38676 rc = pagerPagecount(pPager, &nPage);
38677 if( rc==SQLITE_OK ){
38678 if( nPage==0 ){
38679 sqlite3BeginBenignMalloc();
38680 if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
38681 sqlite3OsDelete(pVfs, pPager->zJournal, 0);
38682 pagerUnlockDb(pPager, SHARED_LOCK);
38683 }
38684 sqlite3EndBenignMalloc();
38685 }else{
38686 /* The journal file exists and no other connection has a reserved
38687 ** or greater lock on the database file. Now check that there is
@@ -38067,11 +38730,11 @@
38730 ** has been successfully called. If a shared-lock is already held when
38731 ** this function is called, it is a no-op.
38732 **
38733 ** The following operations are also performed by this function.
38734 **
38735 ** 1) If the pager is currently in PAGER_OPEN state (no lock held
38736 ** on the database file), then an attempt is made to obtain a
38737 ** SHARED lock on the database file. Immediately after obtaining
38738 ** the SHARED lock, the file-system is checked for a hot-journal,
38739 ** which is played back if present. Following any hot-journal
38740 ** rollback, the contents of the cache are validated by checking
@@ -38082,70 +38745,51 @@
38745 ** no outstanding references to any pages, and is in the error state,
38746 ** then an attempt is made to clear the error state by discarding
38747 ** the contents of the page cache and rolling back any open journal
38748 ** file.
38749 **
38750 ** If everything is successful, SQLITE_OK is returned. If an IO error
38751 ** occurs while locking the database, checking for a hot-journal file or
38752 ** rolling back a journal file, the IO error code is returned.
 
 
 
 
 
38753 */
38754 SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
38755 int rc = SQLITE_OK; /* Return code */
 
38756
38757 /* This routine is only called from b-tree and only when there are no
38758 ** outstanding pages. This implies that the pager state should either
38759 ** be OPEN or READER. READER is only possible if the pager is or was in
38760 ** exclusive access mode.
38761 */
38762 assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
38763 assert( assert_pager_state(pPager) );
38764 assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
38765 if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
38766
38767 if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
38768 int bHotJournal = 1; /* True if there exists a hot journal-file */
38769
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38770 assert( !MEMDB );
38771 assert( pPager->noReadlock==0 || pPager->readOnly );
38772
38773 if( pPager->noReadlock==0 ){
 
 
38774 rc = pager_wait_on_lock(pPager, SHARED_LOCK);
38775 if( rc!=SQLITE_OK ){
38776 assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
38777 goto failed;
38778 }
38779 }
 
38780
38781 /* If a journal file exists, and there is no RESERVED lock on the
38782 ** database file, then it either needs to be played back or deleted.
38783 */
38784 if( pPager->eLock<=SHARED_LOCK ){
38785 rc = hasHotJournal(pPager, &bHotJournal);
38786 }
38787 if( rc!=SQLITE_OK ){
38788 goto failed;
38789 }
38790 if( bHotJournal ){
 
38791 /* Get an EXCLUSIVE lock on the database file. At this point it is
38792 ** important that a RESERVED lock is not obtained on the way to the
38793 ** EXCLUSIVE lock. If it were, another process might open the
38794 ** database file, detect the RESERVED lock, and conclude that the
38795 ** database is safe to read while this process is still rolling the
@@ -38153,62 +38797,49 @@
38797 **
38798 ** Because the intermediate RESERVED lock is not requested, any
38799 ** other process attempting to access the database file will get to
38800 ** this point in the code and fail to obtain its own EXCLUSIVE lock
38801 ** on the database file.
38802 **
38803 ** Unless the pager is in locking_mode=exclusive mode, the lock is
38804 ** downgraded to SHARED_LOCK before this function returns.
38805 */
38806 rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
38807 if( rc!=SQLITE_OK ){
38808 goto failed;
38809 }
38810
38811 /* If it is not already open and the file exists on disk, open the
38812 ** journal for read/write access. Write access is required because
38813 ** in exclusive-access mode the file descriptor will be kept open
38814 ** and possibly used for a transaction later on. Also, write-access
38815 ** is usually required to finalize the journal in journal_mode=persist
38816 ** mode (and also for journal_mode=truncate on some systems).
38817 **
38818 ** If the journal does not exist, it usually means that some
38819 ** other connection managed to get in and roll it back before
38820 ** this connection obtained the exclusive lock above. Or, it
38821 ** may mean that the pager was in the error-state when this
38822 ** function was called and the journal file does not exist.
38823 */
38824 if( !isOpen(pPager->jfd) ){
38825 sqlite3_vfs * const pVfs = pPager->pVfs;
38826 int bExists; /* True if journal file exists */
38827 rc = sqlite3OsAccess(
38828 pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
38829 if( rc==SQLITE_OK && bExists ){
38830 int fout = 0;
38831 int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
38832 assert( !pPager->tempFile );
38833 rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
38834 assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
38835 if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
38836 rc = SQLITE_CANTOPEN_BKPT;
38837 sqlite3OsClose(pPager->jfd);
38838 }
38839 }
38840 }
 
 
 
 
 
 
 
 
 
 
 
 
 
38841
38842 /* Playback and delete the journal. Drop the database write
38843 ** lock and reacquire the read lock. Purge the cache before
38844 ** playing back the hot-journal so that we don't end up with
38845 ** an inconsistent cache. Sync the hot journal before playing
@@ -38215,25 +38846,50 @@
38846 ** it back since the process that crashed and left the hot journal
38847 ** probably did not sync it and we are required to always sync
38848 ** the journal before playing it back.
38849 */
38850 if( isOpen(pPager->jfd) ){
38851 assert( rc==SQLITE_OK );
38852 rc = pagerSyncHotJournal(pPager);
38853 if( rc==SQLITE_OK ){
38854 rc = pager_playback(pPager, 1);
38855 pPager->eState = PAGER_OPEN;
38856 }
38857 }else if( !pPager->exclusiveMode ){
38858 pagerUnlockDb(pPager, SHARED_LOCK);
38859 }
38860
38861 if( rc!=SQLITE_OK ){
38862 /* This branch is taken if an error occurs while trying to open
38863 ** or roll back a hot-journal while holding an EXCLUSIVE lock. The
38864 ** pager_unlock() routine will be called before returning to unlock
38865 ** the file. If the unlock attempt fails, then Pager.eLock must be
38866 ** set to UNKNOWN_LOCK (see the comment above the #define for
38867 ** UNKNOWN_LOCK above for an explanation).
38868 **
38869 ** In order to get pager_unlock() to do this, set Pager.eState to
38870 ** PAGER_ERROR now. This is not actually counted as a transition
38871 ** to ERROR state in the state diagram at the top of this file,
38872 ** since we know that the same call to pager_unlock() will very
38873 ** shortly transition the pager object to the OPEN state. Calling
38874 ** assert_pager_state() would fail now, as it should not be possible
38875 ** to be in ERROR state when there are zero outstanding page
38876 ** references.
38877 */
38878 pager_error(pPager, rc);
38879 goto failed;
38880 }
38881
38882 assert( pPager->eState==PAGER_OPEN );
38883 assert( (pPager->eLock==SHARED_LOCK)
38884 || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
38885 );
38886 }
38887
38888 if( !pPager->tempFile
38889 && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0)
38890 ){
38891 /* The shared-lock has just been acquired on the database file
38892 ** and there are already pages in the cache (from a previous
38893 ** read or write transaction). Check to see if the database
38894 ** has been modified. If the database has changed, flush the
38895 ** cache.
@@ -38246,18 +38902,15 @@
38902 **
38903 ** There is a vanishingly small chance that a change will not be
38904 ** detected. The chance of an undetected change is so small that
38905 ** it can be neglected.
38906 */
38907 Pgno nPage = 0;
38908 char dbFileVers[sizeof(pPager->dbFileVers)];
 
38909
38910 rc = pagerPagecount(pPager, &nPage);
38911 if( rc ) goto failed;
 
 
38912
38913 if( nPage>0 ){
38914 IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
38915 rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
38916 if( rc!=SQLITE_OK ){
@@ -38269,22 +38922,34 @@
38922
38923 if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
38924 pager_reset(pPager);
38925 }
38926 }
 
38927
38928 /* If there is a WAL file in the file-system, open this database in WAL
38929 ** mode. Otherwise, the following function call is a no-op.
38930 */
38931 rc = pagerOpenWalIfPresent(pPager);
38932 assert( pPager->pWal==0 || rc==SQLITE_OK );
38933 }
38934
38935 if( pagerUseWal(pPager) ){
38936 assert( rc==SQLITE_OK );
38937 rc = pagerBeginReadTransaction(pPager);
38938 }
38939
38940 if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
38941 rc = pagerPagecount(pPager, &pPager->dbSize);
38942 }
38943
38944 failed:
38945 if( rc!=SQLITE_OK ){
38946 assert( !MEMDB );
38947 pager_unlock(pPager);
38948 assert( pPager->eState==PAGER_OPEN );
38949 }else{
38950 pPager->eState = PAGER_READER;
38951 }
38952 return rc;
38953 }
38954
38955 /*
@@ -38294,13 +38959,11 @@
38959 ** Except, in locking_mode=EXCLUSIVE when there is nothing to in
38960 ** the rollback journal, the unlock is not performed and there is
38961 ** nothing to rollback, so this routine is a no-op.
38962 */
38963 static void pagerUnlockIfUnused(Pager *pPager){
38964 if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
 
 
38965 pagerUnlockAndRollback(pPager);
38966 }
38967 }
38968
38969 /*
@@ -38360,20 +39023,20 @@
39023 int noContent /* Do not bother reading content from disk if true */
39024 ){
39025 int rc;
39026 PgHdr *pPg;
39027
39028 assert( pPager->eState>=PAGER_READER );
39029 assert( assert_pager_state(pPager) );
 
39030
39031 if( pgno==0 ){
39032 return SQLITE_CORRUPT_BKPT;
39033 }
39034
39035 /* If the pager is in the error state, return an error immediately.
39036 ** Otherwise, request the page from the PCache layer. */
39037 if( pPager->errCode!=SQLITE_OK ){
39038 rc = pPager->errCode;
39039 }else{
39040 rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
39041 }
39042
@@ -38395,11 +39058,10 @@
39058 return SQLITE_OK;
39059
39060 }else{
39061 /* The pager cache has created a new page. Its content needs to
39062 ** be initialized. */
 
39063
39064 PAGER_INCR(pPager->nMiss);
39065 pPg = *ppPage;
39066 pPg->pPager = pPager;
39067
@@ -38408,16 +39070,11 @@
39070 if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
39071 rc = SQLITE_CORRUPT_BKPT;
39072 goto pager_acquire_err;
39073 }
39074
39075 if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
 
 
 
 
 
39076 if( pgno>pPager->mxPgno ){
39077 rc = SQLITE_FULL;
39078 goto pager_acquire_err;
39079 }
39080 if( noContent ){
@@ -38443,13 +39100,11 @@
39100 rc = readDbPage(pPg);
39101 if( rc!=SQLITE_OK ){
39102 goto pager_acquire_err;
39103 }
39104 }
39105 pager_set_pagehash(pPg);
 
 
39106 }
39107
39108 return SQLITE_OK;
39109
39110 pager_acquire_err:
@@ -38464,13 +39119,11 @@
39119 }
39120
39121 /*
39122 ** Acquire a page if it is already in the in-memory cache. Do
39123 ** not read the page from disk. Return a pointer to the page,
39124 ** or 0 if the page is not in cache.
 
 
39125 **
39126 ** See also sqlite3PagerGet(). The difference between this routine
39127 ** and sqlite3PagerGet() is that _get() will go to the disk and read
39128 ** in the page if the page is not already in cache. This routine
39129 ** returns NULL if the page is not in cache or if a disk I/O error
@@ -38479,11 +39132,11 @@
39132 SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
39133 PgHdr *pPg = 0;
39134 assert( pPager!=0 );
39135 assert( pgno!=0 );
39136 assert( pPager->pPCache!=0 );
39137 assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR );
39138 sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
39139 return pPg;
39140 }
39141
39142 /*
@@ -38524,73 +39177,71 @@
39177 ** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
39178 ** an IO error code if opening or writing the journal file fails.
39179 */
39180 static int pager_open_journal(Pager *pPager){
39181 int rc = SQLITE_OK; /* Return code */
 
39182 sqlite3_vfs * const pVfs = pPager->pVfs; /* Local cache of vfs pointer */
39183
39184 assert( pPager->eState==PAGER_WRITER_LOCKED );
39185 assert( assert_pager_state(pPager) );
 
39186 assert( pPager->pInJournal==0 );
39187
39188 /* If already in the error state, this function is a no-op. But on
39189 ** the other hand, this routine is never called if we are already in
39190 ** an error state. */
39191 if( NEVER(pPager->errCode) ) return pPager->errCode;
39192
39193 if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
39194 pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
39195 if( pPager->pInJournal==0 ){
39196 return SQLITE_NOMEM;
39197 }
39198
39199 /* Open the journal file if it is not already open. */
39200 if( !isOpen(pPager->jfd) ){
39201 if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
39202 sqlite3MemJournalOpen(pPager->jfd);
39203 }else{
39204 const int flags = /* VFS flags to open journal file */
39205 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
39206 (pPager->tempFile ?
39207 (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
39208 (SQLITE_OPEN_MAIN_JOURNAL)
39209 );
39210 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
39211 rc = sqlite3JournalOpen(
39212 pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
39213 );
39214 #else
39215 rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
39216 #endif
39217 }
39218 assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
39219 }
39220
39221
39222 /* Write the first journal header to the journal file and open
39223 ** the sub-journal if necessary.
39224 */
39225 if( rc==SQLITE_OK ){
39226 /* TODO: Check if all of these are really required. */
39227 pPager->nRec = 0;
39228 pPager->journalOff = 0;
39229 pPager->setMaster = 0;
39230 pPager->journalHdr = 0;
39231 rc = writeJournalHdr(pPager);
39232 }
 
 
 
 
39233 }
39234
39235 if( rc!=SQLITE_OK ){
39236 sqlite3BitvecDestroy(pPager->pInJournal);
39237 pPager->pInJournal = 0;
39238 }else{
39239 assert( pPager->eState==PAGER_WRITER_LOCKED );
39240 pPager->eState = PAGER_WRITER_CACHEMOD;
39241 }
39242
39243 return rc;
39244 }
39245
39246 /*
39247 ** Begin a write-transaction on the specified pager object. If a
@@ -38599,18 +39250,10 @@
39250 ** If the exFlag argument is false, then acquire at least a RESERVED
39251 ** lock on the database file. If exFlag is true, then acquire at least
39252 ** an EXCLUSIVE lock. If such a lock is already held, no locking
39253 ** functions need be called.
39254 **
 
 
 
 
 
 
 
 
39255 ** If the subjInMemory argument is non-zero, then any sub-journal opened
39256 ** within this transaction will be opened as an in-memory file. This
39257 ** has no effect if the sub-journal is already opened (as it may be when
39258 ** running in exclusive mode) or if the transaction does not require a
39259 ** sub-journal. If the subjInMemory argument is zero, then any required
@@ -38617,24 +39260,24 @@
39260 ** sub-journal is implemented in-memory if pPager is an in-memory database,
39261 ** or using a temporary file otherwise.
39262 */
39263 SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
39264 int rc = SQLITE_OK;
39265
39266 if( pPager->errCode ) return pPager->errCode;
39267 assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
39268 pPager->subjInMemory = (u8)subjInMemory;
39269
39270 if( ALWAYS(pPager->eState==PAGER_READER) ){
39271 assert( pPager->pInJournal==0 );
 
39272
39273 if( pagerUseWal(pPager) ){
39274 /* If the pager is configured to use locking_mode=exclusive, and an
39275 ** exclusive lock on the database is not already held, obtain it now.
39276 */
39277 if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
39278 rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
 
39279 if( rc!=SQLITE_OK ){
39280 return rc;
39281 }
39282 sqlite3WalExclusiveMode(pPager->pWal, 1);
39283 }
@@ -38641,56 +39284,44 @@
39284
39285 /* Grab the write lock on the log file. If successful, upgrade to
39286 ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
39287 ** The busy-handler is not invoked if another connection already
39288 ** holds the write-lock. If possible, the upper layer will call it.
 
 
 
 
 
 
39289 */
39290 rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
 
 
 
 
 
 
 
 
39291 }else{
39292 /* Obtain a RESERVED lock on the database file. If the exFlag parameter
39293 ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
39294 ** busy-handler callback can be used when upgrading to the EXCLUSIVE
39295 ** lock, but not when obtaining the RESERVED lock.
39296 */
39297 rc = pagerLockDb(pPager, RESERVED_LOCK);
39298 if( rc==SQLITE_OK && exFlag ){
39299 rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
39300 }
39301 }
39302
39303 if( rc==SQLITE_OK ){
39304 /* Change to WRITER_LOCKED state.
39305 **
39306 ** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD
39307 ** when it has an open transaction, but never to DBMOD or FINISHED.
39308 ** This is because in those states the code to roll back savepoint
39309 ** transactions may copy data from the sub-journal into the database
39310 ** file as well as into the page cache. Which would be incorrect in
39311 ** WAL mode.
39312 */
39313 pPager->eState = PAGER_WRITER_LOCKED;
39314 pPager->dbHintSize = pPager->dbSize;
39315 pPager->dbFileSize = pPager->dbSize;
39316 pPager->dbOrigSize = pPager->dbSize;
39317 pPager->journalOff = 0;
39318 }
39319
39320 assert( rc==SQLITE_OK || pPager->eState==PAGER_READER );
39321 assert( rc!=SQLITE_OK || pPager->eState==PAGER_WRITER_LOCKED );
39322 assert( assert_pager_state(pPager) );
39323 }
39324
39325 PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
39326 return rc;
39327 }
@@ -38705,110 +39336,98 @@
39336 static int pager_write(PgHdr *pPg){
39337 void *pData = pPg->pData;
39338 Pager *pPager = pPg->pPager;
39339 int rc = SQLITE_OK;
39340
39341 /* This routine is not called unless a write-transaction has already
39342 ** been started. The journal file may or may not be open at this point.
39343 ** It is never called in the ERROR state.
39344 */
39345 assert( pPager->eState==PAGER_WRITER_LOCKED
39346 || pPager->eState==PAGER_WRITER_CACHEMOD
39347 || pPager->eState==PAGER_WRITER_DBMOD
39348 );
39349 assert( assert_pager_state(pPager) );
39350
39351 /* If an error has been previously detected, report the same error
39352 ** again. This should not happen, but the check provides robustness. */
 
39353 if( NEVER(pPager->errCode) ) return pPager->errCode;
39354
39355 /* Higher-level routines never call this function if database is not
39356 ** writable. But check anyway, just for robustness. */
39357 if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
39358
 
 
39359 CHECK_PAGE(pPg);
39360
39361 /* Mark the page as dirty. If the page has already been written
39362 ** to the journal then we can return right away.
39363 */
39364 sqlite3PcacheMakeDirty(pPg);
39365 if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
39366 assert( !pagerUseWal(pPager) );
39367 assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
39368 }else{
39369
39370 /* If we get this far, it means that the page needs to be
39371 ** written to the transaction journal or the checkpoint journal
39372 ** or both.
39373 **
39374 ** Higher level routines have already obtained the necessary locks
39375 ** to begin the write-transaction, but the rollback journal might not
39376 ** yet be open. Open it now if this is the case.
39377 */
39378 if( pPager->eState==PAGER_WRITER_LOCKED ){
 
 
 
 
 
39379 rc = pager_open_journal(pPager);
39380 if( rc!=SQLITE_OK ) return rc;
39381 }
39382 assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
39383 assert( assert_pager_state(pPager) );
39384
39385 /* The transaction journal now exists and we have a RESERVED or an
39386 ** EXCLUSIVE lock on the main database file. Write the current page to
39387 ** the transaction journal if it is not there already.
39388 */
39389 if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
39390 assert( pagerUseWal(pPager)==0 );
39391 if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
39392 u32 cksum;
39393 char *pData2;
39394 i64 iOff = pPager->journalOff;
39395
39396 /* We should never write to the journal file the page that
39397 ** contains the database locks. The following assert verifies
39398 ** that we do not. */
39399 assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
39400
39401 assert( pPager->journalHdr<=pPager->journalOff );
39402 CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
39403 cksum = pager_cksum(pPager, (u8*)pData2);
39404
39405 /* Even if an IO or diskfull error occurs while journalling the
39406 ** page in the block above, set the need-sync flag for the page.
39407 ** Otherwise, when the transaction is rolled back, the logic in
39408 ** playback_one_page() will think that the page needs to be restored
39409 ** in the database file. And if an IO error occurs while doing so,
39410 ** then corruption may follow.
39411 */
39412 pPg->flags |= PGHDR_NEED_SYNC;
39413
39414 rc = write32bits(pPager->jfd, iOff, pPg->pgno);
39415 if( rc!=SQLITE_OK ) return rc;
39416 rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
39417 if( rc!=SQLITE_OK ) return rc;
39418 rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
39419 if( rc!=SQLITE_OK ) return rc;
39420
39421 IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
39422 pPager->journalOff, pPager->pageSize));
39423 PAGER_INCR(sqlite3_pager_writej_count);
39424 PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
39425 PAGERID(pPager), pPg->pgno,
39426 ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
39427
39428 pPager->journalOff += 8 + pPager->pageSize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39429 pPager->nRec++;
39430 assert( pPager->pInJournal!=0 );
39431 rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
39432 testcase( rc==SQLITE_NOMEM );
39433 assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
@@ -38816,13 +39435,12 @@
39435 if( rc!=SQLITE_OK ){
39436 assert( rc==SQLITE_NOMEM );
39437 return rc;
39438 }
39439 }else{
39440 if( pPager->eState!=PAGER_WRITER_DBMOD ){
39441 pPg->flags |= PGHDR_NEED_SYNC;
 
39442 }
39443 PAGERTRACE(("APPEND %d page %d needSync=%d\n",
39444 PAGERID(pPager), pPg->pgno,
39445 ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
39446 }
@@ -38838,11 +39456,10 @@
39456 }
39457 }
39458
39459 /* Update the database size and return.
39460 */
 
39461 if( pPager->dbSize<pPg->pgno ){
39462 pPager->dbSize = pPg->pgno;
39463 }
39464 return rc;
39465 }
@@ -38866,10 +39483,14 @@
39483
39484 PgHdr *pPg = pDbPage;
39485 Pager *pPager = pPg->pPager;
39486 Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
39487
39488 assert( pPager->eState>=PAGER_WRITER_LOCKED );
39489 assert( pPager->eState!=PAGER_ERROR );
39490 assert( assert_pager_state(pPager) );
39491
39492 if( nPagePerSector>1 ){
39493 Pgno nPageCount; /* Total number of pages in database file */
39494 Pgno pg1; /* First page of the sector pPg is located on. */
39495 int nPage = 0; /* Number of pages starting at pg1 to journal */
39496 int ii; /* Loop counter */
@@ -38887,23 +39508,21 @@
39508 ** an integer power of 2. It sets variable pg1 to the identifier
39509 ** of the first page of the sector pPg is located on.
39510 */
39511 pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
39512
39513 nPageCount = pPager->dbSize;
39514 if( pPg->pgno>nPageCount ){
39515 nPage = (pPg->pgno - pg1)+1;
39516 }else if( (pg1+nPagePerSector-1)>nPageCount ){
39517 nPage = nPageCount+1-pg1;
39518 }else{
39519 nPage = nPagePerSector;
39520 }
39521 assert(nPage>0);
39522 assert(pg1<=pPg->pgno);
39523 assert((pg1+nPage)>pPg->pgno);
 
 
39524
39525 for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
39526 Pgno pg = pg1+ii;
39527 PgHdr *pPage;
39528 if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
@@ -38911,11 +39530,10 @@
39530 rc = sqlite3PagerGet(pPager, pg, &pPage);
39531 if( rc==SQLITE_OK ){
39532 rc = pager_write(pPage);
39533 if( pPage->flags&PGHDR_NEED_SYNC ){
39534 needSync = 1;
 
39535 }
39536 sqlite3PagerUnref(pPage);
39537 }
39538 }
39539 }else if( (pPage = pager_lookup(pPager, pg))!=0 ){
@@ -38931,19 +39549,18 @@
39549 ** writing to any of these nPage pages may damage the others, the
39550 ** journal file must contain sync()ed copies of all of them
39551 ** before any of them can be written out to the database file.
39552 */
39553 if( rc==SQLITE_OK && needSync ){
39554 assert( !MEMDB );
39555 for(ii=0; ii<nPage; ii++){
39556 PgHdr *pPage = pager_lookup(pPager, pg1+ii);
39557 if( pPage ){
39558 pPage->flags |= PGHDR_NEED_SYNC;
39559 sqlite3PagerUnref(pPage);
39560 }
39561 }
 
39562 }
39563
39564 assert( pPager->doNotSyncSpill==1 );
39565 pPager->doNotSyncSpill--;
39566 }else{
@@ -38981,13 +39598,11 @@
39598 Pager *pPager = pPg->pPager;
39599 if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
39600 PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
39601 IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
39602 pPg->flags |= PGHDR_DONT_WRITE;
39603 pager_set_pagehash(pPg);
 
 
39604 }
39605 }
39606
39607 /*
39608 ** This routine is called to increment the value of the database file
@@ -39005,10 +39620,15 @@
39620 ** by writing an updated version of page 1 using a call to the
39621 ** sqlite3OsWrite() function.
39622 */
39623 static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
39624 int rc = SQLITE_OK;
39625
39626 assert( pPager->eState==PAGER_WRITER_CACHEMOD
39627 || pPager->eState==PAGER_WRITER_DBMOD
39628 );
39629 assert( assert_pager_state(pPager) );
39630
39631 /* Declare and initialize constant integer 'isDirect'. If the
39632 ** atomic-write optimization is enabled in this build, then isDirect
39633 ** is initialized to the value passed as the isDirectMode parameter
39634 ** to this function. Otherwise, it is always set to zero.
@@ -39024,11 +39644,10 @@
39644 UNUSED_PARAMETER(isDirectMode);
39645 #else
39646 # define DIRECT_MODE isDirectMode
39647 #endif
39648
 
39649 if( !pPager->changeCountDone && pPager->dbSize>0 ){
39650 PgHdr *pPgHdr; /* Reference to page 1 */
39651 u32 change_counter; /* Initial value of change-counter field */
39652
39653 assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -39109,13 +39728,17 @@
39728 ** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
39729 ** returned.
39730 */
39731 SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
39732 int rc = SQLITE_OK;
39733 assert( pPager->eState==PAGER_WRITER_CACHEMOD
39734 || pPager->eState==PAGER_WRITER_DBMOD
39735 || pPager->eState==PAGER_WRITER_LOCKED
39736 );
39737 assert( assert_pager_state(pPager) );
39738 if( 0==pagerUseWal(pPager) ){
39739 rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
39740 }
39741 return rc;
39742 }
39743
39744 /*
@@ -39149,26 +39772,33 @@
39772 const char *zMaster, /* If not NULL, the master journal name */
39773 int noSync /* True to omit the xSync on the db file */
39774 ){
39775 int rc = SQLITE_OK; /* Return code */
39776
39777 assert( pPager->eState==PAGER_WRITER_LOCKED
39778 || pPager->eState==PAGER_WRITER_CACHEMOD
39779 || pPager->eState==PAGER_WRITER_DBMOD
39780 || pPager->eState==PAGER_ERROR
39781 );
39782 assert( assert_pager_state(pPager) );
39783
39784 /* If a prior error occurred, report that error again. */
39785 if( NEVER(pPager->errCode) ) return pPager->errCode;
39786
39787 PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
39788 pPager->zFilename, zMaster, pPager->dbSize));
39789
39790 /* If no database changes have been made, return early. */
39791 if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
39792
39793 if( MEMDB ){
39794 /* If this is an in-memory db, or no pages have been written to, or this
39795 ** function has already been called, it is mostly a no-op. However, any
39796 ** backup in progress needs to be restarted.
39797 */
39798 sqlite3BackupRestart(pPager->pBackup);
39799 }else{
39800 if( pagerUseWal(pPager) ){
39801 PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
39802 if( pList ){
39803 rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
39804 (pPager->fullSync ? pPager->sync_flags : 0)
@@ -39207,11 +39837,11 @@
39837 || pPager->journalMode==PAGER_JOURNALMODE_OFF
39838 || pPager->journalMode==PAGER_JOURNALMODE_WAL
39839 );
39840 if( !zMaster && isOpen(pPager->jfd)
39841 && pPager->journalOff==jrnlBufferSize(pPager)
39842 && pPager->dbSize>=pPager->dbOrigSize
39843 && (0==(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
39844 ){
39845 /* Update the db file change counter via the direct-write method. The
39846 ** following call will modify the in-memory representation of page 1
39847 ** to include the updated change counter and then write page 1
@@ -39237,17 +39867,14 @@
39867 ** Before reading the pages with page numbers larger than the
39868 ** current value of Pager.dbSize, set dbSize back to the value
39869 ** that it took at the start of the transaction. Otherwise, the
39870 ** calls to sqlite3PagerGet() return zeroed pages instead of
39871 ** reading data from the database file.
 
 
 
39872 */
39873 #ifndef SQLITE_OMIT_AUTOVACUUM
39874 if( pPager->dbSize<pPager->dbOrigSize
39875 && pPager->journalMode!=PAGER_JOURNALMODE_OFF
39876 ){
39877 Pgno i; /* Iterator variable */
39878 const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
39879 const Pgno dbSize = pPager->dbSize; /* Database image size */
39880 pPager->dbSize = pPager->dbOrigSize;
@@ -39270,18 +39897,24 @@
39897 ** or if zMaster is NULL (no master journal), then this call is a no-op.
39898 */
39899 rc = writeMasterJournal(pPager, zMaster);
39900 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39901
39902 /* Sync the journal file and write all dirty pages to the database.
39903 ** If the atomic-update optimization is being used, this sync will not
39904 ** create the journal file or perform any real IO.
39905 **
39906 ** Because the change-counter page was just modified, unless the
39907 ** atomic-update optimization is used it is almost certain that the
39908 ** journal requires a sync here. However, in locking_mode=exclusive
39909 ** on a system under memory pressure it is just possible that this is
39910 ** not the case. In this case it is likely enough that the redundant
39911 ** xSync() call will be changed to a no-op by the OS anyhow.
39912 */
39913 rc = syncJournal(pPager, 0);
39914 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39915
 
39916 rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
39917 if( rc!=SQLITE_OK ){
39918 assert( rc!=SQLITE_IOERR_BLOCKED );
39919 goto commit_phase_one_exit;
39920 }
@@ -39290,11 +39923,11 @@
39923 /* If the file on disk is not the same size as the database image,
39924 ** then use pager_truncate to grow or shrink the file here.
39925 */
39926 if( pPager->dbSize!=pPager->dbFileSize ){
39927 Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
39928 assert( pPager->eState==PAGER_WRITER_DBMOD );
39929 rc = pager_truncate(pPager, nNew);
39930 if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
39931 }
39932
39933 /* Finally, sync the database file. */
@@ -39301,16 +39934,16 @@
39934 if( !pPager->noSync && !noSync ){
39935 rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
39936 }
39937 IOTRACE(("DBSYNC %p\n", pPager))
39938 }
 
 
 
39939 }
39940
39941 commit_phase_one_exit:
39942 if( rc==SQLITE_OK && !pagerUseWal(pPager) ){
39943 pPager->eState = PAGER_WRITER_FINISHED;
39944 }
39945 return rc;
39946 }
39947
39948
39949 /*
@@ -39334,16 +39967,15 @@
39967 /* This routine should not be called if a prior error has occurred.
39968 ** But if (due to a coding error elsewhere in the system) it does get
39969 ** called, just return the same error code without doing anything. */
39970 if( NEVER(pPager->errCode) ) return pPager->errCode;
39971
39972 assert( pPager->eState==PAGER_WRITER_LOCKED
39973 || pPager->eState==PAGER_WRITER_FINISHED
39974 || (pagerUseWal(pPager) && pPager->eState==PAGER_WRITER_CACHEMOD)
39975 );
39976 assert( assert_pager_state(pPager) );
 
39977
39978 /* An optimization. If the database was not actually modified during
39979 ** this transaction, the pager is running in exclusive-mode and is
39980 ** using persistent journals, then this function is a no-op.
39981 **
@@ -39352,106 +39984,80 @@
39984 ** a hot-journal during hot-journal rollback, 0 changes will be made
39985 ** to the database file. So there is no need to zero the journal
39986 ** header. Since the pager is in exclusive mode, there is no need
39987 ** to drop any locks either.
39988 */
39989 if( pPager->eState==PAGER_WRITER_LOCKED
39990 && pPager->exclusiveMode
39991 && pPager->journalMode==PAGER_JOURNALMODE_PERSIST
39992 ){
39993 assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
39994 pPager->eState = PAGER_READER;
39995 return SQLITE_OK;
39996 }
39997
39998 PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
 
39999 rc = pager_end_transaction(pPager, pPager->setMaster);
40000 return pager_error(pPager, rc);
40001 }
40002
40003 /*
40004 ** If a write transaction is open, then all changes made within the
40005 ** transaction are reverted and the current write-transaction is closed.
40006 ** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR
40007 ** state if an error occurs.
40008 **
40009 ** If the pager is already in PAGER_ERROR state when this function is called,
40010 ** it returns Pager.errCode immediately. No work is performed in this case.
40011 **
40012 ** Otherwise, in rollback mode, this function performs two functions:
40013 **
40014 ** 1) It rolls back the journal file, restoring all database file and
40015 ** in-memory cache pages to the state they were in when the transaction
40016 ** was opened, and
40017 **
40018 ** 2) It finalizes the journal file, so that it is not used for hot
40019 ** rollback at any point in the future.
40020 **
40021 ** Finalization of the journal file (task 2) is only performed if the
40022 ** rollback is successful.
40023 **
40024 ** In WAL mode, all cache-entries containing data modified within the
40025 ** current transaction are either expelled from the cache or reverted to
40026 ** their pre-transaction state by re-reading data from the database or
40027 ** WAL files. The WAL transaction is then closed.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40028 */
40029 SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
40030 int rc = SQLITE_OK; /* Return code */
40031 PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
40032
40033 /* PagerRollback() is a no-op if called in READER or OPEN state. If
40034 ** the pager is already in the ERROR state, the rollback is not
40035 ** attempted here. Instead, the error code is returned to the caller.
40036 */
40037 assert( assert_pager_state(pPager) );
40038 if( pPager->eState==PAGER_ERROR ) return pPager->errCode;
40039 if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
40040
40041 if( pagerUseWal(pPager) ){
40042 int rc2;
 
40043 rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
40044 rc2 = pager_end_transaction(pPager, pPager->setMaster);
40045 if( rc==SQLITE_OK ) rc = rc2;
40046 }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
40047 rc = pager_end_transaction(pPager, 0);
40048 }else{
40049 rc = pager_playback(pPager, 0);
40050 }
40051
40052 assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
40053 assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
40054
40055 /* If an error occurs during a ROLLBACK, we can no longer trust the pager
40056 ** cache. So call pager_error() on the way out to make any error persistent.
40057 */
40058 return pager_error(pPager, rc);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40059 }
40060
40061 /*
40062 ** Return TRUE if the database file is opened read-only. Return FALSE
40063 ** if the database is (in theory) writable.
@@ -39493,12 +40099,12 @@
40099 SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
40100 static int a[11];
40101 a[0] = sqlite3PcacheRefCount(pPager->pPCache);
40102 a[1] = sqlite3PcachePagecount(pPager->pPCache);
40103 a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
40104 a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
40105 a[4] = pPager->eState;
40106 a[5] = pPager->errCode;
40107 a[6] = pPager->nHit;
40108 a[7] = pPager->nMiss;
40109 a[8] = 0; /* Used to be pPager->nOvfl */
40110 a[9] = pPager->nRead;
@@ -39525,18 +40131,17 @@
40131 ** returned. Otherwise, SQLITE_OK.
40132 */
40133 SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
40134 int rc = SQLITE_OK; /* Return code */
40135 int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
40136
40137 assert( pPager->eState>=PAGER_WRITER_LOCKED );
40138 assert( assert_pager_state(pPager) );
40139
40140 if( nSavepoint>nCurrent && pPager->useJournal ){
40141 int ii; /* Iterator variable */
40142 PagerSavepoint *aNew; /* New Pager.aSavepoint array */
 
 
 
 
40143
40144 /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
40145 ** if the allocation fails. Otherwise, zero the new portion in case a
40146 ** malloc failure occurs while populating it in the for(...) loop below.
40147 */
@@ -39549,18 +40154,18 @@
40154 memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
40155 pPager->aSavepoint = aNew;
40156
40157 /* Populate the PagerSavepoint structures just allocated. */
40158 for(ii=nCurrent; ii<nSavepoint; ii++){
40159 aNew[ii].nOrig = pPager->dbSize;
40160 if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
40161 aNew[ii].iOffset = pPager->journalOff;
40162 }else{
40163 aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
40164 }
40165 aNew[ii].iSubRec = pPager->nSubRec;
40166 aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
40167 if( !aNew[ii].pInSavepoint ){
40168 return SQLITE_NOMEM;
40169 }
40170 if( pagerUseWal(pPager) ){
40171 sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
@@ -39603,16 +40208,16 @@
40208 ** This function may return SQLITE_NOMEM if a memory allocation fails,
40209 ** or an IO error code if an IO error occurs while rolling back a
40210 ** savepoint. If no errors occur, SQLITE_OK is returned.
40211 */
40212 SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
40213 int rc = pPager->errCode; /* Return code */
40214
40215 assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
40216 assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
40217
40218 if( rc==SQLITE_OK && iSavepoint<pPager->nSavepoint ){
40219 int ii; /* Iterator variable */
40220 int nNew; /* Number of remaining savepoints after this op. */
40221
40222 /* Figure out how many savepoints will still be active after this
40223 ** operation. Store this value in nNew. Then free resources associated
@@ -39644,12 +40249,12 @@
40249 else if( pagerUseWal(pPager) || isOpen(pPager->jfd) ){
40250 PagerSavepoint *pSavepoint = (nNew==0)?0:&pPager->aSavepoint[nNew-1];
40251 rc = pagerPlaybackSavepoint(pPager, pSavepoint);
40252 assert(rc!=SQLITE_DONE);
40253 }
 
40254 }
40255
40256 return rc;
40257 }
40258
40259 /*
40260 ** Return the full pathname of the database file.
@@ -39743,10 +40348,14 @@
40348 Pgno needSyncPgno = 0; /* Old value of pPg->pgno, if sync is required */
40349 int rc; /* Return code */
40350 Pgno origPgno; /* The original page number */
40351
40352 assert( pPg->nRef>0 );
40353 assert( pPager->eState==PAGER_WRITER_CACHEMOD
40354 || pPager->eState==PAGER_WRITER_DBMOD
40355 );
40356 assert( assert_pager_state(pPager) );
40357
40358 /* In order to be able to rollback, an in-memory database must journal
40359 ** the page we are moving from.
40360 */
40361 if( MEMDB ){
@@ -39792,15 +40401,14 @@
40401 */
40402 if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
40403 needSyncPgno = pPg->pgno;
40404 assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
40405 assert( pPg->flags&PGHDR_DIRTY );
 
40406 }
40407
40408 /* If the cache contains a page with page-number pgno, remove it
40409 ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for
40410 ** page pgno before the 'move' operation, it needs to be retained
40411 ** for the page moved there.
40412 */
40413 pPg->flags &= ~PGHDR_NEED_SYNC;
40414 pPgOld = pager_lookup(pPager, pgno);
@@ -39808,67 +40416,59 @@
40416 if( pPgOld ){
40417 pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
40418 if( MEMDB ){
40419 /* Do not discard pages from an in-memory database since we might
40420 ** need to rollback later. Just move the page out of the way. */
 
40421 sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
40422 }else{
40423 sqlite3PcacheDrop(pPgOld);
40424 }
40425 }
40426
40427 origPgno = pPg->pgno;
40428 sqlite3PcacheMove(pPg, pgno);
40429 sqlite3PcacheMakeDirty(pPg);
40430
40431 /* For an in-memory database, make sure the original page continues
40432 ** to exist, in case the transaction needs to roll back. Use pPgOld
40433 ** as the original page since it has already been allocated.
40434 */
40435 if( MEMDB ){
40436 assert( pPgOld );
40437 sqlite3PcacheMove(pPgOld, origPgno);
40438 sqlite3PagerUnref(pPgOld);
40439 }
40440
40441 if( needSyncPgno ){
40442 /* If needSyncPgno is non-zero, then the journal file needs to be
40443 ** sync()ed before any data is written to database file page needSyncPgno.
40444 ** Currently, no such page exists in the page-cache and the
40445 ** "is journaled" bitvec flag has been set. This needs to be remedied by
40446 ** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC
40447 ** flag.
40448 **
40449 ** If the attempt to load the page into the page-cache fails, (due
40450 ** to a malloc() or IO failure), clear the bit in the pInJournal[]
40451 ** array. Otherwise, if the page is loaded and written again in
40452 ** this transaction, it may be written to the database file before
40453 ** it is synced into the journal file. This way, it may end up in
40454 ** the journal file twice, but that is not a problem.
 
 
 
40455 */
40456 PgHdr *pPgHdr;
 
40457 rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
40458 if( rc!=SQLITE_OK ){
40459 if( needSyncPgno<=pPager->dbOrigSize ){
40460 assert( pPager->pTmpSpace!=0 );
40461 sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace);
40462 }
40463 return rc;
40464 }
 
 
40465 pPgHdr->flags |= PGHDR_NEED_SYNC;
40466 sqlite3PcacheMakeDirty(pPgHdr);
40467 sqlite3PagerUnref(pPgHdr);
40468 }
40469
 
 
 
 
 
 
 
 
 
 
40470 return SQLITE_OK;
40471 }
40472 #endif
40473
40474 /*
@@ -39929,10 +40529,17 @@
40529 **
40530 ** The returned indicate the current (possibly updated) journal-mode.
40531 */
40532 SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
40533 u8 eOld = pPager->journalMode; /* Prior journalmode */
40534
40535 #ifdef SQLITE_DEBUG
40536 /* The print_pager_state() routine is intended to be used by the debugger
40537 ** only. We invoke it once here to suppress a compiler warning. */
40538 print_pager_state(pPager);
40539 #endif
40540
40541
40542 /* The eMode parameter is always valid */
40543 assert( eMode==PAGER_JOURNALMODE_DELETE
40544 || eMode==PAGER_JOURNALMODE_TRUNCATE
40545 || eMode==PAGER_JOURNALMODE_PERSIST
@@ -39955,24 +40562,17 @@
40562 eMode = eOld;
40563 }
40564 }
40565
40566 if( eMode!=eOld ){
 
 
 
 
 
 
 
 
40567
40568 /* Change the journal mode. */
40569 assert( pPager->eState!=PAGER_ERROR );
40570 pPager->journalMode = (u8)eMode;
40571
40572 /* When transistioning from TRUNCATE or PERSIST to any other journal
40573 ** mode except WAL, unless the pager is in locking_mode=exclusive mode,
40574 ** delete the journal file.
40575 */
40576 assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
40577 assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
40578 assert( (PAGER_JOURNALMODE_DELETE & 5)==0 );
@@ -39989,28 +40589,34 @@
40589 **
40590 ** Before deleting the journal file, obtain a RESERVED lock on the
40591 ** database file. This ensures that the journal file is not deleted
40592 ** while it is in use by some other client.
40593 */
40594 sqlite3OsClose(pPager->jfd);
40595 if( pPager->eLock>=RESERVED_LOCK ){
 
 
 
 
 
 
 
 
40596 sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
40597 }else{
40598 int rc = SQLITE_OK;
40599 int state = pPager->eState;
40600 assert( state==PAGER_OPEN || state==PAGER_READER );
40601 if( state==PAGER_OPEN ){
40602 rc = sqlite3PagerSharedLock(pPager);
40603 }
40604 if( pPager->eState==PAGER_READER ){
40605 assert( rc==SQLITE_OK );
40606 rc = pagerLockDb(pPager, RESERVED_LOCK);
40607 }
40608 if( rc==SQLITE_OK ){
40609 sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
40610 }
40611 if( rc==SQLITE_OK && state==PAGER_READER ){
40612 pagerUnlockDb(pPager, SHARED_LOCK);
40613 }else if( state==PAGER_OPEN ){
40614 pager_unlock(pPager);
40615 }
40616 assert( state==pPager->eState );
40617 }
 
 
 
 
 
 
40618 }
40619 }
40620
40621 /* Return the new journal mode */
40622 return (int)pPager->journalMode;
@@ -40027,11 +40633,12 @@
40633 ** Return TRUE if the pager is in a state where it is OK to change the
40634 ** journalmode. Journalmode changes can only happen when the database
40635 ** is unmodified.
40636 */
40637 SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
40638 assert( assert_pager_state(pPager) );
40639 if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
40640 if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
40641 return 1;
40642 }
40643
40644 /*
@@ -40092,39 +40699,46 @@
40699 **
40700 ** If the pager passed as the first argument is open on a real database
40701 ** file (not a temp file or an in-memory database), and the WAL file
40702 ** is not already open, make an attempt to open it now. If successful,
40703 ** return SQLITE_OK. If an error occurs or the VFS used by the pager does
40704 ** not support the xShmXXX() methods, return an error code. *pbOpen is
40705 ** not modified in either case.
40706 **
40707 ** If the pager is open on a temp-file (or in-memory database), or if
40708 ** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK
40709 ** without doing anything.
40710 */
40711 SQLITE_PRIVATE int sqlite3PagerOpenWal(
40712 Pager *pPager, /* Pager object */
40713 int *pbOpen /* OUT: Set to true if call is a no-op */
40714 ){
40715 int rc = SQLITE_OK; /* Return code */
40716
40717 assert( assert_pager_state(pPager) );
40718 assert( pPager->eState==PAGER_OPEN || pbOpen );
40719 assert( pPager->eState==PAGER_READER || !pbOpen );
40720 assert( pbOpen==0 || *pbOpen==0 );
40721 assert( pbOpen!=0 || (!pPager->tempFile && !pPager->pWal) );
40722
40723 if( !pPager->tempFile && !pPager->pWal ){
40724 if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;
40725
40726 /* Close any rollback journal previously open */
40727 sqlite3OsClose(pPager->jfd);
40728
40729 /* Open the connection to the log file. If this operation fails,
40730 ** (e.g. due to malloc() failure), unlock the database file and
40731 ** return an error code.
40732 */
40733 rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
40734 if( rc==SQLITE_OK ){
40735 pPager->journalMode = PAGER_JOURNALMODE_WAL;
40736 pPager->eState = PAGER_OPEN;
40737 }
40738 }else{
40739 *pbOpen = 1;
40740 }
40741
40742 return rc;
40743 }
40744
@@ -40146,11 +40760,11 @@
40760 ** it may need to be checkpointed before the connection can switch to
40761 ** rollback mode. Open it now so this can happen.
40762 */
40763 if( !pPager->pWal ){
40764 int logexists = 0;
40765 rc = pagerLockDb(pPager, SHARED_LOCK);
40766 if( rc==SQLITE_OK ){
40767 rc = sqlite3OsAccess(
40768 pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
40769 );
40770 }
@@ -40162,21 +40776,21 @@
40776
40777 /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
40778 ** the database file, the log and log-summary files will be deleted.
40779 */
40780 if( rc==SQLITE_OK && pPager->pWal ){
40781 rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
40782 if( rc==SQLITE_OK ){
40783 rc = sqlite3WalClose(pPager->pWal,
40784 (pPager->noSync ? 0 : pPager->sync_flags),
40785 pPager->pageSize, (u8*)pPager->pTmpSpace
40786 );
40787 pPager->pWal = 0;
40788 }else{
40789 /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
40790 ** that we did get back to SHARED. */
40791 pagerUnlockDb(pPager, SQLITE_LOCK_SHARED);
40792 }
40793 }
40794 return rc;
40795 }
40796
@@ -40492,18 +41106,22 @@
41106 /*
41107 ** The following object holds a copy of the wal-index header content.
41108 **
41109 ** The actual header in the wal-index consists of two copies of this
41110 ** object.
41111 **
41112 ** The szPage value can be any power of 2 between 512 and 32768, inclusive.
41113 ** Or it can be 1 to represent a 65536-byte page. The latter case was
41114 ** added in 3.7.1 when support for 64K pages was added.
41115 */
41116 struct WalIndexHdr {
41117 u32 iVersion; /* Wal-index version */
41118 u32 unused; /* Unused (padding) field */
41119 u32 iChange; /* Counter incremented each transaction */
41120 u8 isInit; /* 1 when initialized */
41121 u8 bigEndCksum; /* True if checksums in WAL are big-endian */
41122 u16 szPage; /* Database page size in bytes. 1==64K */
41123 u32 mxFrame; /* Index of last valid frame in the WAL */
41124 u32 nPage; /* Size of database in pages */
41125 u32 aFrameCksum[2]; /* Checksum of last frame in log */
41126 u32 aSalt[2]; /* Two salt values copied from WAL header */
41127 u32 aCksum[2]; /* Checksum over all prior fields */
@@ -40610,11 +41228,11 @@
41228 sqlite3_file *pDbFd; /* File handle for the database file */
41229 sqlite3_file *pWalFd; /* File handle for WAL file */
41230 u32 iCallback; /* Value to pass to log callback (or 0) */
41231 int nWiData; /* Size of array apWiData */
41232 volatile u32 **apWiData; /* Pointer to wal-index content in memory */
41233 u32 szPage; /* Database page size */
41234 i16 readLock; /* Which read lock is being held. -1 for none */
41235 u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
41236 u8 writeLock; /* True if in a write transaction */
41237 u8 ckptLock; /* True if holding a checkpoint lock */
41238 u8 readOnly; /* True if the WAL file is open read-only */
@@ -41281,11 +41899,11 @@
41899 || szPage<512
41900 ){
41901 goto finished;
41902 }
41903 pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
41904 pWal->szPage = szPage;
41905 pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
41906 memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
41907
41908 /* Verify that the WAL header checksum is correct */
41909 walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
@@ -41331,11 +41949,13 @@
41949
41950 /* If nTruncate is non-zero, this is a commit record. */
41951 if( nTruncate ){
41952 pWal->hdr.mxFrame = iFrame;
41953 pWal->hdr.nPage = nTruncate;
41954 pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
41955 testcase( szPage<=32768 );
41956 testcase( szPage>=65536 );
41957 aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
41958 aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
41959 }
41960 }
41961
@@ -41356,10 +41976,21 @@
41976 */
41977 pInfo = walCkptInfo(pWal);
41978 pInfo->nBackfill = 0;
41979 pInfo->aReadMark[0] = 0;
41980 for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
41981
41982 /* If more than one frame was recovered from the log file, report an
41983 ** event via sqlite3_log(). This is to help with identifying performance
41984 ** problems caused by applications routinely shutting down without
41985 ** checkpointing the log file.
41986 */
41987 if( pWal->hdr.nPage ){
41988 sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s",
41989 pWal->hdr.nPage, pWal->zWalName
41990 );
41991 }
41992 }
41993
41994 recovery_error:
41995 WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
41996 walUnlockExclusive(pWal, iLock, nLock);
@@ -41716,18 +42347,22 @@
42347 int sync_flags, /* Flags for OsSync() (or 0) */
42348 int nBuf, /* Size of zBuf in bytes */
42349 u8 *zBuf /* Temporary buffer to use */
42350 ){
42351 int rc; /* Return code */
42352 int szPage; /* Database page-size */
42353 WalIterator *pIter = 0; /* Wal iterator context */
42354 u32 iDbpage = 0; /* Next database page to write */
42355 u32 iFrame = 0; /* Wal frame containing data for iDbpage */
42356 u32 mxSafeFrame; /* Max frame that can be backfilled */
42357 u32 mxPage; /* Max database page to write */
42358 int i; /* Loop counter */
42359 volatile WalCkptInfo *pInfo; /* The checkpoint status information */
42360
42361 szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
42362 testcase( szPage<=32768 );
42363 testcase( szPage>=65536 );
42364 if( pWal->hdr.mxFrame==0 ) return SQLITE_OK;
42365
42366 /* Allocate the iterator */
42367 rc = walIteratorInit(pWal, &pIter);
42368 if( rc!=SQLITE_OK ){
@@ -41734,11 +42369,11 @@
42369 return rc;
42370 }
42371 assert( pIter );
42372
42373 /*** TODO: Move this test out to the caller. Make it an assert() here ***/
42374 if( szPage!=nBuf ){
42375 rc = SQLITE_CORRUPT_BKPT;
42376 goto walcheckpoint_out;
42377 }
42378
42379 /* Compute in mxSafeFrame the index of the last frame of the WAL that is
@@ -41745,10 +42380,11 @@
42380 ** safe to write into the database. Frames beyond mxSafeFrame might
42381 ** overwrite database pages that are in use by active readers and thus
42382 ** cannot be backfilled from the WAL.
42383 */
42384 mxSafeFrame = pWal->hdr.mxFrame;
42385 mxPage = pWal->hdr.nPage;
42386 pInfo = walCkptInfo(pWal);
42387 for(i=1; i<WAL_NREADER; i++){
42388 u32 y = pInfo->aReadMark[i];
42389 if( mxSafeFrame>=y ){
42390 assert( y<=pWal->hdr.mxFrame );
@@ -41765,22 +42401,34 @@
42401 }
42402
42403 if( pInfo->nBackfill<mxSafeFrame
42404 && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
42405 ){
42406 i64 nSize; /* Current size of database file */
42407 u32 nBackfill = pInfo->nBackfill;
42408
42409 /* Sync the WAL to disk */
42410 if( sync_flags ){
42411 rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
42412 }
42413
42414 /* If the database file may grow as a result of this checkpoint, hint
42415 ** about the eventual size of the db file to the VFS layer.
42416 */
42417 if( rc==SQLITE_OK ){
42418 i64 nReq = ((i64)mxPage * szPage);
42419 rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
42420 if( rc==SQLITE_OK && nSize<nReq ){
42421 sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
42422 }
42423 }
42424
42425 /* Iterate through the contents of the WAL, copying data to the db file. */
42426 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
42427 i64 iOffset;
42428 assert( walFramePgno(pWal, iFrame)==iDbpage );
42429 if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
42430 iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
42431 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
42432 rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
42433 if( rc!=SQLITE_OK ) break;
42434 iOffset = (iDbpage-1)*(i64)szPage;
@@ -41883,11 +42531,11 @@
42531 WalIndexHdr volatile *aHdr; /* Header in shared memory */
42532
42533 /* The first page of the wal-index must be mapped at this point. */
42534 assert( pWal->nWiData>0 && pWal->apWiData[0] );
42535
42536 /* Read the header. This might happen concurrently with a write to the
42537 ** same area of shared memory on a different CPU in a SMP,
42538 ** meaning it is possible that an inconsistent snapshot is read
42539 ** from the file. If this happens, return non-zero.
42540 **
42541 ** There are two copies of the header at the beginning of the wal-index.
@@ -41912,11 +42560,13 @@
42560 }
42561
42562 if( memcmp(&pWal->hdr, &h1, sizeof(WalIndexHdr)) ){
42563 *pChanged = 1;
42564 memcpy(&pWal->hdr, &h1, sizeof(WalIndexHdr));
42565 pWal->szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
42566 testcase( pWal->szPage<=32768 );
42567 testcase( pWal->szPage>=65536 );
42568 }
42569
42570 /* The header was successfully read. Return zero. */
42571 return 0;
42572 }
@@ -42231,10 +42881,11 @@
42881 /*
42882 ** Finish with a read transaction. All this does is release the
42883 ** read-lock.
42884 */
42885 SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
42886 sqlite3WalEndWriteTransaction(pWal);
42887 if( pWal->readLock>=0 ){
42888 walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
42889 pWal->readLock = -1;
42890 }
42891 }
@@ -42341,11 +42992,17 @@
42992
42993 /* If iRead is non-zero, then it is the log frame number that contains the
42994 ** required page. Read and return data from the log file.
42995 */
42996 if( iRead ){
42997 int sz;
42998 i64 iOffset;
42999 sz = pWal->hdr.szPage;
43000 sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
43001 testcase( sz<=32768 );
43002 testcase( sz>=65536 );
43003 iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
43004 *pInWal = 1;
43005 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
43006 return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
43007 }
43008
@@ -42353,15 +43010,17 @@
43010 return SQLITE_OK;
43011 }
43012
43013
43014 /*
43015 ** Return the size of the database in pages (or zero, if unknown).
43016 */
43017 SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
43018 if( pWal && ALWAYS(pWal->readLock>=0) ){
43019 return pWal->hdr.nPage;
43020 }
43021 return 0;
43022 }
43023
43024
43025 /*
43026 ** This function starts a write transaction on the WAL.
@@ -42433,11 +43092,11 @@
43092 ** Otherwise, if the callback function does not return an error, this
43093 ** function returns SQLITE_OK.
43094 */
43095 SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
43096 int rc = SQLITE_OK;
43097 if( ALWAYS(pWal->writeLock) ){
43098 Pgno iMax = pWal->hdr.mxFrame;
43099 Pgno iFrame;
43100
43101 /* Restore the clients cache of the wal-index header to the state it
43102 ** was in before the client began writing to the database.
@@ -42622,11 +43281,11 @@
43281 memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
43282 walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
43283 sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
43284 sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
43285
43286 pWal->szPage = szPage;
43287 pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
43288 pWal->hdr.aFrameCksum[0] = aCksum[0];
43289 pWal->hdr.aFrameCksum[1] = aCksum[1];
43290
43291 rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
@@ -42717,11 +43376,13 @@
43376 rc = walIndexAppend(pWal, iFrame, pLast->pgno);
43377 }
43378
43379 if( rc==SQLITE_OK ){
43380 /* Update the private copy of the header. */
43381 pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
43382 testcase( szPage<=32768 );
43383 testcase( szPage>=65536 );
43384 pWal->hdr.mxFrame = iFrame;
43385 if( isCommit ){
43386 pWal->hdr.iChange++;
43387 pWal->hdr.nPage = nTruncate;
43388 }
@@ -42929,11 +43590,11 @@
43590 **
43591 ** FORMAT DETAILS
43592 **
43593 ** The file is divided into pages. The first page is called page 1,
43594 ** the second is page 2, and so forth. A page number of zero indicates
43595 ** "no such page". The page size can be any power of 2 between 512 and 65536.
43596 ** Each page can be either a btree page, a freelist page, an overflow
43597 ** page, or a pointer-map page.
43598 **
43599 ** The first page is always a btree page. The first 100 bytes of the first
43600 ** page contain a special header (the "file header") that describes the file.
@@ -43295,18 +43956,18 @@
43956 u8 initiallyEmpty; /* Database is empty at start of transaction */
43957 #ifndef SQLITE_OMIT_AUTOVACUUM
43958 u8 autoVacuum; /* True if auto-vacuum is enabled */
43959 u8 incrVacuum; /* True if incr-vacuum is enabled */
43960 #endif
 
 
43961 u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
43962 u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
43963 u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
43964 u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
43965 u8 inTransaction; /* Transaction state */
43966 u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
43967 u32 pageSize; /* Total number of bytes on a page */
43968 u32 usableSize; /* Number of usable bytes on each page */
43969 int nTransaction; /* Number of open transactions (read + write) */
43970 u32 nPage; /* Number of pages in the database */
43971 void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
43972 void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
43973 sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
@@ -43901,11 +44562,20 @@
44562 # define TRACE(X) if(sqlite3BtreeTrace){printf X;fflush(stdout);}
44563 #else
44564 # define TRACE(X)
44565 #endif
44566
44567 /*
44568 ** Extract a 2-byte big-endian integer from an array of unsigned bytes.
44569 ** But if the value is zero, make it 65536.
44570 **
44571 ** This routine is used to extract the "offset to cell content area" value
44572 ** from the header of a btree page. If the page size is 65536 and the page
44573 ** is empty, the offset should be 65536, but the 2-byte value stores zero.
44574 ** This routine makes the necessary adjustment to 65536.
44575 */
44576 #define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
44577
44578 #ifndef SQLITE_OMIT_SHARED_CACHE
44579 /*
44580 ** A list of BtShared objects that are eligible for participation
44581 ** in shared cache. This variable has file scope during normal builds,
@@ -44590,15 +45260,20 @@
45260 #ifndef SQLITE_OMIT_AUTOVACUUM
45261 /*
45262 ** Given a page number of a regular database page, return the page
45263 ** number for the pointer-map page that contains the entry for the
45264 ** input page number.
45265 **
45266 ** Return 0 (not a valid page) for pgno==1 since there is
45267 ** no pointer map associated with page 1. The integrity_check logic
45268 ** requires that ptrmapPageno(*,1)!=1.
45269 */
45270 static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
45271 int nPagesPerMapPage;
45272 Pgno iPtrMap, ret;
45273 assert( sqlite3_mutex_held(pBt->mutex) );
45274 if( pgno<2 ) return 0;
45275 nPagesPerMapPage = (pBt->usableSize/5)+1;
45276 iPtrMap = (pgno-2)/nPagesPerMapPage;
45277 ret = (iPtrMap*nPagesPerMapPage) + 2;
45278 if( ret==PENDING_BYTE_PAGE(pBt) ){
45279 ret++;
@@ -45023,21 +45698,21 @@
45698 assert( nByte < usableSize-8 );
45699
45700 nFrag = data[hdr+7];
45701 assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
45702 gap = pPage->cellOffset + 2*pPage->nCell;
45703 top = get2byteNotZero(&data[hdr+5]);
45704 if( gap>top ) return SQLITE_CORRUPT_BKPT;
45705 testcase( gap+2==top );
45706 testcase( gap+1==top );
45707 testcase( gap==top );
45708
45709 if( nFrag>=60 ){
45710 /* Always defragment highly fragmented pages */
45711 rc = defragmentPage(pPage);
45712 if( rc ) return rc;
45713 top = get2byteNotZero(&data[hdr+5]);
45714 }else if( gap+2<=top ){
45715 /* Search the freelist looking for a free slot big enough to satisfy
45716 ** the request. The allocation is made from the first free slot in
45717 ** the list that is large enough to accomadate it.
45718 */
@@ -45075,11 +45750,11 @@
45750 */
45751 testcase( gap+2+nByte==top );
45752 if( gap+2+nByte>top ){
45753 rc = defragmentPage(pPage);
45754 if( rc ) return rc;
45755 top = get2byteNotZero(&data[hdr+5]);
45756 assert( gap+nByte<=top );
45757 }
45758
45759
45760 /* Allocate memory from the gap in between the cell pointer array
@@ -45241,28 +45916,28 @@
45916 if( !pPage->isInit ){
45917 u16 pc; /* Address of a freeblock within pPage->aData[] */
45918 u8 hdr; /* Offset to beginning of page header */
45919 u8 *data; /* Equal to pPage->aData */
45920 BtShared *pBt; /* The main btree structure */
45921 int usableSize; /* Amount of usable space on each page */
45922 u16 cellOffset; /* Offset from start of page to first cell pointer */
45923 int nFree; /* Number of unused bytes on the page */
45924 int top; /* First byte of the cell content area */
45925 int iCellFirst; /* First allowable cell or freeblock offset */
45926 int iCellLast; /* Last possible cell or freeblock offset */
45927
45928 pBt = pPage->pBt;
45929
45930 hdr = pPage->hdrOffset;
45931 data = pPage->aData;
45932 if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
45933 assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
45934 pPage->maskPage = (u16)(pBt->pageSize - 1);
45935 pPage->nOverflow = 0;
45936 usableSize = pBt->usableSize;
45937 pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
45938 top = get2byteNotZero(&data[hdr+5]);
45939 pPage->nCell = get2byte(&data[hdr+3]);
45940 if( pPage->nCell>MX_CELL(pBt) ){
45941 /* To many cells for a single page. The page must be corrupt */
45942 return SQLITE_CORRUPT_BKPT;
45943 }
@@ -45357,17 +46032,17 @@
46032 data[hdr] = (char)flags;
46033 first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
46034 memset(&data[hdr+1], 0, 4);
46035 data[hdr+7] = 0;
46036 put2byte(&data[hdr+5], pBt->usableSize);
46037 pPage->nFree = (u16)(pBt->usableSize - first);
46038 decodeFlags(pPage, flags);
46039 pPage->hdrOffset = hdr;
46040 pPage->cellOffset = first;
46041 pPage->nOverflow = 0;
46042 assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
46043 pPage->maskPage = (u16)(pBt->pageSize - 1);
46044 pPage->nCell = 0;
46045 pPage->isInit = 1;
46046 }
46047
46048
@@ -45671,11 +46346,11 @@
46346 pBt->pPage1 = 0;
46347 pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
46348 #ifdef SQLITE_SECURE_DELETE
46349 pBt->secureDelete = 1;
46350 #endif
46351 pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
46352 if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
46353 || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
46354 pBt->pageSize = 0;
46355 #ifndef SQLITE_OMIT_AUTOVACUUM
46356 /* If the magic name ":memory:" will create an in-memory database, then
@@ -45985,11 +46660,11 @@
46660 assert( nReserve>=0 && nReserve<=255 );
46661 if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
46662 ((pageSize-1)&pageSize)==0 ){
46663 assert( (pageSize & 7)==0 );
46664 assert( !pBt->pPage1 && !pBt->pCursor );
46665 pBt->pageSize = (u32)pageSize;
46666 freeTempSpace(pBt);
46667 }
46668 rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
46669 pBt->usableSize = pBt->pageSize - (u16)nReserve;
46670 if( iFix ) pBt->pageSizeFixed = 1;
@@ -46120,19 +46795,17 @@
46795
46796 /* Do some checking to help insure the file we opened really is
46797 ** a valid database file.
46798 */
46799 nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
46800 sqlite3PagerPagecount(pBt->pPager, &nPageFile);
 
 
46801 if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
46802 nPage = nPageFile;
46803 }
46804 if( nPage>0 ){
46805 u32 pageSize;
46806 u32 usableSize;
46807 u8 *page1 = pPage1->aData;
46808 rc = SQLITE_NOTADB;
46809 if( memcmp(page1, zMagicHeader, 16)!=0 ){
46810 goto page1_init_failed;
46811 }
@@ -46179,28 +46852,29 @@
46852 ** version 3.6.0, we require them to be fixed.
46853 */
46854 if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
46855 goto page1_init_failed;
46856 }
46857 pageSize = (page1[16]<<8) | (page1[17]<<16);
46858 if( ((pageSize-1)&pageSize)!=0
46859 || pageSize>SQLITE_MAX_PAGE_SIZE
46860 || pageSize<=256
46861 ){
46862 goto page1_init_failed;
46863 }
46864 assert( (pageSize & 7)==0 );
46865 usableSize = pageSize - page1[20];
46866 if( (u32)pageSize!=pBt->pageSize ){
46867 /* After reading the first page of the database assuming a page size
46868 ** of BtShared.pageSize, we have discovered that the page-size is
46869 ** actually pageSize. Unlock the database, leave pBt->pPage1 at
46870 ** zero and return SQLITE_OK. The caller will call this function
46871 ** again with the correct page-size.
46872 */
46873 releasePage(pPage1);
46874 pBt->usableSize = usableSize;
46875 pBt->pageSize = pageSize;
46876 freeTempSpace(pBt);
46877 rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
46878 pageSize-usableSize);
46879 return rc;
46880 }
@@ -46209,12 +46883,12 @@
46883 goto page1_init_failed;
46884 }
46885 if( usableSize<480 ){
46886 goto page1_init_failed;
46887 }
46888 pBt->pageSize = pageSize;
46889 pBt->usableSize = usableSize;
46890 #ifndef SQLITE_OMIT_AUTOVACUUM
46891 pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
46892 pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
46893 #endif
46894 }
@@ -46226,18 +46900,18 @@
46900 ** 2-byte pointer to the cell
46901 ** 4-byte child pointer
46902 ** 9-byte nKey value
46903 ** 4-byte nData value
46904 ** 4-byte overflow page pointer
46905 ** So a cell consists of a 2-byte pointer, a header which is as much as
46906 ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
46907 ** page pointer.
46908 */
46909 pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23);
46910 pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
46911 pBt->maxLeaf = (u16)(pBt->usableSize - 35);
46912 pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
46913 assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
46914 pBt->pPage1 = pPage1;
46915 pBt->nPage = nPage;
46916 return SQLITE_OK;
46917
@@ -46286,11 +46960,12 @@
46960 data = pP1->aData;
46961 rc = sqlite3PagerWrite(pP1->pDbPage);
46962 if( rc ) return rc;
46963 memcpy(data, zMagicHeader, sizeof(zMagicHeader));
46964 assert( sizeof(zMagicHeader)==16 );
46965 data[16] = (u8)((pBt->pageSize>>8)&0xff);
46966 data[17] = (u8)((pBt->pageSize>>16)&0xff);
46967 data[18] = 1;
46968 data[19] = 1;
46969 assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize);
46970 data[20] = (u8)(pBt->pageSize - pBt->usableSize);
46971 data[21] = 64;
@@ -48297,13 +48972,13 @@
48972 c = +1;
48973 }
48974 pCur->validNKey = 1;
48975 pCur->info.nKey = nCellKey;
48976 }else{
48977 /* The maximum supported page-size is 65536 bytes. This means that
48978 ** the maximum number of record bytes stored on an index B-Tree
48979 ** page is less than 16384 bytes and may be stored as a 2-byte
48980 ** varint. This information is used to attempt to avoid parsing
48981 ** the entire cell by checking for the cases where the record is
48982 ** stored entirely within the b-tree page by inspecting the first
48983 ** 2 bytes of the cell.
48984 */
@@ -48662,10 +49337,14 @@
49337 }
49338 if( k==0 ){
49339 if( !pPrevTrunk ){
49340 memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
49341 }else{
49342 rc = sqlite3PagerWrite(pPrevTrunk->pDbPage);
49343 if( rc!=SQLITE_OK ){
49344 goto end_allocate_page;
49345 }
49346 memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
49347 }
49348 }else{
49349 /* The trunk page is required by the caller but it contains
49350 ** pointers to free-list leaves. The first leaf becomes a trunk
@@ -48968,11 +49647,11 @@
49647 BtShared *pBt = pPage->pBt;
49648 CellInfo info;
49649 Pgno ovflPgno;
49650 int rc;
49651 int nOvfl;
49652 u32 ovflPageSize;
49653
49654 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49655 btreeParseCellPtr(pPage, pCell, &info);
49656 if( info.iOverflow==0 ){
49657 return SQLITE_OK; /* No overflow pages. Return without doing anything */
@@ -49193,11 +49872,11 @@
49872 **
49873 ** "sz" must be the number of bytes in the cell.
49874 */
49875 static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
49876 int i; /* Loop counter */
49877 u32 pc; /* Offset to cell content of cell being deleted */
49878 u8 *data; /* pPage->aData */
49879 u8 *ptr; /* Used to move bytes around within data[] */
49880 int rc; /* The return code */
49881 int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
49882
@@ -49211,11 +49890,11 @@
49890 ptr = &data[pPage->cellOffset + 2*idx];
49891 pc = get2byte(ptr);
49892 hdr = pPage->hdrOffset;
49893 testcase( pc==get2byte(&data[hdr+5]) );
49894 testcase( pc+sz==pPage->pBt->usableSize );
49895 if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
49896 *pRC = SQLITE_CORRUPT_BKPT;
49897 return;
49898 }
49899 rc = freeSpace(pPage, pc, sz);
49900 if( rc ){
@@ -49268,11 +49947,11 @@
49947 int nSkip = (iChild ? 4 : 0);
49948
49949 if( *pRC ) return;
49950
49951 assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
49952 assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
49953 assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
49954 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
49955 /* The cell should normally be sized correctly. However, when moving a
49956 ** malformed cell from a leaf page to an interior page, if the cell size
49957 ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
@@ -49348,16 +50027,16 @@
50027 const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
50028 const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
50029
50030 assert( pPage->nOverflow==0 );
50031 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
50032 assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921);
50033 assert( sqlite3PagerIswriteable(pPage->pDbPage) );
50034
50035 /* Check that the page has just been zeroed by zeroPage() */
50036 assert( pPage->nCell==0 );
50037 assert( get2byteNotZero(&data[hdr+5])==nUsable );
50038
50039 pCellptr = &data[pPage->cellOffset + nCell*2];
50040 cellbody = nUsable;
50041 for(i=nCell-1; i>=0; i--){
50042 pCellptr -= 2;
@@ -49419,10 +50098,11 @@
50098
50099 assert( sqlite3_mutex_held(pPage->pBt->mutex) );
50100 assert( sqlite3PagerIswriteable(pParent->pDbPage) );
50101 assert( pPage->nOverflow==1 );
50102
50103 /* This error condition is now caught prior to reaching this function */
50104 if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
50105
50106 /* Allocate a new page. This page will become the right-sibling of
50107 ** pPage. Make the parent page writable, so that the new divider cell
50108 ** may be inserted. If both these operations are successful, proceed.
@@ -49748,11 +50428,11 @@
50428 ** In this case, temporarily copy the cell into the aOvflSpace[]
50429 ** buffer. It will be copied out again as soon as the aSpace[] buffer
50430 ** is allocated. */
50431 if( pBt->secureDelete ){
50432 int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
50433 if( (iOff+szNew[i])>(int)pBt->usableSize ){
50434 rc = SQLITE_CORRUPT_BKPT;
50435 memset(apOld, 0, (i+1)*sizeof(MemPage*));
50436 goto balance_cleanup;
50437 }else{
50438 memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
@@ -49827,11 +50507,11 @@
50507 u8 *pTemp;
50508 assert( nCell<nMaxCells );
50509 szCell[nCell] = sz;
50510 pTemp = &aSpace1[iSpace1];
50511 iSpace1 += sz;
50512 assert( sz<=pBt->maxLocal+23 );
50513 assert( iSpace1<=pBt->pageSize );
50514 memcpy(pTemp, apDiv[i], sz);
50515 apCell[nCell] = pTemp+leafCorrection;
50516 assert( leafCorrection==0 || leafCorrection==4 );
50517 szCell[nCell] = szCell[nCell] - leafCorrection;
@@ -50073,11 +50753,11 @@
50753 assert(leafCorrection==4);
50754 sz = cellSizePtr(pParent, pCell);
50755 }
50756 }
50757 iOvflSpace += sz;
50758 assert( sz<=pBt->maxLocal+23 );
50759 assert( iOvflSpace<=pBt->pageSize );
50760 insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
50761 if( rc!=SQLITE_OK ) goto balance_cleanup;
50762 assert( sqlite3PagerIswriteable(pParent->pDbPage) );
50763
@@ -51317,11 +51997,11 @@
51997 #ifndef SQLITE_OMIT_AUTOVACUUM
51998 if( pCheck->pBt->autoVacuum ){
51999 checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
52000 }
52001 #endif
52002 if( n>(int)pCheck->pBt->usableSize/4-2 ){
52003 checkAppendMsg(pCheck, zContext,
52004 "freelist leaf count too big on page %d", iPage);
52005 N--;
52006 }else{
52007 for(i=0; i<n; i++){
@@ -51528,24 +52208,24 @@
52208 hdr = pPage->hdrOffset;
52209 hit = sqlite3PageMalloc( pBt->pageSize );
52210 if( hit==0 ){
52211 pCheck->mallocFailed = 1;
52212 }else{
52213 int contentOffset = get2byteNotZero(&data[hdr+5]);
52214 assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
52215 memset(hit+contentOffset, 0, usableSize-contentOffset);
52216 memset(hit, 1, contentOffset);
52217 nCell = get2byte(&data[hdr+3]);
52218 cellStart = hdr + 12 - 4*pPage->leaf;
52219 for(i=0; i<nCell; i++){
52220 int pc = get2byte(&data[cellStart+i*2]);
52221 u32 size = 65536;
52222 int j;
52223 if( pc<=usableSize-4 ){
52224 size = cellSizePtr(pPage, &data[pc]);
52225 }
52226 if( (int)(pc+size-1)>=usableSize ){
52227 checkAppendMsg(pCheck, 0,
52228 "Corruption detected in cell %d on page %d",i,iPage);
52229 }else{
52230 for(j=pc+size-1; j>=pc; j--) hit[j]++;
52231 }
@@ -52164,10 +52844,19 @@
52844 ** page sizes of the source and destination differ.
52845 */
52846 if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
52847 rc = SQLITE_READONLY;
52848 }
52849
52850 #ifdef SQLITE_HAS_CODEC
52851 /* Backup is not possible if the page size of the destination is changing
52852 ** a a codec is in use.
52853 */
52854 if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){
52855 rc = SQLITE_READONLY;
52856 }
52857 #endif
52858
52859 /* This loop runs once for each destination page spanned by the source
52860 ** page. For each iteration, variable iOff is set to the byte offset
52861 ** of the destination page.
52862 */
@@ -55769,12 +56458,21 @@
56458 mrc = p->rc & 0xff;
56459 assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
56460 isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
56461 || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
56462 if( isSpecialError ){
56463 /* If the query was read-only and the error code is SQLITE_INTERRUPT,
56464 ** no rollback is necessary. Otherwise, at least a savepoint
56465 ** transaction must be rolled back to restore the database to a
56466 ** consistent state.
56467 **
56468 ** Even if the statement is read-only, it is important to perform
56469 ** a statement or transaction rollback operation. If the error
56470 ** occured while writing to the journal, sub-journal or database
56471 ** file as part of an effort to free up cache space (see function
56472 ** pagerStress() in pager.c), the rollback is required to restore
56473 ** the pager to a consistent state.
56474 */
56475 if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
56476 if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
56477 eStatementOp = SAVEPOINT_ROLLBACK;
56478 }else{
@@ -63200,11 +63898,11 @@
63898 ** If P5 is non-zero then the key value is increased by an epsilon
63899 ** prior to the comparison. This make the opcode work like IdxGT except
63900 ** that if the key from register P3 is a prefix of the key in the cursor,
63901 ** the result is false whereas it would be true with IdxGT.
63902 */
63903 /* Opcode: IdxLT P1 P2 P3 P4 P5
63904 **
63905 ** The P4 register values beginning with P3 form an unpacked index
63906 ** key that omits the ROWID. Compare this key value against the index
63907 ** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
63908 **
@@ -67547,22 +68245,23 @@
68245 assert( z[0]=='?' );
68246 pExpr->iColumn = (ynVar)(++pParse->nVar);
68247 }else if( z[0]=='?' ){
68248 /* Wildcard of the form "?nnn". Convert "nnn" to an integer and
68249 ** use it as the variable number */
68250 i64 i;
68251 int bOk = sqlite3Atoi64(&z[1], &i);
68252 pExpr->iColumn = (ynVar)i;
68253 testcase( i==0 );
68254 testcase( i==1 );
68255 testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
68256 testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] );
68257 if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
68258 sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
68259 db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
68260 }
68261 if( i>pParse->nVar ){
68262 pParse->nVar = (int)i;
68263 }
68264 }else{
68265 /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable
68266 ** number as the prior appearance of the same name, or if the name
68267 ** has never appeared before, reuse the same variable number
@@ -72092,10 +72791,11 @@
72791 }
72792 }
72793 sqlite3DbFree(db, pIdx->aSample);
72794 }
72795 #else
72796 UNUSED_PARAMETER(db);
72797 UNUSED_PARAMETER(pIdx);
72798 #endif
72799 }
72800
72801 /*
@@ -87738,11 +88438,11 @@
88438 ** be sent.
88439 **
88440 ** regReturn is the number of the register holding the subroutine
88441 ** return address.
88442 **
88443 ** If regPrev>0 then it is the first register in a vector that
88444 ** records the previous output. mem[regPrev] is a flag that is false
88445 ** if there has been no previous output. If regPrev>0 then code is
88446 ** generated to suppress duplicates. pKeyInfo is used for comparing
88447 ** keys.
88448 **
@@ -88435,16 +89135,17 @@
89135 ** (1) The subquery and the outer query do not both use aggregates.
89136 **
89137 ** (2) The subquery is not an aggregate or the outer query is not a join.
89138 **
89139 ** (3) The subquery is not the right operand of a left outer join
89140 ** (Originally ticket #306. Strengthened by ticket #3300)
89141 **
89142 ** (4) The subquery is not DISTINCT.
89143 **
89144 ** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT
89145 ** sub-queries that were excluded from this optimization. Restriction
89146 ** (4) has since been expanded to exclude all DISTINCT subqueries.
89147 **
89148 ** (6) The subquery does not use aggregates or the outer query is not
89149 ** DISTINCT.
89150 **
89151 ** (7) The subquery has a FROM clause.
@@ -88460,13 +89161,13 @@
89161 ** (11) The subquery and the outer query do not both have ORDER BY clauses.
89162 **
89163 ** (**) Not implemented. Subsumed into restriction (3). Was previously
89164 ** a separate restriction deriving from ticket #350.
89165 **
89166 ** (13) The subquery and outer query do not both use LIMIT.
89167 **
89168 ** (14) The subquery does not use OFFSET.
89169 **
89170 ** (15) The outer query is not part of a compound select or the
89171 ** subquery does not have a LIMIT clause.
89172 ** (See ticket #2339 and ticket [02a8e81d44]).
89173 **
@@ -88553,13 +89254,13 @@
89254 if( pSub->pOffset ) return 0; /* Restriction (14) */
89255 if( p->pRightmost && pSub->pLimit ){
89256 return 0; /* Restriction (15) */
89257 }
89258 if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
89259 if( pSub->selFlags & SF_Distinct ) return 0; /* Restriction (5) */
89260 if( pSub->pLimit && (pSrc->nSrc>1 || isAgg) ){
89261 return 0; /* Restrictions (8)(9) */
89262 }
89263 if( (p->selFlags & SF_Distinct)!=0 && subqueryIsAgg ){
89264 return 0; /* Restriction (6) */
89265 }
89266 if( p->pOrderBy && pSub->pOrderBy ){
@@ -93225,11 +93926,11 @@
93926 pParse->pNewTable->aCol = 0;
93927 }
93928 db->pVTab = 0;
93929 }else{
93930 sqlite3Error(db, SQLITE_ERROR, zErr);
93931 sqlite3DbFree(db, zErr);
93932 rc = SQLITE_ERROR;
93933 }
93934 pParse->declareVtab = 0;
93935
93936 if( pParse->pVdbe ){
@@ -96859,39 +97560,39 @@
97560 **
97561 ** This case is also used when there are no WHERE clause
97562 ** constraints but an index is selected anyway, in order
97563 ** to force the output order to conform to an ORDER BY.
97564 */
97565 static const u8 aStartOp[] = {
97566 0,
97567 0,
97568 OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
97569 OP_Last, /* 3: (!start_constraints && startEq && bRev) */
97570 OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
97571 OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
97572 OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
97573 OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
97574 };
97575 static const u8 aEndOp[] = {
97576 OP_Noop, /* 0: (!end_constraints) */
97577 OP_IdxGE, /* 1: (end_constraints && !bRev) */
97578 OP_IdxLT /* 2: (end_constraints && bRev) */
97579 };
97580 int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
97581 int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
97582 int regBase; /* Base register holding constraint values */
97583 int r1; /* Temp register */
97584 WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
97585 WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
97586 int startEq; /* True if range start uses ==, >= or <= */
97587 int endEq; /* True if range end uses ==, >= or <= */
97588 int start_constraints; /* Start of range is constrained */
97589 int nConstraint; /* Number of constraint terms */
97590 Index *pIdx; /* The index we will be using */
97591 int iIdxCur; /* The VDBE cursor for the index */
97592 int nExtraReg = 0; /* Number of extra registers needed */
97593 int op; /* Instruction opcode */
97594 char *zStartAff; /* Affinity for start of range constraint */
97595 char *zEndAff; /* Affinity for end of range constraint */
97596
97597 pIdx = pLevel->plan.u.pIdx;
97598 iIdxCur = pLevel->iIdxCur;
@@ -108720,11 +109421,11 @@
109421 ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following
109422 ** two forward declarations are for functions declared in these files
109423 ** used to retrieve the respective implementations.
109424 **
109425 ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed
109426 ** to by the argument to point to the "simple" tokenizer implementation.
109427 ** Function ...PorterTokenizerModule() sets *pModule to point to the
109428 ** porter tokenizer/stemmer implementation.
109429 */
109430 SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
109431 SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
@@ -108922,11 +109623,11 @@
109623 ** is defined to accept an argument of type char, and always returns 0 for
109624 ** any values that fall outside of the range of the unsigned char type (i.e.
109625 ** negative values).
109626 */
109627 static int fts3isspace(char c){
109628 return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
109629 }
109630
109631 /*
109632 ** Extract the next token from buffer z (length n) using the tokenizer
109633 ** and other information (column names etc.) in pParse. Create an Fts3Expr
@@ -111309,10 +112010,13 @@
112010
112011
112012 static int simpleDelim(simple_tokenizer *t, unsigned char c){
112013 return c<0x80 && t->delim[c];
112014 }
112015 static int fts3_isalnum(int x){
112016 return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z');
112017 }
112018
112019 /*
112020 ** Create a new tokenizer instance.
112021 */
112022 static int simpleCreate(
@@ -111343,11 +112047,11 @@
112047 }
112048 } else {
112049 /* Mark non-alphanumeric ASCII characters as delimiters */
112050 int i;
112051 for(i=1; i<0x80; i++){
112052 t->delim[i] = !fts3_isalnum(i) ? -1 : 0;
112053 }
112054 }
112055
112056 *ppTokenizer = &t->base;
112057 return SQLITE_OK;
@@ -111449,11 +112153,11 @@
112153 for(i=0; i<n; i++){
112154 /* TODO(shess) This needs expansion to handle UTF-8
112155 ** case-insensitivity.
112156 */
112157 unsigned char ch = p[iStartOffset+i];
112158 c->pToken[i] = (char)((ch>='A' && ch<='Z') ? ch-'A'+'a' : ch);
112159 }
112160 *ppToken = c->pToken;
112161 *pnBytes = n;
112162 *piStartOffset = iStartOffset;
112163 *piEndOffset = c->iOffset;
@@ -116355,15 +117059,14 @@
117059 ** least desirable):
117060 **
117061 ** idxNum idxStr Strategy
117062 ** ------------------------------------------------
117063 ** 1 Unused Direct lookup by rowid.
117064 ** 2 See below R-tree query or full-table scan.
 
117065 ** ------------------------------------------------
117066 **
117067 ** If strategy 1 is used, then idxStr is not meaningful. If strategy
117068 ** 2 is used, idxStr is formatted to contain 2 bytes for each
117069 ** constraint used. The first two bytes of idxStr correspond to
117070 ** the constraint in sqlite3_index_info.aConstraintUsage[] with
117071 ** (argvIndex==1) etc.
117072 **
117073
+25 -16
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -95,23 +95,23 @@
9595
** be held constant and Z will be incremented or else Y will be incremented
9696
** and Z will be reset to zero.
9797
**
9898
** Since version 3.6.18, SQLite source code has been stored in the
9999
** <a href="http://www.fossil-scm.org/">Fossil configuration management
100
-** system</a>. ^The SQLITE_SOURCE_ID macro evalutes to
100
+** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
101101
** a string which identifies a particular check-in of SQLite
102102
** within its configuration management system. ^The SQLITE_SOURCE_ID
103103
** string contains the date and time of the check-in (UTC) and an SHA1
104104
** hash of the entire source tree.
105105
**
106106
** See also: [sqlite3_libversion()],
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110
-#define SQLITE_VERSION "3.7.1"
111
-#define SQLITE_VERSION_NUMBER 3007001
112
-#define SQLITE_SOURCE_ID "2010-08-05 03:21:40 fbe70e1106bcc5086ceb9d8f39cc39baf3643092"
110
+#define SQLITE_VERSION "3.7.2"
111
+#define SQLITE_VERSION_NUMBER 3007002
112
+#define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
@@ -152,19 +152,19 @@
152152
** ^The sqlite3_compileoption_used() function returns 0 or 1
153153
** indicating whether the specified option was defined at
154154
** compile time. ^The SQLITE_ prefix may be omitted from the
155155
** option name passed to sqlite3_compileoption_used().
156156
**
157
-** ^The sqlite3_compileoption_get() function allows interating
157
+** ^The sqlite3_compileoption_get() function allows iterating
158158
** over the list of options that were defined at compile time by
159159
** returning the N-th compile time option string. ^If N is out of range,
160160
** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
161161
** prefix is omitted from any strings returned by
162162
** sqlite3_compileoption_get().
163163
**
164164
** ^Support for the diagnostic functions sqlite3_compileoption_used()
165
-** and sqlite3_compileoption_get() may be omitted by specifing the
165
+** and sqlite3_compileoption_get() may be omitted by specifying the
166166
** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
167167
**
168168
** See also: SQL functions [sqlite_compileoption_used()] and
169169
** [sqlite_compileoption_get()] and the [compile_options pragma].
170170
*/
@@ -266,11 +266,11 @@
266266
/*
267267
** CAPI3REF: Closing A Database Connection
268268
**
269269
** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
270270
** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
271
-** successfullly destroyed and all associated resources are deallocated.
271
+** successfully destroyed and all associated resources are deallocated.
272272
**
273273
** Applications must [sqlite3_finalize | finalize] all [prepared statements]
274274
** and [sqlite3_blob_close | close] all [BLOB handles] associated with
275275
** the [sqlite3] object prior to attempting to close the object. ^If
276276
** sqlite3_close() is called on a [database connection] that still has
@@ -693,16 +693,25 @@
693693
** layer a hint of how large the database file will grow to be during the
694694
** current transaction. This hint is not guaranteed to be accurate but it
695695
** is often close. The underlying VFS might choose to preallocate database
696696
** file space based on this hint in order to help writes to the database
697697
** file run faster.
698
+**
699
+** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
700
+** extends and truncates the database file in chunks of a size specified
701
+** by the user. The fourth argument to [sqlite3_file_control()] should
702
+** point to an integer (type int) containing the new chunk-size to use
703
+** for the nominated database. Allocating database file space in large
704
+** chunks (say 1MB at a time), may reduce file-system fragmentation and
705
+** improve performance on some systems.
698706
*/
699707
#define SQLITE_FCNTL_LOCKSTATE 1
700708
#define SQLITE_GET_LOCKPROXYFILE 2
701709
#define SQLITE_SET_LOCKPROXYFILE 3
702710
#define SQLITE_LAST_ERRNO 4
703711
#define SQLITE_FCNTL_SIZE_HINT 5
712
+#define SQLITE_FCNTL_CHUNK_SIZE 6
704713
705714
/*
706715
** CAPI3REF: Mutex Handle
707716
**
708717
** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -2661,11 +2670,11 @@
26612670
** <li> @VVV
26622671
** <li> $VVV
26632672
** </ul>
26642673
**
26652674
** In the templates above, NNN represents an integer literal,
2666
-** and VVV represents an alphanumeric identifer.)^ ^The values of these
2675
+** and VVV represents an alphanumeric identifier.)^ ^The values of these
26672676
** parameters (also called "host parameter names" or "SQL parameters")
26682677
** can be set using the sqlite3_bind_*() routines defined here.
26692678
**
26702679
** ^The first argument to the sqlite3_bind_*() routines is always
26712680
** a pointer to the [sqlite3_stmt] object returned from
@@ -3440,11 +3449,11 @@
34403449
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
34413450
34423451
/*
34433452
** CAPI3REF: Obtain Aggregate Function Context
34443453
**
3445
-** Implementions of aggregate SQL functions use this
3454
+** Implementations of aggregate SQL functions use this
34463455
** routine to allocate memory for storing their state.
34473456
**
34483457
** ^The first time the sqlite3_aggregate_context(C,N) routine is called
34493458
** for a particular aggregate function, SQLite
34503459
** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -3712,11 +3721,11 @@
37123721
** the routine expects pointers to 16-bit word aligned strings
37133722
** of UTF-16 in the native byte order.
37143723
**
37153724
** A pointer to the user supplied routine must be passed as the fifth
37163725
** argument. ^If it is NULL, this is the same as deleting the collation
3717
-** sequence (so that SQLite cannot call it anymore).
3726
+** sequence (so that SQLite cannot call it any more).
37183727
** ^Each time the application supplied function is invoked, it is passed
37193728
** as its first parameter a copy of the void* passed as the fourth argument
37203729
** to sqlite3_create_collation() or sqlite3_create_collation16().
37213730
**
37223731
** ^The remaining arguments to the application-supplied routine are two strings,
@@ -4930,11 +4939,11 @@
49304939
** of passing a NULL pointer instead of a valid mutex handle are undefined
49314940
** (i.e. it is acceptable to provide an implementation that segfaults if
49324941
** it is passed a NULL pointer).
49334942
**
49344943
** The xMutexInit() method must be threadsafe. ^It must be harmless to
4935
-** invoke xMutexInit() mutiple times within the same process and without
4944
+** invoke xMutexInit() multiple times within the same process and without
49364945
** intervening calls to xMutexEnd(). Second and subsequent calls to
49374946
** xMutexInit() must be no-ops.
49384947
**
49394948
** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
49404949
** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5100,11 +5109,11 @@
51005109
51015110
/*
51025111
** CAPI3REF: SQLite Runtime Status
51035112
**
51045113
** ^This interface is used to retrieve runtime status information
5105
-** about the preformance of SQLite, and optionally to reset various
5114
+** about the performance of SQLite, and optionally to reset various
51065115
** highwater marks. ^The first argument is an integer code for
51075116
** the specific parameter to measure. ^(Recognized integer codes
51085117
** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
51095118
** ^The current value of the parameter is returned into *pCurrent.
51105119
** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5226,11 +5235,11 @@
52265235
** ^This interface is used to retrieve runtime status information
52275236
** about a single [database connection]. ^The first argument is the
52285237
** database connection object to be interrogated. ^The second argument
52295238
** is an integer constant, taken from the set of
52305239
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5231
-** determiness the parameter to interrogate. The set of
5240
+** determines the parameter to interrogate. The set of
52325241
** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
52335242
** to grow in future releases of SQLite.
52345243
**
52355244
** ^The current value of the requested parameter is written into *pCur
52365245
** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -5648,11 +5657,11 @@
56485657
**
56495658
** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
56505659
**
56515660
** ^Each call to sqlite3_backup_step() sets two values inside
56525661
** the [sqlite3_backup] object: the number of pages still to be backed
5653
-** up and the total number of pages in the source databae file.
5662
+** up and the total number of pages in the source database file.
56545663
** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
56555664
** retrieve these two values, respectively.
56565665
**
56575666
** ^The values returned by these functions are only updated by
56585667
** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -5744,11 +5753,11 @@
57445753
** ^(There may be at most one unlock-notify callback registered by a
57455754
** blocked connection. If sqlite3_unlock_notify() is called when the
57465755
** blocked connection already has a registered unlock-notify callback,
57475756
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
57485757
** called with a NULL pointer as its second argument, then any existing
5749
-** unlock-notify callback is cancelled. ^The blocked connections
5758
+** unlock-notify callback is canceled. ^The blocked connections
57505759
** unlock-notify callback may also be canceled by closing the blocked
57515760
** connection using [sqlite3_close()].
57525761
**
57535762
** The unlock-notify callback is not reentrant. If an application invokes
57545763
** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -5826,11 +5835,11 @@
58265835
/*
58275836
** CAPI3REF: String Comparison
58285837
**
58295838
** ^The [sqlite3_strnicmp()] API allows applications and extensions to
58305839
** compare the contents of two buffers containing UTF-8 strings in a
5831
-** case-indendent fashion, using the same definition of case independence
5840
+** case-independent fashion, using the same definition of case independence
58325841
** that SQLite uses internally when comparing identifiers.
58335842
*/
58345843
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
58355844
58365845
/*
58375846
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -95,23 +95,23 @@
95 ** be held constant and Z will be incremented or else Y will be incremented
96 ** and Z will be reset to zero.
97 **
98 ** Since version 3.6.18, SQLite source code has been stored in the
99 ** <a href="http://www.fossil-scm.org/">Fossil configuration management
100 ** system</a>. ^The SQLITE_SOURCE_ID macro evalutes to
101 ** a string which identifies a particular check-in of SQLite
102 ** within its configuration management system. ^The SQLITE_SOURCE_ID
103 ** string contains the date and time of the check-in (UTC) and an SHA1
104 ** hash of the entire source tree.
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.1"
111 #define SQLITE_VERSION_NUMBER 3007001
112 #define SQLITE_SOURCE_ID "2010-08-05 03:21:40 fbe70e1106bcc5086ceb9d8f39cc39baf3643092"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -152,19 +152,19 @@
152 ** ^The sqlite3_compileoption_used() function returns 0 or 1
153 ** indicating whether the specified option was defined at
154 ** compile time. ^The SQLITE_ prefix may be omitted from the
155 ** option name passed to sqlite3_compileoption_used().
156 **
157 ** ^The sqlite3_compileoption_get() function allows interating
158 ** over the list of options that were defined at compile time by
159 ** returning the N-th compile time option string. ^If N is out of range,
160 ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
161 ** prefix is omitted from any strings returned by
162 ** sqlite3_compileoption_get().
163 **
164 ** ^Support for the diagnostic functions sqlite3_compileoption_used()
165 ** and sqlite3_compileoption_get() may be omitted by specifing the
166 ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
167 **
168 ** See also: SQL functions [sqlite_compileoption_used()] and
169 ** [sqlite_compileoption_get()] and the [compile_options pragma].
170 */
@@ -266,11 +266,11 @@
266 /*
267 ** CAPI3REF: Closing A Database Connection
268 **
269 ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
270 ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
271 ** successfullly destroyed and all associated resources are deallocated.
272 **
273 ** Applications must [sqlite3_finalize | finalize] all [prepared statements]
274 ** and [sqlite3_blob_close | close] all [BLOB handles] associated with
275 ** the [sqlite3] object prior to attempting to close the object. ^If
276 ** sqlite3_close() is called on a [database connection] that still has
@@ -693,16 +693,25 @@
693 ** layer a hint of how large the database file will grow to be during the
694 ** current transaction. This hint is not guaranteed to be accurate but it
695 ** is often close. The underlying VFS might choose to preallocate database
696 ** file space based on this hint in order to help writes to the database
697 ** file run faster.
 
 
 
 
 
 
 
 
698 */
699 #define SQLITE_FCNTL_LOCKSTATE 1
700 #define SQLITE_GET_LOCKPROXYFILE 2
701 #define SQLITE_SET_LOCKPROXYFILE 3
702 #define SQLITE_LAST_ERRNO 4
703 #define SQLITE_FCNTL_SIZE_HINT 5
 
704
705 /*
706 ** CAPI3REF: Mutex Handle
707 **
708 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -2661,11 +2670,11 @@
2661 ** <li> @VVV
2662 ** <li> $VVV
2663 ** </ul>
2664 **
2665 ** In the templates above, NNN represents an integer literal,
2666 ** and VVV represents an alphanumeric identifer.)^ ^The values of these
2667 ** parameters (also called "host parameter names" or "SQL parameters")
2668 ** can be set using the sqlite3_bind_*() routines defined here.
2669 **
2670 ** ^The first argument to the sqlite3_bind_*() routines is always
2671 ** a pointer to the [sqlite3_stmt] object returned from
@@ -3440,11 +3449,11 @@
3440 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
3441
3442 /*
3443 ** CAPI3REF: Obtain Aggregate Function Context
3444 **
3445 ** Implementions of aggregate SQL functions use this
3446 ** routine to allocate memory for storing their state.
3447 **
3448 ** ^The first time the sqlite3_aggregate_context(C,N) routine is called
3449 ** for a particular aggregate function, SQLite
3450 ** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -3712,11 +3721,11 @@
3712 ** the routine expects pointers to 16-bit word aligned strings
3713 ** of UTF-16 in the native byte order.
3714 **
3715 ** A pointer to the user supplied routine must be passed as the fifth
3716 ** argument. ^If it is NULL, this is the same as deleting the collation
3717 ** sequence (so that SQLite cannot call it anymore).
3718 ** ^Each time the application supplied function is invoked, it is passed
3719 ** as its first parameter a copy of the void* passed as the fourth argument
3720 ** to sqlite3_create_collation() or sqlite3_create_collation16().
3721 **
3722 ** ^The remaining arguments to the application-supplied routine are two strings,
@@ -4930,11 +4939,11 @@
4930 ** of passing a NULL pointer instead of a valid mutex handle are undefined
4931 ** (i.e. it is acceptable to provide an implementation that segfaults if
4932 ** it is passed a NULL pointer).
4933 **
4934 ** The xMutexInit() method must be threadsafe. ^It must be harmless to
4935 ** invoke xMutexInit() mutiple times within the same process and without
4936 ** intervening calls to xMutexEnd(). Second and subsequent calls to
4937 ** xMutexInit() must be no-ops.
4938 **
4939 ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
4940 ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5100,11 +5109,11 @@
5100
5101 /*
5102 ** CAPI3REF: SQLite Runtime Status
5103 **
5104 ** ^This interface is used to retrieve runtime status information
5105 ** about the preformance of SQLite, and optionally to reset various
5106 ** highwater marks. ^The first argument is an integer code for
5107 ** the specific parameter to measure. ^(Recognized integer codes
5108 ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
5109 ** ^The current value of the parameter is returned into *pCurrent.
5110 ** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5226,11 +5235,11 @@
5226 ** ^This interface is used to retrieve runtime status information
5227 ** about a single [database connection]. ^The first argument is the
5228 ** database connection object to be interrogated. ^The second argument
5229 ** is an integer constant, taken from the set of
5230 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5231 ** determiness the parameter to interrogate. The set of
5232 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
5233 ** to grow in future releases of SQLite.
5234 **
5235 ** ^The current value of the requested parameter is written into *pCur
5236 ** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -5648,11 +5657,11 @@
5648 **
5649 ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
5650 **
5651 ** ^Each call to sqlite3_backup_step() sets two values inside
5652 ** the [sqlite3_backup] object: the number of pages still to be backed
5653 ** up and the total number of pages in the source databae file.
5654 ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
5655 ** retrieve these two values, respectively.
5656 **
5657 ** ^The values returned by these functions are only updated by
5658 ** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -5744,11 +5753,11 @@
5744 ** ^(There may be at most one unlock-notify callback registered by a
5745 ** blocked connection. If sqlite3_unlock_notify() is called when the
5746 ** blocked connection already has a registered unlock-notify callback,
5747 ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
5748 ** called with a NULL pointer as its second argument, then any existing
5749 ** unlock-notify callback is cancelled. ^The blocked connections
5750 ** unlock-notify callback may also be canceled by closing the blocked
5751 ** connection using [sqlite3_close()].
5752 **
5753 ** The unlock-notify callback is not reentrant. If an application invokes
5754 ** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -5826,11 +5835,11 @@
5826 /*
5827 ** CAPI3REF: String Comparison
5828 **
5829 ** ^The [sqlite3_strnicmp()] API allows applications and extensions to
5830 ** compare the contents of two buffers containing UTF-8 strings in a
5831 ** case-indendent fashion, using the same definition of case independence
5832 ** that SQLite uses internally when comparing identifiers.
5833 */
5834 SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
5835
5836 /*
5837
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -95,23 +95,23 @@
95 ** be held constant and Z will be incremented or else Y will be incremented
96 ** and Z will be reset to zero.
97 **
98 ** Since version 3.6.18, SQLite source code has been stored in the
99 ** <a href="http://www.fossil-scm.org/">Fossil configuration management
100 ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
101 ** a string which identifies a particular check-in of SQLite
102 ** within its configuration management system. ^The SQLITE_SOURCE_ID
103 ** string contains the date and time of the check-in (UTC) and an SHA1
104 ** hash of the entire source tree.
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.2"
111 #define SQLITE_VERSION_NUMBER 3007002
112 #define SQLITE_SOURCE_ID "2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -152,19 +152,19 @@
152 ** ^The sqlite3_compileoption_used() function returns 0 or 1
153 ** indicating whether the specified option was defined at
154 ** compile time. ^The SQLITE_ prefix may be omitted from the
155 ** option name passed to sqlite3_compileoption_used().
156 **
157 ** ^The sqlite3_compileoption_get() function allows iterating
158 ** over the list of options that were defined at compile time by
159 ** returning the N-th compile time option string. ^If N is out of range,
160 ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
161 ** prefix is omitted from any strings returned by
162 ** sqlite3_compileoption_get().
163 **
164 ** ^Support for the diagnostic functions sqlite3_compileoption_used()
165 ** and sqlite3_compileoption_get() may be omitted by specifying the
166 ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
167 **
168 ** See also: SQL functions [sqlite_compileoption_used()] and
169 ** [sqlite_compileoption_get()] and the [compile_options pragma].
170 */
@@ -266,11 +266,11 @@
266 /*
267 ** CAPI3REF: Closing A Database Connection
268 **
269 ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
270 ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
271 ** successfully destroyed and all associated resources are deallocated.
272 **
273 ** Applications must [sqlite3_finalize | finalize] all [prepared statements]
274 ** and [sqlite3_blob_close | close] all [BLOB handles] associated with
275 ** the [sqlite3] object prior to attempting to close the object. ^If
276 ** sqlite3_close() is called on a [database connection] that still has
@@ -693,16 +693,25 @@
693 ** layer a hint of how large the database file will grow to be during the
694 ** current transaction. This hint is not guaranteed to be accurate but it
695 ** is often close. The underlying VFS might choose to preallocate database
696 ** file space based on this hint in order to help writes to the database
697 ** file run faster.
698 **
699 ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
700 ** extends and truncates the database file in chunks of a size specified
701 ** by the user. The fourth argument to [sqlite3_file_control()] should
702 ** point to an integer (type int) containing the new chunk-size to use
703 ** for the nominated database. Allocating database file space in large
704 ** chunks (say 1MB at a time), may reduce file-system fragmentation and
705 ** improve performance on some systems.
706 */
707 #define SQLITE_FCNTL_LOCKSTATE 1
708 #define SQLITE_GET_LOCKPROXYFILE 2
709 #define SQLITE_SET_LOCKPROXYFILE 3
710 #define SQLITE_LAST_ERRNO 4
711 #define SQLITE_FCNTL_SIZE_HINT 5
712 #define SQLITE_FCNTL_CHUNK_SIZE 6
713
714 /*
715 ** CAPI3REF: Mutex Handle
716 **
717 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -2661,11 +2670,11 @@
2670 ** <li> @VVV
2671 ** <li> $VVV
2672 ** </ul>
2673 **
2674 ** In the templates above, NNN represents an integer literal,
2675 ** and VVV represents an alphanumeric identifier.)^ ^The values of these
2676 ** parameters (also called "host parameter names" or "SQL parameters")
2677 ** can be set using the sqlite3_bind_*() routines defined here.
2678 **
2679 ** ^The first argument to the sqlite3_bind_*() routines is always
2680 ** a pointer to the [sqlite3_stmt] object returned from
@@ -3440,11 +3449,11 @@
3449 SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
3450
3451 /*
3452 ** CAPI3REF: Obtain Aggregate Function Context
3453 **
3454 ** Implementations of aggregate SQL functions use this
3455 ** routine to allocate memory for storing their state.
3456 **
3457 ** ^The first time the sqlite3_aggregate_context(C,N) routine is called
3458 ** for a particular aggregate function, SQLite
3459 ** allocates N of memory, zeroes out that memory, and returns a pointer
@@ -3712,11 +3721,11 @@
3721 ** the routine expects pointers to 16-bit word aligned strings
3722 ** of UTF-16 in the native byte order.
3723 **
3724 ** A pointer to the user supplied routine must be passed as the fifth
3725 ** argument. ^If it is NULL, this is the same as deleting the collation
3726 ** sequence (so that SQLite cannot call it any more).
3727 ** ^Each time the application supplied function is invoked, it is passed
3728 ** as its first parameter a copy of the void* passed as the fourth argument
3729 ** to sqlite3_create_collation() or sqlite3_create_collation16().
3730 **
3731 ** ^The remaining arguments to the application-supplied routine are two strings,
@@ -4930,11 +4939,11 @@
4939 ** of passing a NULL pointer instead of a valid mutex handle are undefined
4940 ** (i.e. it is acceptable to provide an implementation that segfaults if
4941 ** it is passed a NULL pointer).
4942 **
4943 ** The xMutexInit() method must be threadsafe. ^It must be harmless to
4944 ** invoke xMutexInit() multiple times within the same process and without
4945 ** intervening calls to xMutexEnd(). Second and subsequent calls to
4946 ** xMutexInit() must be no-ops.
4947 **
4948 ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
4949 ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
@@ -5100,11 +5109,11 @@
5109
5110 /*
5111 ** CAPI3REF: SQLite Runtime Status
5112 **
5113 ** ^This interface is used to retrieve runtime status information
5114 ** about the performance of SQLite, and optionally to reset various
5115 ** highwater marks. ^The first argument is an integer code for
5116 ** the specific parameter to measure. ^(Recognized integer codes
5117 ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^
5118 ** ^The current value of the parameter is returned into *pCurrent.
5119 ** ^The highest recorded value is returned in *pHighwater. ^If the
@@ -5226,11 +5235,11 @@
5235 ** ^This interface is used to retrieve runtime status information
5236 ** about a single [database connection]. ^The first argument is the
5237 ** database connection object to be interrogated. ^The second argument
5238 ** is an integer constant, taken from the set of
5239 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that
5240 ** determines the parameter to interrogate. The set of
5241 ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely
5242 ** to grow in future releases of SQLite.
5243 **
5244 ** ^The current value of the requested parameter is written into *pCur
5245 ** and the highest instantaneous value is written into *pHiwtr. ^If
@@ -5648,11 +5657,11 @@
5657 **
5658 ** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b>
5659 **
5660 ** ^Each call to sqlite3_backup_step() sets two values inside
5661 ** the [sqlite3_backup] object: the number of pages still to be backed
5662 ** up and the total number of pages in the source database file.
5663 ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
5664 ** retrieve these two values, respectively.
5665 **
5666 ** ^The values returned by these functions are only updated by
5667 ** sqlite3_backup_step(). ^If the source database is modified during a backup
@@ -5744,11 +5753,11 @@
5753 ** ^(There may be at most one unlock-notify callback registered by a
5754 ** blocked connection. If sqlite3_unlock_notify() is called when the
5755 ** blocked connection already has a registered unlock-notify callback,
5756 ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
5757 ** called with a NULL pointer as its second argument, then any existing
5758 ** unlock-notify callback is canceled. ^The blocked connections
5759 ** unlock-notify callback may also be canceled by closing the blocked
5760 ** connection using [sqlite3_close()].
5761 **
5762 ** The unlock-notify callback is not reentrant. If an application invokes
5763 ** any sqlite3_xxx API functions from within an unlock-notify callback, a
@@ -5826,11 +5835,11 @@
5835 /*
5836 ** CAPI3REF: String Comparison
5837 **
5838 ** ^The [sqlite3_strnicmp()] API allows applications and extensions to
5839 ** compare the contents of two buffers containing UTF-8 strings in a
5840 ** case-independent fashion, using the same definition of case independence
5841 ** that SQLite uses internally when comparing identifiers.
5842 */
5843 SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
5844
5845 /*
5846
+18 -6
--- src/stat.c
+++ src/stat.c
@@ -28,15 +28,17 @@
2828
** Show statistics and global information about the repository.
2929
*/
3030
void stat_page(void){
3131
i64 t;
3232
int n, m, fsize;
33
+ int szMax, szAvg;
3334
char zBuf[100];
35
+
3436
login_check_credentials();
3537
if( !g.okRead ){ login_needed(); return; }
3638
style_header("Repository Statistics");
37
- @ <p><table class="label-value">
39
+ @ <table class="label-value">
3840
@ <tr><th>Repository&nbsp;Size:</th><td>
3941
fsize = file_size(g.zRepositoryName);
4042
@ %d(fsize) bytes
4143
@ </td></tr>
4244
@ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
@@ -44,14 +46,21 @@
4446
m = db_int(0, "SELECT count(*) FROM delta");
4547
@ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
4648
@ </td></tr>
4749
if( n>0 ){
4850
int a, b;
51
+ Stmt q;
4952
@ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
50
- t = db_int64(0, "SELECT total(size) FROM blob WHERE size>0");
53
+ db_prepare(&q, "SELECT total(size), avg(size), max(size)"
54
+ " FROM blob WHERE size>0");
55
+ db_step(&q);
56
+ t = db_column_int64(&q, 0);
57
+ szAvg = db_column_int(&q, 1);
58
+ szMax = db_column_int(&q, 2);
59
+ db_finalize(&q);
5160
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
52
- @ %d((int)(((double)t)/(double)n)) bytes average, %s(zBuf) bytes total
61
+ @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) bytes total
5362
@ </td></tr>
5463
@ <tr><th>Compression&nbsp;Ratio:</th><td>
5564
if( t/fsize < 5 ){
5665
b = 10;
5766
fsize /= 10;
@@ -82,10 +91,12 @@
8291
@ </td></tr>
8392
@ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
8493
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
8594
" + 0.99");
8695
@ %d(n) days
96
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%.2f", n/365.24);
97
+ @ or approximately %s(zBuf) years
8798
@ </td></tr>
8899
@ <tr><th>Project&nbsp;ID:</th><td>
89100
@ %h(db_get("project-code",""))
90101
@ </td></tr>
91102
@ <tr><th>Server&nbsp;ID:</th><td>
@@ -94,19 +105,20 @@
94105
95106
@ <tr><th>Fossil&nbsp;Version:</th><td>
96107
@ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
97108
@ </td></tr>
98109
@ <tr><th>SQLite&nbsp;Version:</th><td>
99
- @ %h(db_text(0, "SELECT substr(sqlite_source_id(),1,30)"))
100
- @ (%h(SQLITE_VERSION))
110
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
111
+ SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
112
+ @ %s(zBuf)
101113
@ </td></tr>
102114
@ <tr><th>Database&nbsp;Stats:</th><td>
103115
@ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages,
104116
@ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page,
105117
@ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages,
106118
@ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)),
107119
@ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode
108120
@ </td></tr>
109121
110
- @ </table></p>
122
+ @ </table>
111123
style_footer();
112124
}
113125
--- src/stat.c
+++ src/stat.c
@@ -28,15 +28,17 @@
28 ** Show statistics and global information about the repository.
29 */
30 void stat_page(void){
31 i64 t;
32 int n, m, fsize;
 
33 char zBuf[100];
 
34 login_check_credentials();
35 if( !g.okRead ){ login_needed(); return; }
36 style_header("Repository Statistics");
37 @ <p><table class="label-value">
38 @ <tr><th>Repository&nbsp;Size:</th><td>
39 fsize = file_size(g.zRepositoryName);
40 @ %d(fsize) bytes
41 @ </td></tr>
42 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
@@ -44,14 +46,21 @@
44 m = db_int(0, "SELECT count(*) FROM delta");
45 @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
46 @ </td></tr>
47 if( n>0 ){
48 int a, b;
 
49 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
50 t = db_int64(0, "SELECT total(size) FROM blob WHERE size>0");
 
 
 
 
 
 
51 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
52 @ %d((int)(((double)t)/(double)n)) bytes average, %s(zBuf) bytes total
53 @ </td></tr>
54 @ <tr><th>Compression&nbsp;Ratio:</th><td>
55 if( t/fsize < 5 ){
56 b = 10;
57 fsize /= 10;
@@ -82,10 +91,12 @@
82 @ </td></tr>
83 @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
84 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
85 " + 0.99");
86 @ %d(n) days
 
 
87 @ </td></tr>
88 @ <tr><th>Project&nbsp;ID:</th><td>
89 @ %h(db_get("project-code",""))
90 @ </td></tr>
91 @ <tr><th>Server&nbsp;ID:</th><td>
@@ -94,19 +105,20 @@
94
95 @ <tr><th>Fossil&nbsp;Version:</th><td>
96 @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
97 @ </td></tr>
98 @ <tr><th>SQLite&nbsp;Version:</th><td>
99 @ %h(db_text(0, "SELECT substr(sqlite_source_id(),1,30)"))
100 @ (%h(SQLITE_VERSION))
 
101 @ </td></tr>
102 @ <tr><th>Database&nbsp;Stats:</th><td>
103 @ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages,
104 @ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page,
105 @ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages,
106 @ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)),
107 @ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode
108 @ </td></tr>
109
110 @ </table></p>
111 style_footer();
112 }
113
--- src/stat.c
+++ src/stat.c
@@ -28,15 +28,17 @@
28 ** Show statistics and global information about the repository.
29 */
30 void stat_page(void){
31 i64 t;
32 int n, m, fsize;
33 int szMax, szAvg;
34 char zBuf[100];
35
36 login_check_credentials();
37 if( !g.okRead ){ login_needed(); return; }
38 style_header("Repository Statistics");
39 @ <table class="label-value">
40 @ <tr><th>Repository&nbsp;Size:</th><td>
41 fsize = file_size(g.zRepositoryName);
42 @ %d(fsize) bytes
43 @ </td></tr>
44 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
@@ -44,14 +46,21 @@
46 m = db_int(0, "SELECT count(*) FROM delta");
47 @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
48 @ </td></tr>
49 if( n>0 ){
50 int a, b;
51 Stmt q;
52 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
53 db_prepare(&q, "SELECT total(size), avg(size), max(size)"
54 " FROM blob WHERE size>0");
55 db_step(&q);
56 t = db_column_int64(&q, 0);
57 szAvg = db_column_int(&q, 1);
58 szMax = db_column_int(&q, 2);
59 db_finalize(&q);
60 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
61 @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) bytes total
62 @ </td></tr>
63 @ <tr><th>Compression&nbsp;Ratio:</th><td>
64 if( t/fsize < 5 ){
65 b = 10;
66 fsize /= 10;
@@ -82,10 +91,12 @@
91 @ </td></tr>
92 @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
93 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
94 " + 0.99");
95 @ %d(n) days
96 sqlite3_snprintf(sizeof(zBuf), zBuf, "%.2f", n/365.24);
97 @ or approximately %s(zBuf) years
98 @ </td></tr>
99 @ <tr><th>Project&nbsp;ID:</th><td>
100 @ %h(db_get("project-code",""))
101 @ </td></tr>
102 @ <tr><th>Server&nbsp;ID:</th><td>
@@ -94,19 +105,20 @@
105
106 @ <tr><th>Fossil&nbsp;Version:</th><td>
107 @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
108 @ </td></tr>
109 @ <tr><th>SQLite&nbsp;Version:</th><td>
110 sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
111 SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
112 @ %s(zBuf)
113 @ </td></tr>
114 @ <tr><th>Database&nbsp;Stats:</th><td>
115 @ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages,
116 @ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page,
117 @ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages,
118 @ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)),
119 @ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode
120 @ </td></tr>
121
122 @ </table>
123 style_footer();
124 }
125
+328 -22
--- src/style.c
+++ src/style.c
@@ -83,11 +83,11 @@
8383
zTitle = vmprintf(zTitleFormat, ap);
8484
va_end(ap);
8585
8686
cgi_destination(CGI_HEADER);
8787
cgi_printf("%s",
88
- "<!DOCTYPE html PUBLIC \"-//W3C/DTD XHTML 1.0 Strict//EN\""
88
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
8989
" \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
9090
9191
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
9292
9393
/* Generate the header up through the main menu */
@@ -139,20 +139,23 @@
139139
}
140140
@ <div class="content">
141141
cgi_destination(CGI_BODY);
142142
143143
/* Put the footer at the bottom of the page.
144
+ ** the additional clear/both is needed to extend the content
145
+ ** part to the end of an optional sidebox.
144146
*/
145
- @ </div><br clear="both"/>
147
+ @ <div style="clear: both;"></div>
148
+ @ </div>
146149
zFooter = db_get("footer", (char*)zDefaultFooter);
147150
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
148151
Th_Render(zFooter);
149152
if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
150153
151154
/* Render trace log if TH1 tracing is enabled. */
152155
if( g.thTrace ){
153
- cgi_append_content("<font color=\"red\"><hr>\n", -1);
156
+ cgi_append_content("<font color=\"red\"><hr />\n", -1);
154157
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
155158
cgi_append_content("</font>\n", -1);
156159
}
157160
}
158161
@@ -160,20 +163,18 @@
160163
** Begin a side-box on the right-hand side of a page. The title and
161164
** the width of the box are given as arguments. The width is usually
162165
** a percentage of total screen width.
163166
*/
164167
void style_sidebox_begin(const char *zTitle, const char *zWidth){
165
- @ <table width="%s(zWidth)" align="right" border="1" cellpadding=5
166
- @ vspace=5 hspace=5>
167
- @ <tr><td>
168
- @ <b>%h(zTitle)</b>
168
+ @ <div class="sidebox" style="width:%s(zWidth)">
169
+ @ <div class="sideboxTitle">%h(zTitle)</div>
169170
}
170171
171172
/* End the side-box
172173
*/
173174
void style_sidebox_end(void){
174
- @ </td></tr></table>
175
+ @ </div>
175176
}
176177
177178
/* @-comment: // */
178179
/*
179180
** The default page header.
@@ -181,27 +182,27 @@
181182
const char zDefaultHeader[] =
182183
@ <html>
183184
@ <head>
184185
@ <title>$<project_name>: $<title></title>
185186
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
186
-@ href="$baseurl/timeline.rss">
187
+@ href="$baseurl/timeline.rss" />
187188
@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
188
-@ media="screen">
189
+@ media="screen" />
189190
@ </head>
190191
@ <body>
191192
@ <div class="header">
192193
@ <div class="logo">
193194
@ <img src="$baseurl/logo" alt="logo">
194195
@ </div>
195
-@ <div class="title"><small>$<project_name></small><br>$<title></div>
196
-@ <div class="status"><nobr><th1>
196
+@ <div class="title"><small>$<project_name></small><br />$<title></div>
197
+@ <div class="status"><th1>
197198
@ if {[info exists login]} {
198199
@ puts "Logged in as $login"
199200
@ } else {
200201
@ puts "Not logged in"
201202
@ }
202
-@ </th1></nobr></div>
203
+@ </th1></div>
203204
@ </div>
204205
@ <div class="mainmenu"><th1>
205206
@ html "<a href='$baseurl$index_page'>Home</a> "
206207
@ if {[anycap jor]} {
207208
@ html "<a href='$baseurl/timeline'>Timeline</a> "
@@ -243,10 +244,14 @@
243244
@ </body></html>
244245
;
245246
246247
/*
247248
** The default Cascading Style Sheet.
249
+** It's assembled by different strings for each class.
250
+** The default css conatains all definitions.
251
+** The style sheet, send to the client only contains the ones,
252
+** not defined in the user defined css.
248253
*/
249254
const char zDefaultCSS[] =
250255
@ /* General settings for the entire page */
251256
@ body {
252257
@ margin: 0ex 1ex;
@@ -272,11 +277,11 @@
272277
@ font-weight: bold;
273278
@ text-align: center;
274279
@ padding: 0 0 0 1em;
275280
@ color: #558195;
276281
@ vertical-align: bottom;
277
-@ width: 100%;
282
+@ width: 100% ;
278283
@ }
279284
@
280285
@ /* The login status message in the top right-hand corner */
281286
@ div.status {
282287
@ display: table-cell;
@@ -284,16 +289,17 @@
284289
@ vertical-align: bottom;
285290
@ color: #558195;
286291
@ font-size: 0.8em;
287292
@ font-weight: bold;
288293
@ min-width: 200px;
294
+@ white-space: nowrap;
289295
@ }
290296
@
291297
@ /* The header across the top of the page */
292298
@ div.header {
293299
@ display: table;
294
-@ width: 100%;
300
+@ width: 100% ;
295301
@ }
296302
@
297303
@ /* The main menu bar that appears at the top of the page beneath
298304
@ ** the header */
299305
@ div.mainmenu {
@@ -337,10 +343,11 @@
337343
@ padding: 1px 1px 1px 1px;
338344
@ font-size: 1.2em;
339345
@ font-weight: bold;
340346
@ background-color: #558195;
341347
@ color: white;
348
+@ white-space: nowrap;
342349
@ }
343350
@
344351
@ /* The "Date" that occurs on the left hand side of timelines */
345352
@ div.divider {
346353
@ background: #a1c4d4;
@@ -366,11 +373,11 @@
366373
@ div.footer a { color: white; }
367374
@ div.footer a:link { color: white; }
368375
@ div.footer a:visited { color: white; }
369376
@ div.footer a:hover { background-color: white; color: #558195; }
370377
@
371
-@ /* <verbatim> blocks */
378
+@ /* verbatim blocks */
372379
@ pre.verbatim {
373380
@ background-color: #f5f5f5;
374381
@ padding: 0.5em;
375382
@}
376383
@
@@ -378,33 +385,332 @@
378385
@ table.label-value th {
379386
@ vertical-align: top;
380387
@ text-align: right;
381388
@ padding: 0.2ex 2ex;
382389
@ }
390
+@
383391
;
384392
393
+
394
+/* The following table contains bits of default CSS that must
395
+** be included if they are not found in the application-defined
396
+** CSS.
397
+*/
398
+const struct strctCssDefaults {
399
+ char const * const elementClass; /* Name of element needed */
400
+ char const * const comment; /* Comment text */
401
+ char const * const value; /* CSS text */
402
+} cssDefaultList[] = {
403
+ { "",
404
+ "",
405
+ zDefaultCSS
406
+ },
407
+ { "div.sidebox",
408
+ "The nomenclature sidebox for branches,..",
409
+ @ float: right;
410
+ @ background-color: white;
411
+ @ border-width: medium;
412
+ @ border-style: double;
413
+ @ margin: 10;
414
+ },
415
+ { "div.sideboxTitle",
416
+ "The nomenclature title in sideboxes for branches,..",
417
+ @ display: inline;
418
+ @ font-weight: bold;
419
+ },
420
+ { "div.sideboxDescribed",
421
+ "The defined element in sideboxes for branches,..",
422
+ @ display: inline;
423
+ @ font-weight: bold;
424
+ },
425
+ { "span.disabled",
426
+ "The defined element in sideboxes for branches,..",
427
+ @ color: red;
428
+ },
429
+ { "span.timelineDisabled",
430
+ "The suppressed duplicates lines in timeline, ..",
431
+ @ font-style: italic;
432
+ @ font-size: small;
433
+ },
434
+ { "table.timelineTable",
435
+ "the format for the timeline data table",
436
+ @ cellspacing: 0;
437
+ @ border: 0;
438
+ @ cellpadding: 0
439
+ },
440
+ { "td.timelineTableCell",
441
+ "the format for the timeline data cells",
442
+ @ valign: top;
443
+ @ align: left;
444
+ },
445
+ { "span.timelineLeaf",
446
+ "the format for the timeline leaf marks",
447
+ @ font-weight: bold;
448
+ },
449
+ { "a.timelineHistLink",
450
+ "the format for the timeline version links",
451
+ @
452
+ },
453
+ { "span.timelineHistDsp",
454
+ "the format for the timeline version display(no history permission!)",
455
+ @ font-weight: bold;
456
+ },
457
+ { "td.timelineTime",
458
+ "the format for the timeline time display",
459
+ @ vertical-align: top;
460
+ @ text-align: right;
461
+ },
462
+ { "td.timelineGraph",
463
+ "the format for the grap placeholder cells in timelines",
464
+ @ width: 20;
465
+ @ text-align: left;
466
+ @ vertical-align: top;
467
+ },
468
+ { "a.tagLink",
469
+ "the format for the tag links",
470
+ @
471
+ },
472
+ { "span.tagDsp",
473
+ "the format for the tag display(no history permission!)",
474
+ @ font-weight: bold;
475
+ },
476
+ { "span.wikiError",
477
+ "the format for wiki errors",
478
+ @ font-weight: bold;
479
+ @ color: red;
480
+ },
481
+ { "span.infoTagCancelled",
482
+ "the format for fixed/canceled tags,..",
483
+ @ font-weight: bold;
484
+ @ text-decoration: line-through;
485
+ },
486
+ { "span.infoTag",
487
+ "the format for tags,..",
488
+ @ font-weight: bold;
489
+ },
490
+ { "span.wikiTagCancelled",
491
+ "the format for fixed/cancelled tags,.. on wiki pages",
492
+ @ text-decoration: line-through;
493
+ },
494
+ { "table.browser",
495
+ "format for the file display table",
496
+ @ /* the format for wiki errors */
497
+ @ width: 100% ;
498
+ @ border: 0;
499
+ },
500
+ { "td.browser",
501
+ "format for cells in the file browser",
502
+ @ width: 24% ;
503
+ @ vertical-align: top;
504
+ },
505
+ { "ul.browser",
506
+ "format for the list in the file browser",
507
+ @ margin-left: 0.5em;
508
+ @ padding-left: 0.5em;
509
+ },
510
+ { "table.login_out",
511
+ "table format for login/out label/input table",
512
+ @ text-align: left;
513
+ @ margin-right: 10px;
514
+ @ margin-left: 10px;
515
+ @ margin-top: 10px;
516
+ },
517
+ { "div.captcha",
518
+ "captcha display options",
519
+ @ text-align: center;
520
+ },
521
+ { "table.captcha",
522
+ "format for the layout table, used for the captcha display",
523
+ @ margin: auto;
524
+ @ padding: 10px;
525
+ @ outline-width: 1;
526
+ @ outline-style: double;
527
+ },
528
+ { "td.login_out_label",
529
+ "format for the label cells in the login/out table",
530
+ @ text-align: center;
531
+ },
532
+ { "span.loginError",
533
+ "format for login error messages",
534
+ @ color: red;
535
+ },
536
+ { "span.note",
537
+ "format for leading text for notes",
538
+ @ font-weight: bold;
539
+ },
540
+ { "span.textareaLabel",
541
+ "format for textare labels",
542
+ @ font-weight: bold;
543
+ },
544
+ { "table.usetupLayoutTable",
545
+ "format for the user setup layout table",
546
+ @ outline-style: none;
547
+ @ padding: 0;
548
+ @ margin: 25px;
549
+ },
550
+ { "td.usetupColumnLayout",
551
+ "format of the columns on the user setup list page",
552
+ @ vertical-align: top
553
+ },
554
+ { "table.usetupUserList",
555
+ "format for the user list table on the user setup page",
556
+ @ outline-style: double;
557
+ @ outline-width: 1;
558
+ @ padding: 10px;
559
+ },
560
+ { "th.usetupListUser",
561
+ "format for table header user in user list on user setup page",
562
+ @ text-align: right;
563
+ @ padding-right: 20px;
564
+ },
565
+ { "th.usetupListCap",
566
+ "format for table header capabilities in user list on user setup page",
567
+ @ text-align: center;
568
+ @ padding-right: 15px;
569
+ },
570
+ { "th.usetupListCon",
571
+ "format for table header contact info in user list on user setup page",
572
+ @ text-align: left;
573
+ },
574
+ { "td.usetupListUser",
575
+ "format for table cell user in user list on user setup page",
576
+ @ text-align: right;
577
+ @ padding-right: 20px;
578
+ @ white-space:nowrap;
579
+ },
580
+ { "td.usetupListCap",
581
+ "format for table cell capabilities in user list on user setup page",
582
+ @ text-align: center;
583
+ @ padding-right: 15px;
584
+ },
585
+ { "td.usetupListCon",
586
+ "format for table cell contact info in user list on user setup page",
587
+ @ text-align: left
588
+ },
589
+ { "div.ueditCapBox",
590
+ "layout definition for the capabilities box on the user edit detail page",
591
+ @ float: left;
592
+ @ margin-right: 20px;
593
+ @ margin-bottom: 20px;
594
+ },
595
+ { "td.usetupEditLabel",
596
+ "format of the label cells in the detailed user edit page",
597
+ @ text-align: right;
598
+ @ vertical-align: top;
599
+ @ white-space: nowrap;
600
+ },
601
+ { "span.ueditInheritNobody",
602
+ "color for capabilities, inherited by nobody",
603
+ @ color: green;
604
+ },
605
+ { "span.ueditInheritDeveloper",
606
+ "color for capabilities, inherited by developer",
607
+ @ color: red;
608
+ },
609
+ { "span.ueditInheritReader",
610
+ "color for capabilities, inherited by reader",
611
+ @ color: black;
612
+ },
613
+ { "span.ueditInheritAnonymous",
614
+ "color for capabilities, inherited by anonymous",
615
+ @ color: blue;
616
+ },
617
+ { "span.capability",
618
+ "format for capabilites, mentioned on the user edit page",
619
+ @ font-weight: bold;
620
+ },
621
+ { "span.usertype",
622
+ "format for different user types, mentioned on the user edit page",
623
+ @ font-weight: bold;
624
+ },
625
+ { "span.usertype:before",
626
+ "leading text for user types, mentioned on the user edit page",
627
+ @ content:"'";
628
+ },
629
+ { "span.usertype:after",
630
+ "trailing text for user types, mentioned on the user edit page",
631
+ @ content:"'";
632
+ },
633
+ { "span.wikiruleHead",
634
+ "format for leading text in wikirules definitions",
635
+ @ font-weight: bold;
636
+ },
637
+ { "td.tktDspLabel",
638
+ "format for labels on ticket display page",
639
+ @ text-align: right;
640
+ },
641
+ { "td.tktDspValue",
642
+ "format for values on ticket display page",
643
+ @ text-align: left;
644
+ @ vertical-align: top;
645
+ @ background-color: #d0d0d0;
646
+ },
647
+ { "span.tktError",
648
+ "format for ticket error messages",
649
+ @ color: red;
650
+ @ font-weight: bold;
651
+ },
652
+ { 0,
653
+ 0,
654
+ 0
655
+ }
656
+};
657
+
658
+/*
659
+** Append all of the default CSS to the CGI output.
660
+*/
661
+void cgi_append_default_css(void) {
662
+ int i;
663
+
664
+ for (i=0;cssDefaultList[i].elementClass;i++){
665
+ if (cssDefaultList[i].elementClass[0]){
666
+ cgi_printf("/* %s */\n%s {\n%s\n}\n\n",
667
+ cssDefaultList[i].comment,
668
+ cssDefaultList[i].elementClass,
669
+ cssDefaultList[i].value
670
+ );
671
+ }else{
672
+ cgi_printf("%s",
673
+ cssDefaultList[i].value
674
+ );
675
+ }
676
+ }
677
+}
678
+
385679
/*
386680
** WEBPAGE: style.css
387681
*/
388682
void page_style_css(void){
389
- char *zCSS = 0;
683
+ const char *zCSS = 0;
684
+ int i;
390685
391686
cgi_set_content_type("text/css");
392687
zCSS = db_get("css",(char*)zDefaultCSS);
688
+ /* append user defined css */
393689
cgi_append_content(zCSS, -1);
690
+ /* add special missing definitions */
691
+ for (i=1;cssDefaultList[i].elementClass;i++)
692
+ if (!strstr(zCSS,cssDefaultList[i].elementClass)) {
693
+ cgi_append_content("/* ", -1);
694
+ cgi_append_content(cssDefaultList[i].comment, -1);
695
+ cgi_append_content(" */\n", -1);
696
+ cgi_append_content(cssDefaultList[i].elementClass, -1);
697
+ cgi_append_content(" {\n", -1);
698
+ cgi_append_content(cssDefaultList[i].value, -1);
699
+ cgi_append_content("}\n\n", -1);
700
+ }
394701
g.isConst = 1;
395702
}
396703
397704
/*
398705
** WEBPAGE: test_env
399706
*/
400707
void page_test_env(void){
401708
style_header("Environment Test");
402
-#if !defined(__MINGW32__)
403
- @ uid=%d(getuid()), gid=%d(getgid())<br>
709
+#if !defined(_WIN32)
710
+ @ uid=%d(getuid()), gid=%d(getgid())<br />
404711
#endif
405
- @ g.zBaseURL = %h(g.zBaseURL)<br>
406
- @ g.zTop = %h(g.zTop)<br>
407
- @ g.zRepositoryName = %h(g.zRepositoryName)<br>
712
+ @ g.zBaseURL = %h(g.zBaseURL)<br />
713
+ @ g.zTop = %h(g.zTop)<br />
408714
cgi_print_all();
409715
style_footer();
410716
}
411717
--- src/style.c
+++ src/style.c
@@ -83,11 +83,11 @@
83 zTitle = vmprintf(zTitleFormat, ap);
84 va_end(ap);
85
86 cgi_destination(CGI_HEADER);
87 cgi_printf("%s",
88 "<!DOCTYPE html PUBLIC \"-//W3C/DTD XHTML 1.0 Strict//EN\""
89 " \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
90
91 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
92
93 /* Generate the header up through the main menu */
@@ -139,20 +139,23 @@
139 }
140 @ <div class="content">
141 cgi_destination(CGI_BODY);
142
143 /* Put the footer at the bottom of the page.
 
 
144 */
145 @ </div><br clear="both"/>
 
146 zFooter = db_get("footer", (char*)zDefaultFooter);
147 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
148 Th_Render(zFooter);
149 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
150
151 /* Render trace log if TH1 tracing is enabled. */
152 if( g.thTrace ){
153 cgi_append_content("<font color=\"red\"><hr>\n", -1);
154 cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
155 cgi_append_content("</font>\n", -1);
156 }
157 }
158
@@ -160,20 +163,18 @@
160 ** Begin a side-box on the right-hand side of a page. The title and
161 ** the width of the box are given as arguments. The width is usually
162 ** a percentage of total screen width.
163 */
164 void style_sidebox_begin(const char *zTitle, const char *zWidth){
165 @ <table width="%s(zWidth)" align="right" border="1" cellpadding=5
166 @ vspace=5 hspace=5>
167 @ <tr><td>
168 @ <b>%h(zTitle)</b>
169 }
170
171 /* End the side-box
172 */
173 void style_sidebox_end(void){
174 @ </td></tr></table>
175 }
176
177 /* @-comment: // */
178 /*
179 ** The default page header.
@@ -181,27 +182,27 @@
181 const char zDefaultHeader[] =
182 @ <html>
183 @ <head>
184 @ <title>$<project_name>: $<title></title>
185 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
186 @ href="$baseurl/timeline.rss">
187 @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
188 @ media="screen">
189 @ </head>
190 @ <body>
191 @ <div class="header">
192 @ <div class="logo">
193 @ <img src="$baseurl/logo" alt="logo">
194 @ </div>
195 @ <div class="title"><small>$<project_name></small><br>$<title></div>
196 @ <div class="status"><nobr><th1>
197 @ if {[info exists login]} {
198 @ puts "Logged in as $login"
199 @ } else {
200 @ puts "Not logged in"
201 @ }
202 @ </th1></nobr></div>
203 @ </div>
204 @ <div class="mainmenu"><th1>
205 @ html "<a href='$baseurl$index_page'>Home</a> "
206 @ if {[anycap jor]} {
207 @ html "<a href='$baseurl/timeline'>Timeline</a> "
@@ -243,10 +244,14 @@
243 @ </body></html>
244 ;
245
246 /*
247 ** The default Cascading Style Sheet.
 
 
 
 
248 */
249 const char zDefaultCSS[] =
250 @ /* General settings for the entire page */
251 @ body {
252 @ margin: 0ex 1ex;
@@ -272,11 +277,11 @@
272 @ font-weight: bold;
273 @ text-align: center;
274 @ padding: 0 0 0 1em;
275 @ color: #558195;
276 @ vertical-align: bottom;
277 @ width: 100%;
278 @ }
279 @
280 @ /* The login status message in the top right-hand corner */
281 @ div.status {
282 @ display: table-cell;
@@ -284,16 +289,17 @@
284 @ vertical-align: bottom;
285 @ color: #558195;
286 @ font-size: 0.8em;
287 @ font-weight: bold;
288 @ min-width: 200px;
 
289 @ }
290 @
291 @ /* The header across the top of the page */
292 @ div.header {
293 @ display: table;
294 @ width: 100%;
295 @ }
296 @
297 @ /* The main menu bar that appears at the top of the page beneath
298 @ ** the header */
299 @ div.mainmenu {
@@ -337,10 +343,11 @@
337 @ padding: 1px 1px 1px 1px;
338 @ font-size: 1.2em;
339 @ font-weight: bold;
340 @ background-color: #558195;
341 @ color: white;
 
342 @ }
343 @
344 @ /* The "Date" that occurs on the left hand side of timelines */
345 @ div.divider {
346 @ background: #a1c4d4;
@@ -366,11 +373,11 @@
366 @ div.footer a { color: white; }
367 @ div.footer a:link { color: white; }
368 @ div.footer a:visited { color: white; }
369 @ div.footer a:hover { background-color: white; color: #558195; }
370 @
371 @ /* <verbatim> blocks */
372 @ pre.verbatim {
373 @ background-color: #f5f5f5;
374 @ padding: 0.5em;
375 @}
376 @
@@ -378,33 +385,332 @@
378 @ table.label-value th {
379 @ vertical-align: top;
380 @ text-align: right;
381 @ padding: 0.2ex 2ex;
382 @ }
 
383 ;
384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385 /*
386 ** WEBPAGE: style.css
387 */
388 void page_style_css(void){
389 char *zCSS = 0;
 
390
391 cgi_set_content_type("text/css");
392 zCSS = db_get("css",(char*)zDefaultCSS);
 
393 cgi_append_content(zCSS, -1);
 
 
 
 
 
 
 
 
 
 
 
394 g.isConst = 1;
395 }
396
397 /*
398 ** WEBPAGE: test_env
399 */
400 void page_test_env(void){
401 style_header("Environment Test");
402 #if !defined(__MINGW32__)
403 @ uid=%d(getuid()), gid=%d(getgid())<br>
404 #endif
405 @ g.zBaseURL = %h(g.zBaseURL)<br>
406 @ g.zTop = %h(g.zTop)<br>
407 @ g.zRepositoryName = %h(g.zRepositoryName)<br>
408 cgi_print_all();
409 style_footer();
410 }
411
--- src/style.c
+++ src/style.c
@@ -83,11 +83,11 @@
83 zTitle = vmprintf(zTitleFormat, ap);
84 va_end(ap);
85
86 cgi_destination(CGI_HEADER);
87 cgi_printf("%s",
88 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
89 " \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
90
91 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
92
93 /* Generate the header up through the main menu */
@@ -139,20 +139,23 @@
139 }
140 @ <div class="content">
141 cgi_destination(CGI_BODY);
142
143 /* Put the footer at the bottom of the page.
144 ** the additional clear/both is needed to extend the content
145 ** part to the end of an optional sidebox.
146 */
147 @ <div style="clear: both;"></div>
148 @ </div>
149 zFooter = db_get("footer", (char*)zDefaultFooter);
150 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
151 Th_Render(zFooter);
152 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
153
154 /* Render trace log if TH1 tracing is enabled. */
155 if( g.thTrace ){
156 cgi_append_content("<font color=\"red\"><hr />\n", -1);
157 cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
158 cgi_append_content("</font>\n", -1);
159 }
160 }
161
@@ -160,20 +163,18 @@
163 ** Begin a side-box on the right-hand side of a page. The title and
164 ** the width of the box are given as arguments. The width is usually
165 ** a percentage of total screen width.
166 */
167 void style_sidebox_begin(const char *zTitle, const char *zWidth){
168 @ <div class="sidebox" style="width:%s(zWidth)">
169 @ <div class="sideboxTitle">%h(zTitle)</div>
 
 
170 }
171
172 /* End the side-box
173 */
174 void style_sidebox_end(void){
175 @ </div>
176 }
177
178 /* @-comment: // */
179 /*
180 ** The default page header.
@@ -181,27 +182,27 @@
182 const char zDefaultHeader[] =
183 @ <html>
184 @ <head>
185 @ <title>$<project_name>: $<title></title>
186 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
187 @ href="$baseurl/timeline.rss" />
188 @ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
189 @ media="screen" />
190 @ </head>
191 @ <body>
192 @ <div class="header">
193 @ <div class="logo">
194 @ <img src="$baseurl/logo" alt="logo">
195 @ </div>
196 @ <div class="title"><small>$<project_name></small><br />$<title></div>
197 @ <div class="status"><th1>
198 @ if {[info exists login]} {
199 @ puts "Logged in as $login"
200 @ } else {
201 @ puts "Not logged in"
202 @ }
203 @ </th1></div>
204 @ </div>
205 @ <div class="mainmenu"><th1>
206 @ html "<a href='$baseurl$index_page'>Home</a> "
207 @ if {[anycap jor]} {
208 @ html "<a href='$baseurl/timeline'>Timeline</a> "
@@ -243,10 +244,14 @@
244 @ </body></html>
245 ;
246
247 /*
248 ** The default Cascading Style Sheet.
249 ** It's assembled by different strings for each class.
250 ** The default css conatains all definitions.
251 ** The style sheet, send to the client only contains the ones,
252 ** not defined in the user defined css.
253 */
254 const char zDefaultCSS[] =
255 @ /* General settings for the entire page */
256 @ body {
257 @ margin: 0ex 1ex;
@@ -272,11 +277,11 @@
277 @ font-weight: bold;
278 @ text-align: center;
279 @ padding: 0 0 0 1em;
280 @ color: #558195;
281 @ vertical-align: bottom;
282 @ width: 100% ;
283 @ }
284 @
285 @ /* The login status message in the top right-hand corner */
286 @ div.status {
287 @ display: table-cell;
@@ -284,16 +289,17 @@
289 @ vertical-align: bottom;
290 @ color: #558195;
291 @ font-size: 0.8em;
292 @ font-weight: bold;
293 @ min-width: 200px;
294 @ white-space: nowrap;
295 @ }
296 @
297 @ /* The header across the top of the page */
298 @ div.header {
299 @ display: table;
300 @ width: 100% ;
301 @ }
302 @
303 @ /* The main menu bar that appears at the top of the page beneath
304 @ ** the header */
305 @ div.mainmenu {
@@ -337,10 +343,11 @@
343 @ padding: 1px 1px 1px 1px;
344 @ font-size: 1.2em;
345 @ font-weight: bold;
346 @ background-color: #558195;
347 @ color: white;
348 @ white-space: nowrap;
349 @ }
350 @
351 @ /* The "Date" that occurs on the left hand side of timelines */
352 @ div.divider {
353 @ background: #a1c4d4;
@@ -366,11 +373,11 @@
373 @ div.footer a { color: white; }
374 @ div.footer a:link { color: white; }
375 @ div.footer a:visited { color: white; }
376 @ div.footer a:hover { background-color: white; color: #558195; }
377 @
378 @ /* verbatim blocks */
379 @ pre.verbatim {
380 @ background-color: #f5f5f5;
381 @ padding: 0.5em;
382 @}
383 @
@@ -378,33 +385,332 @@
385 @ table.label-value th {
386 @ vertical-align: top;
387 @ text-align: right;
388 @ padding: 0.2ex 2ex;
389 @ }
390 @
391 ;
392
393
394 /* The following table contains bits of default CSS that must
395 ** be included if they are not found in the application-defined
396 ** CSS.
397 */
398 const struct strctCssDefaults {
399 char const * const elementClass; /* Name of element needed */
400 char const * const comment; /* Comment text */
401 char const * const value; /* CSS text */
402 } cssDefaultList[] = {
403 { "",
404 "",
405 zDefaultCSS
406 },
407 { "div.sidebox",
408 "The nomenclature sidebox for branches,..",
409 @ float: right;
410 @ background-color: white;
411 @ border-width: medium;
412 @ border-style: double;
413 @ margin: 10;
414 },
415 { "div.sideboxTitle",
416 "The nomenclature title in sideboxes for branches,..",
417 @ display: inline;
418 @ font-weight: bold;
419 },
420 { "div.sideboxDescribed",
421 "The defined element in sideboxes for branches,..",
422 @ display: inline;
423 @ font-weight: bold;
424 },
425 { "span.disabled",
426 "The defined element in sideboxes for branches,..",
427 @ color: red;
428 },
429 { "span.timelineDisabled",
430 "The suppressed duplicates lines in timeline, ..",
431 @ font-style: italic;
432 @ font-size: small;
433 },
434 { "table.timelineTable",
435 "the format for the timeline data table",
436 @ cellspacing: 0;
437 @ border: 0;
438 @ cellpadding: 0
439 },
440 { "td.timelineTableCell",
441 "the format for the timeline data cells",
442 @ valign: top;
443 @ align: left;
444 },
445 { "span.timelineLeaf",
446 "the format for the timeline leaf marks",
447 @ font-weight: bold;
448 },
449 { "a.timelineHistLink",
450 "the format for the timeline version links",
451 @
452 },
453 { "span.timelineHistDsp",
454 "the format for the timeline version display(no history permission!)",
455 @ font-weight: bold;
456 },
457 { "td.timelineTime",
458 "the format for the timeline time display",
459 @ vertical-align: top;
460 @ text-align: right;
461 },
462 { "td.timelineGraph",
463 "the format for the grap placeholder cells in timelines",
464 @ width: 20;
465 @ text-align: left;
466 @ vertical-align: top;
467 },
468 { "a.tagLink",
469 "the format for the tag links",
470 @
471 },
472 { "span.tagDsp",
473 "the format for the tag display(no history permission!)",
474 @ font-weight: bold;
475 },
476 { "span.wikiError",
477 "the format for wiki errors",
478 @ font-weight: bold;
479 @ color: red;
480 },
481 { "span.infoTagCancelled",
482 "the format for fixed/canceled tags,..",
483 @ font-weight: bold;
484 @ text-decoration: line-through;
485 },
486 { "span.infoTag",
487 "the format for tags,..",
488 @ font-weight: bold;
489 },
490 { "span.wikiTagCancelled",
491 "the format for fixed/cancelled tags,.. on wiki pages",
492 @ text-decoration: line-through;
493 },
494 { "table.browser",
495 "format for the file display table",
496 @ /* the format for wiki errors */
497 @ width: 100% ;
498 @ border: 0;
499 },
500 { "td.browser",
501 "format for cells in the file browser",
502 @ width: 24% ;
503 @ vertical-align: top;
504 },
505 { "ul.browser",
506 "format for the list in the file browser",
507 @ margin-left: 0.5em;
508 @ padding-left: 0.5em;
509 },
510 { "table.login_out",
511 "table format for login/out label/input table",
512 @ text-align: left;
513 @ margin-right: 10px;
514 @ margin-left: 10px;
515 @ margin-top: 10px;
516 },
517 { "div.captcha",
518 "captcha display options",
519 @ text-align: center;
520 },
521 { "table.captcha",
522 "format for the layout table, used for the captcha display",
523 @ margin: auto;
524 @ padding: 10px;
525 @ outline-width: 1;
526 @ outline-style: double;
527 },
528 { "td.login_out_label",
529 "format for the label cells in the login/out table",
530 @ text-align: center;
531 },
532 { "span.loginError",
533 "format for login error messages",
534 @ color: red;
535 },
536 { "span.note",
537 "format for leading text for notes",
538 @ font-weight: bold;
539 },
540 { "span.textareaLabel",
541 "format for textare labels",
542 @ font-weight: bold;
543 },
544 { "table.usetupLayoutTable",
545 "format for the user setup layout table",
546 @ outline-style: none;
547 @ padding: 0;
548 @ margin: 25px;
549 },
550 { "td.usetupColumnLayout",
551 "format of the columns on the user setup list page",
552 @ vertical-align: top
553 },
554 { "table.usetupUserList",
555 "format for the user list table on the user setup page",
556 @ outline-style: double;
557 @ outline-width: 1;
558 @ padding: 10px;
559 },
560 { "th.usetupListUser",
561 "format for table header user in user list on user setup page",
562 @ text-align: right;
563 @ padding-right: 20px;
564 },
565 { "th.usetupListCap",
566 "format for table header capabilities in user list on user setup page",
567 @ text-align: center;
568 @ padding-right: 15px;
569 },
570 { "th.usetupListCon",
571 "format for table header contact info in user list on user setup page",
572 @ text-align: left;
573 },
574 { "td.usetupListUser",
575 "format for table cell user in user list on user setup page",
576 @ text-align: right;
577 @ padding-right: 20px;
578 @ white-space:nowrap;
579 },
580 { "td.usetupListCap",
581 "format for table cell capabilities in user list on user setup page",
582 @ text-align: center;
583 @ padding-right: 15px;
584 },
585 { "td.usetupListCon",
586 "format for table cell contact info in user list on user setup page",
587 @ text-align: left
588 },
589 { "div.ueditCapBox",
590 "layout definition for the capabilities box on the user edit detail page",
591 @ float: left;
592 @ margin-right: 20px;
593 @ margin-bottom: 20px;
594 },
595 { "td.usetupEditLabel",
596 "format of the label cells in the detailed user edit page",
597 @ text-align: right;
598 @ vertical-align: top;
599 @ white-space: nowrap;
600 },
601 { "span.ueditInheritNobody",
602 "color for capabilities, inherited by nobody",
603 @ color: green;
604 },
605 { "span.ueditInheritDeveloper",
606 "color for capabilities, inherited by developer",
607 @ color: red;
608 },
609 { "span.ueditInheritReader",
610 "color for capabilities, inherited by reader",
611 @ color: black;
612 },
613 { "span.ueditInheritAnonymous",
614 "color for capabilities, inherited by anonymous",
615 @ color: blue;
616 },
617 { "span.capability",
618 "format for capabilites, mentioned on the user edit page",
619 @ font-weight: bold;
620 },
621 { "span.usertype",
622 "format for different user types, mentioned on the user edit page",
623 @ font-weight: bold;
624 },
625 { "span.usertype:before",
626 "leading text for user types, mentioned on the user edit page",
627 @ content:"'";
628 },
629 { "span.usertype:after",
630 "trailing text for user types, mentioned on the user edit page",
631 @ content:"'";
632 },
633 { "span.wikiruleHead",
634 "format for leading text in wikirules definitions",
635 @ font-weight: bold;
636 },
637 { "td.tktDspLabel",
638 "format for labels on ticket display page",
639 @ text-align: right;
640 },
641 { "td.tktDspValue",
642 "format for values on ticket display page",
643 @ text-align: left;
644 @ vertical-align: top;
645 @ background-color: #d0d0d0;
646 },
647 { "span.tktError",
648 "format for ticket error messages",
649 @ color: red;
650 @ font-weight: bold;
651 },
652 { 0,
653 0,
654 0
655 }
656 };
657
658 /*
659 ** Append all of the default CSS to the CGI output.
660 */
661 void cgi_append_default_css(void) {
662 int i;
663
664 for (i=0;cssDefaultList[i].elementClass;i++){
665 if (cssDefaultList[i].elementClass[0]){
666 cgi_printf("/* %s */\n%s {\n%s\n}\n\n",
667 cssDefaultList[i].comment,
668 cssDefaultList[i].elementClass,
669 cssDefaultList[i].value
670 );
671 }else{
672 cgi_printf("%s",
673 cssDefaultList[i].value
674 );
675 }
676 }
677 }
678
679 /*
680 ** WEBPAGE: style.css
681 */
682 void page_style_css(void){
683 const char *zCSS = 0;
684 int i;
685
686 cgi_set_content_type("text/css");
687 zCSS = db_get("css",(char*)zDefaultCSS);
688 /* append user defined css */
689 cgi_append_content(zCSS, -1);
690 /* add special missing definitions */
691 for (i=1;cssDefaultList[i].elementClass;i++)
692 if (!strstr(zCSS,cssDefaultList[i].elementClass)) {
693 cgi_append_content("/* ", -1);
694 cgi_append_content(cssDefaultList[i].comment, -1);
695 cgi_append_content(" */\n", -1);
696 cgi_append_content(cssDefaultList[i].elementClass, -1);
697 cgi_append_content(" {\n", -1);
698 cgi_append_content(cssDefaultList[i].value, -1);
699 cgi_append_content("}\n\n", -1);
700 }
701 g.isConst = 1;
702 }
703
704 /*
705 ** WEBPAGE: test_env
706 */
707 void page_test_env(void){
708 style_header("Environment Test");
709 #if !defined(_WIN32)
710 @ uid=%d(getuid()), gid=%d(getgid())<br />
711 #endif
712 @ g.zBaseURL = %h(g.zBaseURL)<br />
713 @ g.zTop = %h(g.zTop)<br />
 
714 cgi_print_all();
715 style_footer();
716 }
717
+8 -6
--- src/tag.c
+++ src/tag.c
@@ -514,13 +514,14 @@
514514
);
515515
@ <ul>
516516
while( db_step(&q)==SQLITE_ROW ){
517517
const char *zName = db_column_text(&q, 0);
518518
if( g.okHistory ){
519
- @ <li><a href=%s(g.zBaseURL)/timeline?t=%T(zName)>%h(zName)</a></li>
519
+ @ <li><a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zName)">
520
+ @ %h(zName)</a></li>
520521
}else{
521
- @ <li><strong>%h(zName)</strong></li>
522
+ @ <li><span class="tagDsp">%h(zName)</span></li>
522523
}
523524
}
524525
@ </ul>
525526
db_finalize(&q);
526527
style_footer();
@@ -542,13 +543,14 @@
542543
rid
543544
);
544545
while( db_step(&q)==SQLITE_ROW ){
545546
const char *zTagName = db_column_text(&q, 0);
546547
if( g.okHistory ){
547
- @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[%h(zTagName)]</a>
548
+ @ <a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">
549
+ @ [%h(zTagName)]</a>
548550
}else{
549
- @ <b>[%h(zTagName)]</b>
551
+ @ <span class="tagDsp">[%h(zTagName)]</span>
550552
}
551553
}
552554
db_finalize(&q);
553555
}
554556
@@ -573,14 +575,14 @@
573575
" ORDER BY event.mtime DESC",
574576
timeline_query_for_www()
575577
);
576578
www_print_timeline(&q, 0, tagtimeline_extra);
577579
db_finalize(&q);
578
- @ <br clear="both">
579
- @ <script>
580
+ @ <br />
581
+ @ <script type="text/JavaScript">
580582
@ function xin(id){
581583
@ }
582584
@ function xout(id){
583585
@ }
584586
@ </script>
585587
style_footer();
586588
}
587589
--- src/tag.c
+++ src/tag.c
@@ -514,13 +514,14 @@
514 );
515 @ <ul>
516 while( db_step(&q)==SQLITE_ROW ){
517 const char *zName = db_column_text(&q, 0);
518 if( g.okHistory ){
519 @ <li><a href=%s(g.zBaseURL)/timeline?t=%T(zName)>%h(zName)</a></li>
 
520 }else{
521 @ <li><strong>%h(zName)</strong></li>
522 }
523 }
524 @ </ul>
525 db_finalize(&q);
526 style_footer();
@@ -542,13 +543,14 @@
542 rid
543 );
544 while( db_step(&q)==SQLITE_ROW ){
545 const char *zTagName = db_column_text(&q, 0);
546 if( g.okHistory ){
547 @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[%h(zTagName)]</a>
 
548 }else{
549 @ <b>[%h(zTagName)]</b>
550 }
551 }
552 db_finalize(&q);
553 }
554
@@ -573,14 +575,14 @@
573 " ORDER BY event.mtime DESC",
574 timeline_query_for_www()
575 );
576 www_print_timeline(&q, 0, tagtimeline_extra);
577 db_finalize(&q);
578 @ <br clear="both">
579 @ <script>
580 @ function xin(id){
581 @ }
582 @ function xout(id){
583 @ }
584 @ </script>
585 style_footer();
586 }
587
--- src/tag.c
+++ src/tag.c
@@ -514,13 +514,14 @@
514 );
515 @ <ul>
516 while( db_step(&q)==SQLITE_ROW ){
517 const char *zName = db_column_text(&q, 0);
518 if( g.okHistory ){
519 @ <li><a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zName)">
520 @ %h(zName)</a></li>
521 }else{
522 @ <li><span class="tagDsp">%h(zName)</span></li>
523 }
524 }
525 @ </ul>
526 db_finalize(&q);
527 style_footer();
@@ -542,13 +543,14 @@
543 rid
544 );
545 while( db_step(&q)==SQLITE_ROW ){
546 const char *zTagName = db_column_text(&q, 0);
547 if( g.okHistory ){
548 @ <a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">
549 @ [%h(zTagName)]</a>
550 }else{
551 @ <span class="tagDsp">[%h(zTagName)]</span>
552 }
553 }
554 db_finalize(&q);
555 }
556
@@ -573,14 +575,14 @@
575 " ORDER BY event.mtime DESC",
576 timeline_query_for_www()
577 );
578 www_print_timeline(&q, 0, tagtimeline_extra);
579 db_finalize(&q);
580 @ <br />
581 @ <script type="text/JavaScript">
582 @ function xin(id){
583 @ }
584 @ function xout(id){
585 @ }
586 @ </script>
587 style_footer();
588 }
589
+1 -1
--- src/th.c
+++ src/th.c
@@ -846,11 +846,11 @@
846846
847847
/* Gobble up input a word at a time until the end of the command
848848
** (a semi-colon or end of line).
849849
*/
850850
while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
851
- int nWord;
851
+ int nWord=0;
852852
thNextSpace(interp, zInput, nInput, &nSpace);
853853
rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1);
854854
zInput += (nSpace+nWord);
855855
nInput -= (nSpace+nWord);
856856
}
857857
--- src/th.c
+++ src/th.c
@@ -846,11 +846,11 @@
846
847 /* Gobble up input a word at a time until the end of the command
848 ** (a semi-colon or end of line).
849 */
850 while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
851 int nWord;
852 thNextSpace(interp, zInput, nInput, &nSpace);
853 rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1);
854 zInput += (nSpace+nWord);
855 nInput -= (nSpace+nWord);
856 }
857
--- src/th.c
+++ src/th.c
@@ -846,11 +846,11 @@
846
847 /* Gobble up input a word at a time until the end of the command
848 ** (a semi-colon or end of line).
849 */
850 while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){
851 int nWord=0;
852 thNextSpace(interp, zInput, nInput, &nSpace);
853 rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1);
854 zInput += (nSpace+nWord);
855 nInput -= (nSpace+nWord);
856 }
857
+2 -1
--- src/th_main.c
+++ src/th_main.c
@@ -278,11 +278,12 @@
278278
blob_reset(&name);
279279
for(i=0; i<nElem; i++){
280280
zH = htmlize((char*)azElem[i], aszElem[i]);
281281
if( zValue && aszElem[i]==nValue
282282
&& memcmp(zValue, azElem[i], nValue)==0 ){
283
- z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH);
283
+ z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
284
+ zH, zH);
284285
}else{
285286
z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
286287
}
287288
free(zH);
288289
sendText(z, -1, 0);
289290
--- src/th_main.c
+++ src/th_main.c
@@ -278,11 +278,12 @@
278 blob_reset(&name);
279 for(i=0; i<nElem; i++){
280 zH = htmlize((char*)azElem[i], aszElem[i]);
281 if( zValue && aszElem[i]==nValue
282 && memcmp(zValue, azElem[i], nValue)==0 ){
283 z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH);
 
284 }else{
285 z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
286 }
287 free(zH);
288 sendText(z, -1, 0);
289
--- src/th_main.c
+++ src/th_main.c
@@ -278,11 +278,12 @@
278 blob_reset(&name);
279 for(i=0; i<nElem; i++){
280 zH = htmlize((char*)azElem[i], aszElem[i]);
281 if( zValue && aszElem[i]==nValue
282 && memcmp(zValue, azElem[i], nValue)==0 ){
283 z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
284 zH, zH);
285 }else{
286 z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
287 }
288 free(zH);
289 sendText(z, -1, 0);
290
+37 -27
--- src/timeline.c
+++ src/timeline.c
@@ -48,13 +48,14 @@
4848
*/
4949
void hyperlink_to_uuid(const char *zUuid){
5050
char zShortUuid[UUID_SIZE+1];
5151
shorten_uuid(zShortUuid, zUuid);
5252
if( g.okHistory ){
53
- @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%s(zShortUuid)]</a>
53
+ @ <a class="timelineHistLink" href="%s(g.zBaseURL)/info/%s(zShortUuid)">
54
+ @ [%s(zShortUuid)]</a>
5455
}else{
55
- @ <b>[%s(zShortUuid)]</b>
56
+ @ <span class="timelineHistDsp">[%s(zShortUuid)]</span>
5657
}
5758
}
5859
5960
/*
6061
** Generate a hyperlink that invokes javascript to highlight
@@ -83,11 +84,11 @@
8384
void hyperlink_to_diff(const char *zV1, const char *zV2){
8485
if( g.okHistory ){
8586
if( zV2==0 ){
8687
@ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
8788
}else{
88
- @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
89
+ @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&amp;v2=%s(zV2)">[diff]</a>
8990
}
9091
}
9192
}
9293
9394
/*
@@ -109,11 +110,11 @@
109110
*/
110111
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
111112
if( zSuf==0 ) zSuf = "";
112113
if( g.okHistory ){
113114
if( zD && zD[0] ){
114
- @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf)
115
+ @ <a href="%s(g.zTop)/timeline?c=%T(zD)&amp;u=%T(zU)">%h(zU)</a>%s(zSuf)
115116
}else{
116117
@ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
117118
}
118119
}else{
119120
@ %s(zU)
@@ -189,14 +190,17 @@
189190
}else{
190191
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
191192
}
192193
if( tmFlags & TIMELINE_GRAPH ){
193194
pGraph = graph_init();
195
+ /* style is not moved to css, because this is
196
+ ** a technical div for the timeline graph
197
+ */
194198
@ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
195199
}
196200
197
- @ <table cellspacing=0 border=0 cellpadding=0>
201
+ @ <table class="timelineTable">
198202
blob_zero(&comment);
199203
while( db_step(pQuery)==SQLITE_ROW ){
200204
int rid = db_column_int(pQuery, 0);
201205
const char *zUuid = db_column_text(pQuery, 1);
202206
int isLeaf = db_column_int(pQuery, 5);
@@ -218,30 +222,30 @@
218222
}
219223
}
220224
}
221225
prevTagid = tagid;
222226
if( suppressCnt ){
223
- @ <tr><td><td><td>
224
- @ <small><i>... %d(suppressCnt) similar
225
- @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
227
+ @ <tr><td /><td /><td>
228
+ @ <span class="timelineDisabled">... %d(suppressCnt) similar
229
+ @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
226230
suppressCnt = 0;
227231
}
228232
if( strcmp(zType,"div")==0 ){
229
- @ <tr><td colspan=3><hr></td></tr>
233
+ @ <tr><td colspan="3"><hr /></td></tr>
230234
continue;
231235
}
232236
if( memcmp(zDate, zPrevDate, 10) ){
233237
sprintf(zPrevDate, "%.10s", zDate);
234238
@ <tr><td>
235
- @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
239
+ @ <div class="divider">%s(zPrevDate)</div>
236240
@ </td></tr>
237241
}
238242
memcpy(zTime, &zDate[11], 5);
239243
zTime[5] = 0;
240244
@ <tr>
241
- @ <td valign="top" align="right">%s(zTime)</td>
242
- @ <td width="20" align="left" valign="top">
245
+ @ <td class="timelineTime">%s(zTime)</td>
246
+ @ <td class="timelineGraph">
243247
if( pGraph && zType[0]=='c' ){
244248
int nParent = 0;
245249
int aParent[32];
246250
const char *zBr;
247251
int gidx;
@@ -267,24 +271,25 @@
267271
}
268272
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
269273
db_reset(&qbranch);
270274
@ <div id="m%d(gidx)"></div>
271275
}
276
+ @</td>
272277
if( zBgClr && zBgClr[0] ){
273
- @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
278
+ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
274279
}else{
275
- @ <td valign="top" align="left">
280
+ @ <td class="timelineTableCell">
276281
}
277282
if( zType[0]=='c' ){
278283
hyperlink_to_uuid(zUuid);
279284
if( isLeaf ){
280285
if( db_exists("SELECT 1 FROM tagxref"
281286
" WHERE rid=%d AND tagid=%d AND tagtype>0",
282287
rid, TAG_CLOSED) ){
283
- @ <b>Closed-Leaf:</b>
288
+ @ <span class="timelineLeaf">Closed-Leaf:</span>
284289
}else{
285
- @ <b>Leaf:</b>
290
+ @ <span class="timelineLeaf">Leaf:</span>
286291
}
287292
}
288293
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
289294
hyperlink_to_uuid(zUuid);
290295
}
@@ -309,22 +314,27 @@
309314
xExtra(rid);
310315
}
311316
@ </td></tr>
312317
}
313318
if( suppressCnt ){
314
- @ <tr><td><td><td>
315
- @ <small><i>... %d(suppressCnt) similar
316
- @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
319
+ @ <tr><td /><td /><td>
320
+ @ <span class="timelineDisabled">... %d(suppressCnt) similar
321
+ @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
317322
suppressCnt = 0;
318323
}
319324
if( pGraph ){
320325
graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
321326
if( pGraph->nErr ){
322327
graph_free(pGraph);
323328
pGraph = 0;
324329
}else{
325
- @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
330
+ /* style is not moved to css, because this is
331
+ ** a technical div for the timeline graph
332
+ */
333
+ @ <tr><td /><td>
334
+ @ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
335
+ @ </td></tr>
326336
}
327337
}
328338
@ </table>
329339
timeline_output_graph_javascript(pGraph);
330340
}
@@ -336,11 +346,11 @@
336346
void timeline_output_graph_javascript(GraphContext *pGraph){
337347
if( pGraph && pGraph->nErr==0 ){
338348
GraphRow *pRow;
339349
int i;
340350
char cSep;
341
- @ <script type="text/JavaScript">
351
+ @ <script type="text/JavaScript">
342352
cgi_printf("var rowinfo = [\n");
343353
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
344354
cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
345355
pRow->idx,
346356
pRow->zBgClr,
@@ -352,20 +362,20 @@
352362
);
353363
cSep = '[';
354364
for(i=0; i<GR_MAX_RAIL; i++){
355365
if( i==pRow->iRail ) continue;
356366
if( pRow->aiRaiser[i]>0 ){
357
- cgi_printf("%c%d,%d", cSep, pGraph->railMap[i], pRow->aiRaiser[i]);
367
+ cgi_printf("%c%d,%d", cSep, i, pRow->aiRaiser[i]);
358368
cSep = ',';
359369
}
360370
}
361371
if( cSep=='[' ) cgi_printf("[");
362372
cgi_printf("],mi:");
363373
cSep = '[';
364374
for(i=0; i<GR_MAX_RAIL; i++){
365375
if( pRow->mergeIn & (1<<i) ){
366
- cgi_printf("%c%d", cSep, pGraph->railMap[i]);
376
+ cgi_printf("%c%d", cSep, i);
367377
cSep = ',';
368378
}
369379
}
370380
if( cSep=='[' ) cgi_printf("[");
371381
cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n");
@@ -482,11 +492,11 @@
482492
@ var width = nrail*20;
483493
@ for(var i in rowinfo){
484494
@ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
485495
@ rowinfo[i].x = left + rowinfo[i].r*20;
486496
@ }
487
- @ var btm = rowinfo[rowinfo.length-1].y + 20;
497
+ @ var btm = absoluteY("grbtm") + 10 - canvasY;
488498
@ if( btm<32768 ){
489499
@ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
490500
@ 'style="position:absolute;left:'+(left-5)+'px;"' +
491501
@ ' width="'+width+'" height="'+btm+'"></canvas>';
492502
@ realCanvas = document.getElementById('timeline-canvas');
@@ -882,15 +892,15 @@
882892
}else if( zBrName ){
883893
blob_appendf(&desc, " related to \"%h\"", zBrName);
884894
tmFlags |= TIMELINE_DISJOINT;
885895
}
886896
if( zAfter ){
887
- blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
897
+ blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
888898
}else if( zBefore ){
889
- blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
899
+ blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
890900
}else if( zCirca ){
891
- blob_appendf(&desc, " occurring around %h.<br>", zCirca);
901
+ blob_appendf(&desc, " occurring around %h.<br />", zCirca);
892902
}
893903
if( zSearch ){
894904
blob_appendf(&desc, " matching \"%h\"", zSearch);
895905
}
896906
if( g.okHistory ){
897907
--- src/timeline.c
+++ src/timeline.c
@@ -48,13 +48,14 @@
48 */
49 void hyperlink_to_uuid(const char *zUuid){
50 char zShortUuid[UUID_SIZE+1];
51 shorten_uuid(zShortUuid, zUuid);
52 if( g.okHistory ){
53 @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%s(zShortUuid)]</a>
 
54 }else{
55 @ <b>[%s(zShortUuid)]</b>
56 }
57 }
58
59 /*
60 ** Generate a hyperlink that invokes javascript to highlight
@@ -83,11 +84,11 @@
83 void hyperlink_to_diff(const char *zV1, const char *zV2){
84 if( g.okHistory ){
85 if( zV2==0 ){
86 @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
87 }else{
88 @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
89 }
90 }
91 }
92
93 /*
@@ -109,11 +110,11 @@
109 */
110 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
111 if( zSuf==0 ) zSuf = "";
112 if( g.okHistory ){
113 if( zD && zD[0] ){
114 @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf)
115 }else{
116 @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
117 }
118 }else{
119 @ %s(zU)
@@ -189,14 +190,17 @@
189 }else{
190 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
191 }
192 if( tmFlags & TIMELINE_GRAPH ){
193 pGraph = graph_init();
 
 
 
194 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
195 }
196
197 @ <table cellspacing=0 border=0 cellpadding=0>
198 blob_zero(&comment);
199 while( db_step(pQuery)==SQLITE_ROW ){
200 int rid = db_column_int(pQuery, 0);
201 const char *zUuid = db_column_text(pQuery, 1);
202 int isLeaf = db_column_int(pQuery, 5);
@@ -218,30 +222,30 @@
218 }
219 }
220 }
221 prevTagid = tagid;
222 if( suppressCnt ){
223 @ <tr><td><td><td>
224 @ <small><i>... %d(suppressCnt) similar
225 @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
226 suppressCnt = 0;
227 }
228 if( strcmp(zType,"div")==0 ){
229 @ <tr><td colspan=3><hr></td></tr>
230 continue;
231 }
232 if( memcmp(zDate, zPrevDate, 10) ){
233 sprintf(zPrevDate, "%.10s", zDate);
234 @ <tr><td>
235 @ <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
236 @ </td></tr>
237 }
238 memcpy(zTime, &zDate[11], 5);
239 zTime[5] = 0;
240 @ <tr>
241 @ <td valign="top" align="right">%s(zTime)</td>
242 @ <td width="20" align="left" valign="top">
243 if( pGraph && zType[0]=='c' ){
244 int nParent = 0;
245 int aParent[32];
246 const char *zBr;
247 int gidx;
@@ -267,24 +271,25 @@
267 }
268 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
269 db_reset(&qbranch);
270 @ <div id="m%d(gidx)"></div>
271 }
 
272 if( zBgClr && zBgClr[0] ){
273 @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
274 }else{
275 @ <td valign="top" align="left">
276 }
277 if( zType[0]=='c' ){
278 hyperlink_to_uuid(zUuid);
279 if( isLeaf ){
280 if( db_exists("SELECT 1 FROM tagxref"
281 " WHERE rid=%d AND tagid=%d AND tagtype>0",
282 rid, TAG_CLOSED) ){
283 @ <b>Closed-Leaf:</b>
284 }else{
285 @ <b>Leaf:</b>
286 }
287 }
288 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
289 hyperlink_to_uuid(zUuid);
290 }
@@ -309,22 +314,27 @@
309 xExtra(rid);
310 }
311 @ </td></tr>
312 }
313 if( suppressCnt ){
314 @ <tr><td><td><td>
315 @ <small><i>... %d(suppressCnt) similar
316 @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
317 suppressCnt = 0;
318 }
319 if( pGraph ){
320 graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
321 if( pGraph->nErr ){
322 graph_free(pGraph);
323 pGraph = 0;
324 }else{
325 @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
 
 
 
 
 
326 }
327 }
328 @ </table>
329 timeline_output_graph_javascript(pGraph);
330 }
@@ -336,11 +346,11 @@
336 void timeline_output_graph_javascript(GraphContext *pGraph){
337 if( pGraph && pGraph->nErr==0 ){
338 GraphRow *pRow;
339 int i;
340 char cSep;
341 @ <script type="text/JavaScript">
342 cgi_printf("var rowinfo = [\n");
343 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
344 cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
345 pRow->idx,
346 pRow->zBgClr,
@@ -352,20 +362,20 @@
352 );
353 cSep = '[';
354 for(i=0; i<GR_MAX_RAIL; i++){
355 if( i==pRow->iRail ) continue;
356 if( pRow->aiRaiser[i]>0 ){
357 cgi_printf("%c%d,%d", cSep, pGraph->railMap[i], pRow->aiRaiser[i]);
358 cSep = ',';
359 }
360 }
361 if( cSep=='[' ) cgi_printf("[");
362 cgi_printf("],mi:");
363 cSep = '[';
364 for(i=0; i<GR_MAX_RAIL; i++){
365 if( pRow->mergeIn & (1<<i) ){
366 cgi_printf("%c%d", cSep, pGraph->railMap[i]);
367 cSep = ',';
368 }
369 }
370 if( cSep=='[' ) cgi_printf("[");
371 cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n");
@@ -482,11 +492,11 @@
482 @ var width = nrail*20;
483 @ for(var i in rowinfo){
484 @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
485 @ rowinfo[i].x = left + rowinfo[i].r*20;
486 @ }
487 @ var btm = rowinfo[rowinfo.length-1].y + 20;
488 @ if( btm<32768 ){
489 @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
490 @ 'style="position:absolute;left:'+(left-5)+'px;"' +
491 @ ' width="'+width+'" height="'+btm+'"></canvas>';
492 @ realCanvas = document.getElementById('timeline-canvas');
@@ -882,15 +892,15 @@
882 }else if( zBrName ){
883 blob_appendf(&desc, " related to \"%h\"", zBrName);
884 tmFlags |= TIMELINE_DISJOINT;
885 }
886 if( zAfter ){
887 blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
888 }else if( zBefore ){
889 blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
890 }else if( zCirca ){
891 blob_appendf(&desc, " occurring around %h.<br>", zCirca);
892 }
893 if( zSearch ){
894 blob_appendf(&desc, " matching \"%h\"", zSearch);
895 }
896 if( g.okHistory ){
897
--- src/timeline.c
+++ src/timeline.c
@@ -48,13 +48,14 @@
48 */
49 void hyperlink_to_uuid(const char *zUuid){
50 char zShortUuid[UUID_SIZE+1];
51 shorten_uuid(zShortUuid, zUuid);
52 if( g.okHistory ){
53 @ <a class="timelineHistLink" href="%s(g.zBaseURL)/info/%s(zShortUuid)">
54 @ [%s(zShortUuid)]</a>
55 }else{
56 @ <span class="timelineHistDsp">[%s(zShortUuid)]</span>
57 }
58 }
59
60 /*
61 ** Generate a hyperlink that invokes javascript to highlight
@@ -83,11 +84,11 @@
84 void hyperlink_to_diff(const char *zV1, const char *zV2){
85 if( g.okHistory ){
86 if( zV2==0 ){
87 @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
88 }else{
89 @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&amp;v2=%s(zV2)">[diff]</a>
90 }
91 }
92 }
93
94 /*
@@ -109,11 +110,11 @@
110 */
111 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
112 if( zSuf==0 ) zSuf = "";
113 if( g.okHistory ){
114 if( zD && zD[0] ){
115 @ <a href="%s(g.zTop)/timeline?c=%T(zD)&amp;u=%T(zU)">%h(zU)</a>%s(zSuf)
116 }else{
117 @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
118 }
119 }else{
120 @ %s(zU)
@@ -189,14 +190,17 @@
190 }else{
191 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
192 }
193 if( tmFlags & TIMELINE_GRAPH ){
194 pGraph = graph_init();
195 /* style is not moved to css, because this is
196 ** a technical div for the timeline graph
197 */
198 @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
199 }
200
201 @ <table class="timelineTable">
202 blob_zero(&comment);
203 while( db_step(pQuery)==SQLITE_ROW ){
204 int rid = db_column_int(pQuery, 0);
205 const char *zUuid = db_column_text(pQuery, 1);
206 int isLeaf = db_column_int(pQuery, 5);
@@ -218,30 +222,30 @@
222 }
223 }
224 }
225 prevTagid = tagid;
226 if( suppressCnt ){
227 @ <tr><td /><td /><td>
228 @ <span class="timelineDisabled">... %d(suppressCnt) similar
229 @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
230 suppressCnt = 0;
231 }
232 if( strcmp(zType,"div")==0 ){
233 @ <tr><td colspan="3"><hr /></td></tr>
234 continue;
235 }
236 if( memcmp(zDate, zPrevDate, 10) ){
237 sprintf(zPrevDate, "%.10s", zDate);
238 @ <tr><td>
239 @ <div class="divider">%s(zPrevDate)</div>
240 @ </td></tr>
241 }
242 memcpy(zTime, &zDate[11], 5);
243 zTime[5] = 0;
244 @ <tr>
245 @ <td class="timelineTime">%s(zTime)</td>
246 @ <td class="timelineGraph">
247 if( pGraph && zType[0]=='c' ){
248 int nParent = 0;
249 int aParent[32];
250 const char *zBr;
251 int gidx;
@@ -267,24 +271,25 @@
271 }
272 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
273 db_reset(&qbranch);
274 @ <div id="m%d(gidx)"></div>
275 }
276 @</td>
277 if( zBgClr && zBgClr[0] ){
278 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
279 }else{
280 @ <td class="timelineTableCell">
281 }
282 if( zType[0]=='c' ){
283 hyperlink_to_uuid(zUuid);
284 if( isLeaf ){
285 if( db_exists("SELECT 1 FROM tagxref"
286 " WHERE rid=%d AND tagid=%d AND tagtype>0",
287 rid, TAG_CLOSED) ){
288 @ <span class="timelineLeaf">Closed-Leaf:</span>
289 }else{
290 @ <span class="timelineLeaf">Leaf:</span>
291 }
292 }
293 }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
294 hyperlink_to_uuid(zUuid);
295 }
@@ -309,22 +314,27 @@
314 xExtra(rid);
315 }
316 @ </td></tr>
317 }
318 if( suppressCnt ){
319 @ <tr><td /><td /><td>
320 @ <span class="timelineDisabled">... %d(suppressCnt) similar
321 @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
322 suppressCnt = 0;
323 }
324 if( pGraph ){
325 graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
326 if( pGraph->nErr ){
327 graph_free(pGraph);
328 pGraph = 0;
329 }else{
330 /* style is not moved to css, because this is
331 ** a technical div for the timeline graph
332 */
333 @ <tr><td /><td>
334 @ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
335 @ </td></tr>
336 }
337 }
338 @ </table>
339 timeline_output_graph_javascript(pGraph);
340 }
@@ -336,11 +346,11 @@
346 void timeline_output_graph_javascript(GraphContext *pGraph){
347 if( pGraph && pGraph->nErr==0 ){
348 GraphRow *pRow;
349 int i;
350 char cSep;
351 @ <script type="text/JavaScript">
352 cgi_printf("var rowinfo = [\n");
353 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
354 cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
355 pRow->idx,
356 pRow->zBgClr,
@@ -352,20 +362,20 @@
362 );
363 cSep = '[';
364 for(i=0; i<GR_MAX_RAIL; i++){
365 if( i==pRow->iRail ) continue;
366 if( pRow->aiRaiser[i]>0 ){
367 cgi_printf("%c%d,%d", cSep, i, pRow->aiRaiser[i]);
368 cSep = ',';
369 }
370 }
371 if( cSep=='[' ) cgi_printf("[");
372 cgi_printf("],mi:");
373 cSep = '[';
374 for(i=0; i<GR_MAX_RAIL; i++){
375 if( pRow->mergeIn & (1<<i) ){
376 cgi_printf("%c%d", cSep, i);
377 cSep = ',';
378 }
379 }
380 if( cSep=='[' ) cgi_printf("[");
381 cgi_printf("]}%s", pRow->pNext ? ",\n" : "];\n");
@@ -482,11 +492,11 @@
492 @ var width = nrail*20;
493 @ for(var i in rowinfo){
494 @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY;
495 @ rowinfo[i].x = left + rowinfo[i].r*20;
496 @ }
497 @ var btm = absoluteY("grbtm") + 10 - canvasY;
498 @ if( btm<32768 ){
499 @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
500 @ 'style="position:absolute;left:'+(left-5)+'px;"' +
501 @ ' width="'+width+'" height="'+btm+'"></canvas>';
502 @ realCanvas = document.getElementById('timeline-canvas');
@@ -882,15 +892,15 @@
892 }else if( zBrName ){
893 blob_appendf(&desc, " related to \"%h\"", zBrName);
894 tmFlags |= TIMELINE_DISJOINT;
895 }
896 if( zAfter ){
897 blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
898 }else if( zBefore ){
899 blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
900 }else if( zCirca ){
901 blob_appendf(&desc, " occurring around %h.<br />", zCirca);
902 }
903 if( zSearch ){
904 blob_appendf(&desc, " matching \"%h\"", zSearch);
905 }
906 if( g.okHistory ){
907
+22 -14
--- src/tkt.c
+++ src/tkt.c
@@ -313,11 +313,11 @@
313313
style_submenu_element("New Ticket", "Create a new ticket",
314314
"%s/tktnew", g.zTop);
315315
}
316316
if( g.okApndTkt && g.okAttach ){
317317
style_submenu_element("Attach", "Add An Attachment",
318
- "%s/attachadd?tkt=%T&from=%s/tktview/%t",
318
+ "%s/attachadd?tkt=%T&amp;from=%s/tktview/%t",
319319
g.zTop, zUuid, g.zTop, zUuid);
320320
}
321321
style_header("View Ticket");
322322
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
323323
ticket_init();
@@ -342,19 +342,24 @@
342342
while( db_step(&q)==SQLITE_ROW ){
343343
const char *zDate = db_column_text(&q, 0);
344344
const char *zFile = db_column_text(&q, 1);
345345
const char *zUser = db_column_text(&q, 2);
346346
if( cnt==0 ){
347
- @ <hr><h2>Attachments:</h2>
347
+ @ <hr /><h2>Attachments:</h2>
348348
@ <ul>
349349
}
350350
cnt++;
351
- @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
352
- @ %h(zFile)</a> add by %h(zUser) on
351
+ if( g.okRead && g.okHistory ){
352
+ @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&amp;file=%t(zFile)">
353
+ @ %h(zFile)</a>
354
+ }else{
355
+ @ %h(zFile)
356
+ }
357
+ @ added by %h(zUser) on
353358
hyperlink_to_date(zDate, ".");
354359
if( g.okWrTkt && g.okAttach ){
355
- @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
360
+ @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
356361
}
357362
}
358363
if( cnt ){
359364
@ </ul>
360365
}
@@ -506,12 +511,13 @@
506511
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
507512
ticket_init();
508513
getAllTicketFields();
509514
initializeVariablesFromDb();
510515
initializeVariablesFromCGI();
511
- @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
516
+ @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
512517
login_insert_csrf_secret();
518
+ @ </p>
513519
zScript = ticket_newpage_code();
514520
Th_Store("login", g.zLogin);
515521
Th_Store("date", db_text(0, "SELECT datetime('now')"));
516522
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
517523
(void*)&zNewUuid, 0);
@@ -549,34 +555,36 @@
549555
cgi_redirectf("tktview?name=%T", zName);
550556
}
551557
style_header("Edit Ticket");
552558
if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
553559
|| !validate16(zName,nName) ){
554
- @ <font color="red"><b>Not a valid ticket id: \"%h(zName)\"</b></font>
560
+ @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
555561
style_footer();
556562
return;
557563
}
558564
nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
559565
zName);
560566
if( nRec==0 ){
561
- @ <font color="red"><b>No such ticket: \"%h(zName)\"</b></font>
567
+ @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
562568
style_footer();
563569
return;
564570
}
565571
if( nRec>1 ){
566
- @ <font color="red"><b>%d(nRec) tickets begin with: \"%h(zName)\"</b></font>
572
+ @ <span class="tktError"><b>%d(nRec) tickets begin with:
573
+ @ \"%h(zName)\"</span>
567574
style_footer();
568575
return;
569576
}
570577
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
571578
ticket_init();
572579
getAllTicketFields();
573580
initializeVariablesFromCGI();
574581
initializeVariablesFromDb();
575
- @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
576
- @ <input type="hidden" name="name" value="%s(zName)">
582
+ @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
583
+ @ <input type="hidden" name="name" value="%s(zName)" />
577584
login_insert_csrf_secret();
585
+ @ </p>
578586
zScript = ticket_editpage_code();
579587
Th_Store("login", g.zLogin);
580588
Th_Store("date", db_text(0, "SELECT datetime('now')"));
581589
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
582590
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
@@ -619,11 +627,11 @@
619627
return 0;
620628
}
621629
622630
/*
623631
** WEBPAGE: tkttimeline
624
-** URL: /tkttimeline?name=TICKETUUID&y=TYPE
632
+** URL: /tkttimeline?name=TICKETUUID&amp;y=TYPE
625633
**
626634
** Show the change history for a single ticket in timeline format.
627635
*/
628636
void tkttimeline_page(void){
629637
Stmt q;
@@ -639,11 +647,11 @@
639647
if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
640648
zUuid = PD("name","");
641649
zType = PD("y","a");
642650
if( zType[0]!='c' ){
643651
style_submenu_element("Check-ins", "Check-ins",
644
- "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
652
+ "%s/tkttimeline?name=%T&amp;y=ci", g.zTop, zUuid);
645653
}else{
646654
style_submenu_element("Timeline", "Timeline",
647655
"%s/tkttimeline?name=%T", g.zTop, zUuid);
648656
}
649657
style_submenu_element("History", "History",
@@ -771,12 +779,12 @@
771779
@ <p>Ticket change
772780
@ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
773781
@ (rid %d(rid)) by
774782
hyperlink_to_user(m.zUser,zDate," on");
775783
hyperlink_to_date(zDate, ":");
776
- ticket_output_change_artifact(&m);
777784
@ </p>
785
+ ticket_output_change_artifact(&m);
778786
}
779787
manifest_clear(&m);
780788
}
781789
}
782790
db_finalize(&q);
783791
--- src/tkt.c
+++ src/tkt.c
@@ -313,11 +313,11 @@
313 style_submenu_element("New Ticket", "Create a new ticket",
314 "%s/tktnew", g.zTop);
315 }
316 if( g.okApndTkt && g.okAttach ){
317 style_submenu_element("Attach", "Add An Attachment",
318 "%s/attachadd?tkt=%T&from=%s/tktview/%t",
319 g.zTop, zUuid, g.zTop, zUuid);
320 }
321 style_header("View Ticket");
322 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
323 ticket_init();
@@ -342,19 +342,24 @@
342 while( db_step(&q)==SQLITE_ROW ){
343 const char *zDate = db_column_text(&q, 0);
344 const char *zFile = db_column_text(&q, 1);
345 const char *zUser = db_column_text(&q, 2);
346 if( cnt==0 ){
347 @ <hr><h2>Attachments:</h2>
348 @ <ul>
349 }
350 cnt++;
351 @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
352 @ %h(zFile)</a> add by %h(zUser) on
 
 
 
 
 
353 hyperlink_to_date(zDate, ".");
354 if( g.okWrTkt && g.okAttach ){
355 @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
356 }
357 }
358 if( cnt ){
359 @ </ul>
360 }
@@ -506,12 +511,13 @@
506 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
507 ticket_init();
508 getAllTicketFields();
509 initializeVariablesFromDb();
510 initializeVariablesFromCGI();
511 @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
512 login_insert_csrf_secret();
 
513 zScript = ticket_newpage_code();
514 Th_Store("login", g.zLogin);
515 Th_Store("date", db_text(0, "SELECT datetime('now')"));
516 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
517 (void*)&zNewUuid, 0);
@@ -549,34 +555,36 @@
549 cgi_redirectf("tktview?name=%T", zName);
550 }
551 style_header("Edit Ticket");
552 if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
553 || !validate16(zName,nName) ){
554 @ <font color="red"><b>Not a valid ticket id: \"%h(zName)\"</b></font>
555 style_footer();
556 return;
557 }
558 nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
559 zName);
560 if( nRec==0 ){
561 @ <font color="red"><b>No such ticket: \"%h(zName)\"</b></font>
562 style_footer();
563 return;
564 }
565 if( nRec>1 ){
566 @ <font color="red"><b>%d(nRec) tickets begin with: \"%h(zName)\"</b></font>
 
567 style_footer();
568 return;
569 }
570 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
571 ticket_init();
572 getAllTicketFields();
573 initializeVariablesFromCGI();
574 initializeVariablesFromDb();
575 @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
576 @ <input type="hidden" name="name" value="%s(zName)">
577 login_insert_csrf_secret();
 
578 zScript = ticket_editpage_code();
579 Th_Store("login", g.zLogin);
580 Th_Store("date", db_text(0, "SELECT datetime('now')"));
581 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
582 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
@@ -619,11 +627,11 @@
619 return 0;
620 }
621
622 /*
623 ** WEBPAGE: tkttimeline
624 ** URL: /tkttimeline?name=TICKETUUID&y=TYPE
625 **
626 ** Show the change history for a single ticket in timeline format.
627 */
628 void tkttimeline_page(void){
629 Stmt q;
@@ -639,11 +647,11 @@
639 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
640 zUuid = PD("name","");
641 zType = PD("y","a");
642 if( zType[0]!='c' ){
643 style_submenu_element("Check-ins", "Check-ins",
644 "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
645 }else{
646 style_submenu_element("Timeline", "Timeline",
647 "%s/tkttimeline?name=%T", g.zTop, zUuid);
648 }
649 style_submenu_element("History", "History",
@@ -771,12 +779,12 @@
771 @ <p>Ticket change
772 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
773 @ (rid %d(rid)) by
774 hyperlink_to_user(m.zUser,zDate," on");
775 hyperlink_to_date(zDate, ":");
776 ticket_output_change_artifact(&m);
777 @ </p>
 
778 }
779 manifest_clear(&m);
780 }
781 }
782 db_finalize(&q);
783
--- src/tkt.c
+++ src/tkt.c
@@ -313,11 +313,11 @@
313 style_submenu_element("New Ticket", "Create a new ticket",
314 "%s/tktnew", g.zTop);
315 }
316 if( g.okApndTkt && g.okAttach ){
317 style_submenu_element("Attach", "Add An Attachment",
318 "%s/attachadd?tkt=%T&amp;from=%s/tktview/%t",
319 g.zTop, zUuid, g.zTop, zUuid);
320 }
321 style_header("View Ticket");
322 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
323 ticket_init();
@@ -342,19 +342,24 @@
342 while( db_step(&q)==SQLITE_ROW ){
343 const char *zDate = db_column_text(&q, 0);
344 const char *zFile = db_column_text(&q, 1);
345 const char *zUser = db_column_text(&q, 2);
346 if( cnt==0 ){
347 @ <hr /><h2>Attachments:</h2>
348 @ <ul>
349 }
350 cnt++;
351 if( g.okRead && g.okHistory ){
352 @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&amp;file=%t(zFile)">
353 @ %h(zFile)</a>
354 }else{
355 @ %h(zFile)
356 }
357 @ added by %h(zUser) on
358 hyperlink_to_date(zDate, ".");
359 if( g.okWrTkt && g.okAttach ){
360 @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
361 }
362 }
363 if( cnt ){
364 @ </ul>
365 }
@@ -506,12 +511,13 @@
511 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
512 ticket_init();
513 getAllTicketFields();
514 initializeVariablesFromDb();
515 initializeVariablesFromCGI();
516 @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
517 login_insert_csrf_secret();
518 @ </p>
519 zScript = ticket_newpage_code();
520 Th_Store("login", g.zLogin);
521 Th_Store("date", db_text(0, "SELECT datetime('now')"));
522 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
523 (void*)&zNewUuid, 0);
@@ -549,34 +555,36 @@
555 cgi_redirectf("tktview?name=%T", zName);
556 }
557 style_header("Edit Ticket");
558 if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
559 || !validate16(zName,nName) ){
560 @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
561 style_footer();
562 return;
563 }
564 nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
565 zName);
566 if( nRec==0 ){
567 @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
568 style_footer();
569 return;
570 }
571 if( nRec>1 ){
572 @ <span class="tktError"><b>%d(nRec) tickets begin with:
573 @ \"%h(zName)\"</span>
574 style_footer();
575 return;
576 }
577 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
578 ticket_init();
579 getAllTicketFields();
580 initializeVariablesFromCGI();
581 initializeVariablesFromDb();
582 @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
583 @ <input type="hidden" name="name" value="%s(zName)" />
584 login_insert_csrf_secret();
585 @ </p>
586 zScript = ticket_editpage_code();
587 Th_Store("login", g.zLogin);
588 Th_Store("date", db_text(0, "SELECT datetime('now')"));
589 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
590 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
@@ -619,11 +627,11 @@
627 return 0;
628 }
629
630 /*
631 ** WEBPAGE: tkttimeline
632 ** URL: /tkttimeline?name=TICKETUUID&amp;y=TYPE
633 **
634 ** Show the change history for a single ticket in timeline format.
635 */
636 void tkttimeline_page(void){
637 Stmt q;
@@ -639,11 +647,11 @@
647 if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
648 zUuid = PD("name","");
649 zType = PD("y","a");
650 if( zType[0]!='c' ){
651 style_submenu_element("Check-ins", "Check-ins",
652 "%s/tkttimeline?name=%T&amp;y=ci", g.zTop, zUuid);
653 }else{
654 style_submenu_element("Timeline", "Timeline",
655 "%s/tkttimeline?name=%T", g.zTop, zUuid);
656 }
657 style_submenu_element("History", "History",
@@ -771,12 +779,12 @@
779 @ <p>Ticket change
780 @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
781 @ (rid %d(rid)) by
782 hyperlink_to_user(m.zUser,zDate," on");
783 hyperlink_to_date(zDate, ":");
 
784 @ </p>
785 ticket_output_change_artifact(&m);
786 }
787 manifest_clear(&m);
788 }
789 }
790 db_finalize(&q);
791
+94 -98
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -130,21 +130,21 @@
130130
db_set(zDbField, z, 0);
131131
if( xRebuild ) xRebuild();
132132
cgi_redirect("tktsetup");
133133
}
134134
}
135
- @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
135
+ @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>
136136
login_insert_csrf_secret();
137137
@ <p>%s(zDesc)</p>
138138
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
139
- @ <blockquote>
140
- @ <input type="submit" name="submit" value="Apply Changes">
141
- @ <input type="submit" name="clear" value="Revert To Default">
142
- @ <input type="submit" name="setup" value="Cancel">
143
- @ </blockquote>
144
- @ </form>
145
- @ <hr>
139
+ @ <blockquote><p>
140
+ @ <input type="submit" name="submit" value="Apply Changes" />
141
+ @ <input type="submit" name="clear" value="Revert To Default" />
142
+ @ <input type="submit" name="setup" value="Cancel" />
143
+ @ </p></blockquote>
144
+ @ </div></form>
145
+ @ <hr />
146146
@ <h2>Default %s(zTitle)</h2>
147147
@ <blockquote><pre>
148148
@ %h(zDfltValue)
149149
@ </pre></blockquote>
150150
style_footer();
@@ -153,13 +153,13 @@
153153
/*
154154
** WEBPAGE: tktsetup_tab
155155
*/
156156
void tktsetup_tab_page(void){
157157
static const char zDesc[] =
158
- @ <p>Enter a valid CREATE TABLE statement for the "ticket" table. The
158
+ @ Enter a valid CREATE TABLE statement for the "ticket" table. The
159159
@ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
160
- @ with an unique index on "tkt_uuid" and "tkt_mtime".</p>
160
+ @ with an unique index on "tkt_uuid" and "tkt_mtime".
161161
;
162162
tktsetup_generic(
163163
"Ticket Table Schema",
164164
"ticket-table",
165165
zDefaultTicketTable,
@@ -229,12 +229,12 @@
229229
/*
230230
** WEBPAGE: tktsetup_com
231231
*/
232232
void tktsetup_com_page(void){
233233
static const char zDesc[] =
234
- @ <p>Enter TH1 script that initializes variables prior to generating
235
- @ any of the ticket view, edit, or creation pages.</p>
234
+ @ Enter TH1 script that initializes variables prior to generating
235
+ @ any of the ticket view, edit, or creation pages.
236236
;
237237
tktsetup_generic(
238238
"Ticket Common Script",
239239
"ticket-common",
240240
zDefaultTicketCommon,
@@ -250,80 +250,80 @@
250250
@ if {[info exists submit]} {
251251
@ set status Open
252252
@ submit_ticket
253253
@ }
254254
@ </th1>
255
-@ <h1 align="center">Enter A New Ticket</h1>
255
+@ <h1 style="text-align: center;">Enter A New Ticket</h1>
256256
@ <table cellpadding="5">
257257
@ <tr>
258258
@ <td colspan="2">
259
-@ Enter a one-line summary of the ticket:<br>
260
-@ <input type="text" name="title" size="60" value="$<title>">
259
+@ Enter a one-line summary of the ticket:<br />
260
+@ <input type="text" name="title" size="60" value="$<title>" />
261261
@ </td>
262262
@ </tr>
263263
@
264264
@ <tr>
265
-@ <td align="right">Type:
265
+@ <td style="text-align: center;">Type:
266266
@ <th1>combobox type $type_choices 1</th1>
267267
@ </td>
268268
@ <td>What type of ticket is this?</td>
269269
@ </tr>
270270
@
271271
@ <tr>
272
-@ <td align="right">Version:
273
-@ <input type="text" name="foundin" size="20" value="$<foundin>">
272
+@ <td style="text-align: center;">Version:
273
+@ <input type="text" name="foundin" size="20" value="$<foundin>" />
274274
@ </td>
275275
@ <td>In what version or build number do you observe the problem?</td>
276276
@ </tr>
277277
@
278278
@ <tr>
279
-@ <td align="right">Severity:
279
+@ <td style="text-align: center;">Severity:
280280
@ <th1>combobox severity $severity_choices 1</th1>
281281
@ </td>
282282
@ <td>How debilitating is the problem? How badly does the problem
283283
@ affect the operation of the product?</td>
284284
@ </tr>
285285
@
286286
@ <tr>
287
-@ <td align="right">EMail:
288
-@ <input type="text" name="private_contact" value="$<private_contact>" size="30">
287
+@ <td style="text-align: center;">EMail:
288
+@ <input type="text" name="private_contact" value="$<private_contact>" size="30" />
289289
@ </td>
290
-@ <td><u>Not publicly visible</u>. Used by developers to contact you with
291
-@ questions.</td>
290
+@ <td><span style="text-decoration: underline;">Not publicly visible</span>.
291
+@ Used by developers to contact you with questions.</td>
292292
@ </tr>
293293
@
294294
@ <tr>
295295
@ <td colspan="2">
296296
@ Enter a detailed description of the problem.
297297
@ For code defects, be sure to provide details on exactly how
298298
@ the problem can be reproduced. Provide as much detail as
299299
@ possible.
300
-@ <br>
300
+@ <br />
301301
@ <th1>set nline [linecount $comment 50 10]</th1>
302302
@ <textarea name="comment" cols="80" rows="$nline"
303
-@ wrap="virtual" class="wikiedit">$<comment></textarea><br>
304
-@ <input type="submit" name="preview" value="Preview">
303
+@ wrap="virtual" class="wikiedit">$<comment></textarea><br />
304
+@ <input type="submit" name="preview" value="Preview" /></td>
305305
@ </tr>
306306
@
307307
@ <th1>enable_output [info exists preview]</th1>
308308
@ <tr><td colspan="2">
309
-@ Description Preview:<br><hr>
309
+@ Description Preview:<br /><hr />
310310
@ <th1>wiki $comment</th1>
311
-@ <hr>
311
+@ <hr />
312312
@ </td></tr>
313313
@ <th1>enable_output 1</th1>
314314
@
315315
@ <tr>
316
-@ <td align="right">
317
-@ <input type="submit" name="submit" value="Submit">
316
+@ <td style="text-align: center;">
317
+@ <input type="submit" name="submit" value="Submit" />
318318
@ </td>
319319
@ <td>After filling in the information above, press this button to create
320320
@ the new ticket</td>
321321
@ </tr>
322322
@ <tr>
323
-@ <td align="right">
324
-@ <input type="submit" name="cancel" value="Cancel">
323
+@ <td style="text-align: center;">
324
+@ <input type="submit" name="cancel" value="Cancel" />
325325
@ </td>
326326
@ <td>Abandon and forget this ticket</td>
327327
@ </tr>
328328
@ </table>
329329
;
@@ -338,12 +338,12 @@
338338
/*
339339
** WEBPAGE: tktsetup_newpage
340340
*/
341341
void tktsetup_newpage_page(void){
342342
static const char zDesc[] =
343
- @ <p>Enter HTML with embedded TH1 script that will render the "new ticket"
344
- @ page</p>
343
+ @ Enter HTML with embedded TH1 script that will render the "new ticket"
344
+ @ page
345345
;
346346
tktsetup_generic(
347347
"HTML For New Tickets",
348348
"ticket-newpage",
349349
zDefaultNew,
@@ -354,51 +354,50 @@
354354
);
355355
}
356356
357357
static const char zDefaultView[] =
358358
@ <table cellpadding="5">
359
-@ <tr><td align="right">Ticket&nbsp;UUID:</td><td bgcolor="#d0d0d0" colspan="3">
360
-@ $<tkt_uuid>
361
-@ </td></tr>
362
-@ <tr><td align="right">Title:</td>
363
-@ <td bgcolor="#d0d0d0" colspan="3" valign="top">
359
+@ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
360
+@ <td class="tktDspValue" colspan="3">$<tkt_uuid></td></tr>
361
+@ <tr><td class="tktDspLabel">Title:</td>
362
+@ <td class="tktDspValue" colspan="3">
364363
@ <th1>wiki $title</th1>
365364
@ </td></tr>
366
-@ <tr><td align="right">Status:</td><td bgcolor="#d0d0d0">
365
+@ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
367366
@ $<status>
368367
@ </td>
369
-@ <td align="right">Type:</td><td bgcolor="#d0d0d0">
368
+@ <td class="tktDspLabel">Type:</td><td class="tktDspValue">
370369
@ $<type>
371370
@ </td></tr>
372
-@ <tr><td align="right">Severity:</td><td bgcolor="#d0d0d0">
371
+@ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue">
373372
@ $<severity>
374373
@ </td>
375
-@ <td align="right">Priority:</td><td bgcolor="#d0d0d0">
374
+@ <td class="tktDspLabel">Priority:</td><td class="tktDspValue">
376375
@ $<priority>
377376
@ </td></tr>
378
-@ <tr><td align="right">Subsystem:</td><td bgcolor="#d0d0d0">
377
+@ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue">
379378
@ $<subsystem>
380379
@ </td>
381
-@ <td align="right">Resolution:</td><td bgcolor="#d0d0d0">
380
+@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
382381
@ $<resolution>
383382
@ </td></tr>
384
-@ <tr><td align="right">Last&nbsp;Modified:</td><td bgcolor="#d0d0d0">
383
+@ <tr><td class="tktDspLabel">Last&nbsp;Modified:</td><td class="tktDspValue">
385384
@ $<tkt_datetime>
386385
@ </td>
387386
@ <th1>enable_output [hascap e]</th1>
388
-@ <td align="right">Contact:</td><td bgcolor="#d0d0d0">
387
+@ <td class="tktDspLabel">Contact:</td><td class="tktDspValue">
389388
@ $<private_contact>
390389
@ </td>
391390
@ <th1>enable_output 1</th1>
392391
@ </tr>
393
-@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td>
394
-@ <td colspan="3" valign="top" bgcolor="#d0d0d0">
392
+@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
393
+@ <td colspan="3" valign="top" class="tktDspValue">
395394
@ $<foundin>
396395
@ </td></tr>
397396
@ <tr><td>Description &amp; Comments:</td></tr>
398
-@ <tr><td colspan="4" bgcolor="#d0d0d0">
399
-@ <span bgcolor="#d0d0d0"><th1>wiki $comment</th1></span>
397
+@ <tr><td colspan="4" class="tktDspValue">
398
+@ <th1>wiki $comment</th1>
400399
@ </td></tr>
401400
@ </table>
402401
;
403402
404403
@@ -412,12 +411,11 @@
412411
/*
413412
** WEBPAGE: tktsetup_viewpage
414413
*/
415414
void tktsetup_viewpage_page(void){
416415
static const char zDesc[] =
417
- @ <p>Enter HTML with embedded TH1 script that will render the "view ticket"
418
- @ page</p>
416
+ @ Enter HTML with embedded TH1 script that will render the "view ticket" page
419417
;
420418
tktsetup_generic(
421419
"HTML For Viewing Tickets",
422420
"ticket-viewpage",
423421
zDefaultView,
@@ -432,51 +430,51 @@
432430
@ <th1>
433431
@ if {![info exists username]} {set username $login}
434432
@ if {[info exists submit]} {
435433
@ if {[info exists cmappnd]} {
436434
@ if {[string length $cmappnd]>0} {
437
-@ set ctxt "\n\n<hr><i>[htmlize $login]"
435
+@ set ctxt "\n\n<hr /><i>[htmlize $login]"
438436
@ if {$username ne $login} {
439437
@ set ctxt "$ctxt claiming to be [htmlize $username]"
440438
@ }
441
-@ set ctxt "$ctxt added on [date]:</i><br>\n$cmappnd"
439
+@ set ctxt "$ctxt added on [date]:</i><br />\n$cmappnd"
442440
@ append_field comment $ctxt
443441
@ }
444442
@ }
445443
@ submit_ticket
446444
@ }
447445
@ </th1>
448446
@ <table cellpadding="5">
449
-@ <tr><td align="right">Title:</td><td>
450
-@ <input type="text" name="title" value="$<title>" size="60">
447
+@ <tr><td class="tktDspLabel">Title:</td><td>
448
+@ <input type="text" name="title" value="$<title>" size="60" />
451449
@ </td></tr>
452
-@ <tr><td align="right">Status:</td><td>
450
+@ <tr><td class="tktDspLabel">Status:</td><td>
453451
@ <th1>combobox status $status_choices 1</th1>
454452
@ </td></tr>
455
-@ <tr><td align="right">Type:</td><td>
453
+@ <tr><td class="tktDspLabel">Type:</td><td>
456454
@ <th1>combobox type $type_choices 1</th1>
457455
@ </td></tr>
458
-@ <tr><td align="right">Severity:</td><td>
456
+@ <tr><td class="tktDspLabel">Severity:</td><td>
459457
@ <th1>combobox severity $severity_choices 1</th1>
460458
@ </td></tr>
461
-@ <tr><td align="right">Priority:</td><td>
459
+@ <tr><td class="tktDspLabel">Priority:</td><td>
462460
@ <th1>combobox priority $priority_choices 1</th1>
463461
@ </td></tr>
464
-@ <tr><td align="right">Resolution:</td><td>
462
+@ <tr><td class="tktDspLabel">Resolution:</td><td>
465463
@ <th1>combobox resolution $resolution_choices 1</th1>
466464
@ </td></tr>
467
-@ <tr><td align="right">Subsystem:</td><td>
465
+@ <tr><td class="tktDspLabel">Subsystem:</td><td>
468466
@ <th1>combobox subsystem $subsystem_choices 1</th1>
469467
@ </td></tr>
470468
@ <th1>enable_output [hascap e]</th1>
471
-@ <tr><td align="right">Contact:</td><td>
469
+@ <tr><td class="tktDspLabel">Contact:</td><td>
472470
@ <input type="text" name="private_contact" size="40"
473
-@ value="$<private_contact>">
471
+@ value="$<private_contact>" />
474472
@ </td></tr>
475473
@ <th1>enable_output 1</th1>
476
-@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
477
-@ <input type="text" name="foundin" size="50" value="$<foundin>">
474
+@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
475
+@ <input type="text" name="foundin" size="50" value="$<foundin>" />
478476
@ </td></tr>
479477
@ <tr><td colspan="2">
480478
@ <th1>
481479
@ if {![info exists eall]} {set eall 0}
482480
@ if {[info exists aonlybtn]} {set eall 0}
@@ -484,45 +482,45 @@
484482
@ if {![hascap w]} {set eall 0}
485483
@ if {![info exists cmappnd]} {set cmappnd {}}
486484
@ set nline [linecount $comment 15 10]
487485
@ enable_output $eall
488486
@ </th1>
489
-@ Description And Comments:<br>
487
+@ Description And Comments:<br />
490488
@ <textarea name="comment" cols="80" rows="$nline"
491
-@ wrap="virtual" class="wikiedit">$<comment></textarea><br>
492
-@ <input type="hidden" name="eall" value="1">
493
-@ <input type="submit" name="aonlybtn" value="Append Remark">
494
-@ <input type="submit" name="preview1btn" value="Preview">
489
+@ wrap="virtual" class="wikiedit">$<comment></textarea><br />
490
+@ <input type="hidden" name="eall" value="1" />
491
+@ <input type="submit" name="aonlybtn" value="Append Remark" />
492
+@ <input type="submit" name="preview1btn" value="Preview" />
495493
@ <th1>enable_output [expr {!$eall}]</th1>
496494
@ Append Remark from
497
-@ <input type="text" name="username" value="$<username>" size="30">:<br>
495
+@ <input type="text" name="username" value="$<username>" size="30" />:<br />
498496
@ <textarea name="cmappnd" cols="80" rows="15"
499
-@ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br>
497
+@ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br />
500498
@ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
501
-@ <input type="submit" name="eallbtn" value="Edit All">
499
+@ <input type="submit" name="eallbtn" value="Edit All" />
502500
@ <th1>enable_output [expr {!$eall}]</th1>
503
-@ <input type="submit" name="preview2btn" value="Preview">
501
+@ <input type="submit" name="preview2btn" value="Preview" />
504502
@ <th1>enable_output 1</th1>
505503
@ </td></tr>
506504
@
507505
@ <th1>enable_output [info exists preview1btn]</th1>
508506
@ <tr><td colspan="2">
509
-@ Description Preview:<br><hr>
507
+@ Description Preview:<br /><hr />
510508
@ <th1>wiki $comment</th1>
511
-@ <hr>
509
+@ <hr />
512510
@ </td></tr>
513511
@ <th1>enable_output [info exists preview2btn]</th1>
514512
@ <tr><td colspan="2">
515
-@ Description Preview:<br><hr>
513
+@ Description Preview:<br /><hr />
516514
@ <th1>wiki $cmappnd</th1>
517
-@ <hr>
515
+@ <hr />
518516
@ </td></tr>
519517
@ <th1>enable_output 1</th1>
520518
@
521519
@ <tr><td align="right"></td><td>
522
-@ <input type="submit" name="submit" value="Submit Changes">
523
-@ <input type="submit" name="cancel" value="Cancel">
520
+@ <input type="submit" name="submit" value="Submit Changes" />
521
+@ <input type="submit" name="cancel" value="Cancel" />
524522
@ </td></tr>
525523
@ </table>
526524
;
527525
528526
/*
@@ -535,12 +533,11 @@
535533
/*
536534
** WEBPAGE: tktsetup_editpage
537535
*/
538536
void tktsetup_editpage_page(void){
539537
static const char zDesc[] =
540
- @ <p>Enter HTML with embedded TH1 script that will render the "edit ticket"
541
- @ page</p>
538
+ @ Enter HTML with embedded TH1 script that will render the "edit ticket" page
542539
;
543540
tktsetup_generic(
544541
"HTML For Editing Tickets",
545542
"ticket-editpage",
546543
zDefaultEdit,
@@ -585,12 +582,11 @@
585582
/*
586583
** WEBPAGE: tktsetup_reportlist
587584
*/
588585
void tktsetup_reportlist(void){
589586
static const char zDesc[] =
590
- @ <p>Enter HTML with embedded TH1 script that will render the "report list"
591
- @ page</p>
587
+ @ Enter HTML with embedded TH1 script that will render the "report list" page
592588
;
593589
tktsetup_generic(
594590
"HTML For Report List",
595591
"ticket-reportlist",
596592
zDefaultReportList,
@@ -633,13 +629,13 @@
633629
/*
634630
** WEBPAGE: tktsetup_rpttplt
635631
*/
636632
void tktsetup_rpttplt_page(void){
637633
static const char zDesc[] =
638
- @ <p>Enter the default ticket report format template. This is the
634
+ @ Enter the default ticket report format template. This is the
639635
@ the template report format that initially appears when creating a
640
- @ new ticket summary report.</p>
636
+ @ new ticket summary report.
641637
;
642638
tktsetup_generic(
643639
"Default Report Template",
644640
"ticket-report-template",
645641
zDefaultReport,
@@ -674,13 +670,13 @@
674670
/*
675671
** WEBPAGE: tktsetup_keytplt
676672
*/
677673
void tktsetup_keytplt_page(void){
678674
static const char zDesc[] =
679
- @ <p>Enter the default ticket report color-key template. This is the
675
+ @ Enter the default ticket report color-key template. This is the
680676
@ the color-key that initially appears when creating a
681
- @ new ticket summary report.</p>
677
+ @ new ticket summary report.
682678
;
683679
tktsetup_generic(
684680
"Default Report Color-Key Template",
685681
"ticket-key-template",
686682
zDefaultKey,
@@ -703,34 +699,34 @@
703699
if( P("setup") ){
704700
cgi_redirect("tktsetup");
705701
}
706702
style_header("Ticket Display On Timelines");
707703
db_begin_transaction();
708
- @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
704
+ @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="post"><div>
709705
login_insert_csrf_secret();
710706
711
- @ <hr>
707
+ @ <hr />
712708
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
713709
@ <p>An SQL expression in a query against the TICKET table that will
714710
@ return the title of the ticket for display purposes.</p>
715711
716
- @ <hr>
712
+ @ <hr />
717713
entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
718714
@ <p>The name of the column in the TICKET table that contains the ticket
719715
@ status in human-readable form. Case sensitive.</p>
720716
721
- @ <hr>
717
+ @ <hr />
722718
entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
723719
"status='Closed'");
724720
@ <p>An SQL expression that evaluates to true in a TICKET table query if
725721
@ the ticket is closed.</p>
726722
727
- @ <hr>
723
+ @ <hr />
728724
@ <p>
729
- @ <input type="submit" name="submit" value="Apply Changes">
730
- @ <input type="submit" name="setup" value="Cancel">
725
+ @ <input type="submit" name="submit" value="Apply Changes" />
726
+ @ <input type="submit" name="setup" value="Cancel" />
731727
@ </p>
732
- @ </form>
728
+ @ </div></form>
733729
db_end_transaction(0);
734730
style_footer();
735731
736732
}
737733
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -130,21 +130,21 @@
130 db_set(zDbField, z, 0);
131 if( xRebuild ) xRebuild();
132 cgi_redirect("tktsetup");
133 }
134 }
135 @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
136 login_insert_csrf_secret();
137 @ <p>%s(zDesc)</p>
138 @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
139 @ <blockquote>
140 @ <input type="submit" name="submit" value="Apply Changes">
141 @ <input type="submit" name="clear" value="Revert To Default">
142 @ <input type="submit" name="setup" value="Cancel">
143 @ </blockquote>
144 @ </form>
145 @ <hr>
146 @ <h2>Default %s(zTitle)</h2>
147 @ <blockquote><pre>
148 @ %h(zDfltValue)
149 @ </pre></blockquote>
150 style_footer();
@@ -153,13 +153,13 @@
153 /*
154 ** WEBPAGE: tktsetup_tab
155 */
156 void tktsetup_tab_page(void){
157 static const char zDesc[] =
158 @ <p>Enter a valid CREATE TABLE statement for the "ticket" table. The
159 @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
160 @ with an unique index on "tkt_uuid" and "tkt_mtime".</p>
161 ;
162 tktsetup_generic(
163 "Ticket Table Schema",
164 "ticket-table",
165 zDefaultTicketTable,
@@ -229,12 +229,12 @@
229 /*
230 ** WEBPAGE: tktsetup_com
231 */
232 void tktsetup_com_page(void){
233 static const char zDesc[] =
234 @ <p>Enter TH1 script that initializes variables prior to generating
235 @ any of the ticket view, edit, or creation pages.</p>
236 ;
237 tktsetup_generic(
238 "Ticket Common Script",
239 "ticket-common",
240 zDefaultTicketCommon,
@@ -250,80 +250,80 @@
250 @ if {[info exists submit]} {
251 @ set status Open
252 @ submit_ticket
253 @ }
254 @ </th1>
255 @ <h1 align="center">Enter A New Ticket</h1>
256 @ <table cellpadding="5">
257 @ <tr>
258 @ <td colspan="2">
259 @ Enter a one-line summary of the ticket:<br>
260 @ <input type="text" name="title" size="60" value="$<title>">
261 @ </td>
262 @ </tr>
263 @
264 @ <tr>
265 @ <td align="right">Type:
266 @ <th1>combobox type $type_choices 1</th1>
267 @ </td>
268 @ <td>What type of ticket is this?</td>
269 @ </tr>
270 @
271 @ <tr>
272 @ <td align="right">Version:
273 @ <input type="text" name="foundin" size="20" value="$<foundin>">
274 @ </td>
275 @ <td>In what version or build number do you observe the problem?</td>
276 @ </tr>
277 @
278 @ <tr>
279 @ <td align="right">Severity:
280 @ <th1>combobox severity $severity_choices 1</th1>
281 @ </td>
282 @ <td>How debilitating is the problem? How badly does the problem
283 @ affect the operation of the product?</td>
284 @ </tr>
285 @
286 @ <tr>
287 @ <td align="right">EMail:
288 @ <input type="text" name="private_contact" value="$<private_contact>" size="30">
289 @ </td>
290 @ <td><u>Not publicly visible</u>. Used by developers to contact you with
291 @ questions.</td>
292 @ </tr>
293 @
294 @ <tr>
295 @ <td colspan="2">
296 @ Enter a detailed description of the problem.
297 @ For code defects, be sure to provide details on exactly how
298 @ the problem can be reproduced. Provide as much detail as
299 @ possible.
300 @ <br>
301 @ <th1>set nline [linecount $comment 50 10]</th1>
302 @ <textarea name="comment" cols="80" rows="$nline"
303 @ wrap="virtual" class="wikiedit">$<comment></textarea><br>
304 @ <input type="submit" name="preview" value="Preview">
305 @ </tr>
306 @
307 @ <th1>enable_output [info exists preview]</th1>
308 @ <tr><td colspan="2">
309 @ Description Preview:<br><hr>
310 @ <th1>wiki $comment</th1>
311 @ <hr>
312 @ </td></tr>
313 @ <th1>enable_output 1</th1>
314 @
315 @ <tr>
316 @ <td align="right">
317 @ <input type="submit" name="submit" value="Submit">
318 @ </td>
319 @ <td>After filling in the information above, press this button to create
320 @ the new ticket</td>
321 @ </tr>
322 @ <tr>
323 @ <td align="right">
324 @ <input type="submit" name="cancel" value="Cancel">
325 @ </td>
326 @ <td>Abandon and forget this ticket</td>
327 @ </tr>
328 @ </table>
329 ;
@@ -338,12 +338,12 @@
338 /*
339 ** WEBPAGE: tktsetup_newpage
340 */
341 void tktsetup_newpage_page(void){
342 static const char zDesc[] =
343 @ <p>Enter HTML with embedded TH1 script that will render the "new ticket"
344 @ page</p>
345 ;
346 tktsetup_generic(
347 "HTML For New Tickets",
348 "ticket-newpage",
349 zDefaultNew,
@@ -354,51 +354,50 @@
354 );
355 }
356
357 static const char zDefaultView[] =
358 @ <table cellpadding="5">
359 @ <tr><td align="right">Ticket&nbsp;UUID:</td><td bgcolor="#d0d0d0" colspan="3">
360 @ $<tkt_uuid>
361 @ </td></tr>
362 @ <tr><td align="right">Title:</td>
363 @ <td bgcolor="#d0d0d0" colspan="3" valign="top">
364 @ <th1>wiki $title</th1>
365 @ </td></tr>
366 @ <tr><td align="right">Status:</td><td bgcolor="#d0d0d0">
367 @ $<status>
368 @ </td>
369 @ <td align="right">Type:</td><td bgcolor="#d0d0d0">
370 @ $<type>
371 @ </td></tr>
372 @ <tr><td align="right">Severity:</td><td bgcolor="#d0d0d0">
373 @ $<severity>
374 @ </td>
375 @ <td align="right">Priority:</td><td bgcolor="#d0d0d0">
376 @ $<priority>
377 @ </td></tr>
378 @ <tr><td align="right">Subsystem:</td><td bgcolor="#d0d0d0">
379 @ $<subsystem>
380 @ </td>
381 @ <td align="right">Resolution:</td><td bgcolor="#d0d0d0">
382 @ $<resolution>
383 @ </td></tr>
384 @ <tr><td align="right">Last&nbsp;Modified:</td><td bgcolor="#d0d0d0">
385 @ $<tkt_datetime>
386 @ </td>
387 @ <th1>enable_output [hascap e]</th1>
388 @ <td align="right">Contact:</td><td bgcolor="#d0d0d0">
389 @ $<private_contact>
390 @ </td>
391 @ <th1>enable_output 1</th1>
392 @ </tr>
393 @ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td>
394 @ <td colspan="3" valign="top" bgcolor="#d0d0d0">
395 @ $<foundin>
396 @ </td></tr>
397 @ <tr><td>Description &amp; Comments:</td></tr>
398 @ <tr><td colspan="4" bgcolor="#d0d0d0">
399 @ <span bgcolor="#d0d0d0"><th1>wiki $comment</th1></span>
400 @ </td></tr>
401 @ </table>
402 ;
403
404
@@ -412,12 +411,11 @@
412 /*
413 ** WEBPAGE: tktsetup_viewpage
414 */
415 void tktsetup_viewpage_page(void){
416 static const char zDesc[] =
417 @ <p>Enter HTML with embedded TH1 script that will render the "view ticket"
418 @ page</p>
419 ;
420 tktsetup_generic(
421 "HTML For Viewing Tickets",
422 "ticket-viewpage",
423 zDefaultView,
@@ -432,51 +430,51 @@
432 @ <th1>
433 @ if {![info exists username]} {set username $login}
434 @ if {[info exists submit]} {
435 @ if {[info exists cmappnd]} {
436 @ if {[string length $cmappnd]>0} {
437 @ set ctxt "\n\n<hr><i>[htmlize $login]"
438 @ if {$username ne $login} {
439 @ set ctxt "$ctxt claiming to be [htmlize $username]"
440 @ }
441 @ set ctxt "$ctxt added on [date]:</i><br>\n$cmappnd"
442 @ append_field comment $ctxt
443 @ }
444 @ }
445 @ submit_ticket
446 @ }
447 @ </th1>
448 @ <table cellpadding="5">
449 @ <tr><td align="right">Title:</td><td>
450 @ <input type="text" name="title" value="$<title>" size="60">
451 @ </td></tr>
452 @ <tr><td align="right">Status:</td><td>
453 @ <th1>combobox status $status_choices 1</th1>
454 @ </td></tr>
455 @ <tr><td align="right">Type:</td><td>
456 @ <th1>combobox type $type_choices 1</th1>
457 @ </td></tr>
458 @ <tr><td align="right">Severity:</td><td>
459 @ <th1>combobox severity $severity_choices 1</th1>
460 @ </td></tr>
461 @ <tr><td align="right">Priority:</td><td>
462 @ <th1>combobox priority $priority_choices 1</th1>
463 @ </td></tr>
464 @ <tr><td align="right">Resolution:</td><td>
465 @ <th1>combobox resolution $resolution_choices 1</th1>
466 @ </td></tr>
467 @ <tr><td align="right">Subsystem:</td><td>
468 @ <th1>combobox subsystem $subsystem_choices 1</th1>
469 @ </td></tr>
470 @ <th1>enable_output [hascap e]</th1>
471 @ <tr><td align="right">Contact:</td><td>
472 @ <input type="text" name="private_contact" size="40"
473 @ value="$<private_contact>">
474 @ </td></tr>
475 @ <th1>enable_output 1</th1>
476 @ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
477 @ <input type="text" name="foundin" size="50" value="$<foundin>">
478 @ </td></tr>
479 @ <tr><td colspan="2">
480 @ <th1>
481 @ if {![info exists eall]} {set eall 0}
482 @ if {[info exists aonlybtn]} {set eall 0}
@@ -484,45 +482,45 @@
484 @ if {![hascap w]} {set eall 0}
485 @ if {![info exists cmappnd]} {set cmappnd {}}
486 @ set nline [linecount $comment 15 10]
487 @ enable_output $eall
488 @ </th1>
489 @ Description And Comments:<br>
490 @ <textarea name="comment" cols="80" rows="$nline"
491 @ wrap="virtual" class="wikiedit">$<comment></textarea><br>
492 @ <input type="hidden" name="eall" value="1">
493 @ <input type="submit" name="aonlybtn" value="Append Remark">
494 @ <input type="submit" name="preview1btn" value="Preview">
495 @ <th1>enable_output [expr {!$eall}]</th1>
496 @ Append Remark from
497 @ <input type="text" name="username" value="$<username>" size="30">:<br>
498 @ <textarea name="cmappnd" cols="80" rows="15"
499 @ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br>
500 @ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
501 @ <input type="submit" name="eallbtn" value="Edit All">
502 @ <th1>enable_output [expr {!$eall}]</th1>
503 @ <input type="submit" name="preview2btn" value="Preview">
504 @ <th1>enable_output 1</th1>
505 @ </td></tr>
506 @
507 @ <th1>enable_output [info exists preview1btn]</th1>
508 @ <tr><td colspan="2">
509 @ Description Preview:<br><hr>
510 @ <th1>wiki $comment</th1>
511 @ <hr>
512 @ </td></tr>
513 @ <th1>enable_output [info exists preview2btn]</th1>
514 @ <tr><td colspan="2">
515 @ Description Preview:<br><hr>
516 @ <th1>wiki $cmappnd</th1>
517 @ <hr>
518 @ </td></tr>
519 @ <th1>enable_output 1</th1>
520 @
521 @ <tr><td align="right"></td><td>
522 @ <input type="submit" name="submit" value="Submit Changes">
523 @ <input type="submit" name="cancel" value="Cancel">
524 @ </td></tr>
525 @ </table>
526 ;
527
528 /*
@@ -535,12 +533,11 @@
535 /*
536 ** WEBPAGE: tktsetup_editpage
537 */
538 void tktsetup_editpage_page(void){
539 static const char zDesc[] =
540 @ <p>Enter HTML with embedded TH1 script that will render the "edit ticket"
541 @ page</p>
542 ;
543 tktsetup_generic(
544 "HTML For Editing Tickets",
545 "ticket-editpage",
546 zDefaultEdit,
@@ -585,12 +582,11 @@
585 /*
586 ** WEBPAGE: tktsetup_reportlist
587 */
588 void tktsetup_reportlist(void){
589 static const char zDesc[] =
590 @ <p>Enter HTML with embedded TH1 script that will render the "report list"
591 @ page</p>
592 ;
593 tktsetup_generic(
594 "HTML For Report List",
595 "ticket-reportlist",
596 zDefaultReportList,
@@ -633,13 +629,13 @@
633 /*
634 ** WEBPAGE: tktsetup_rpttplt
635 */
636 void tktsetup_rpttplt_page(void){
637 static const char zDesc[] =
638 @ <p>Enter the default ticket report format template. This is the
639 @ the template report format that initially appears when creating a
640 @ new ticket summary report.</p>
641 ;
642 tktsetup_generic(
643 "Default Report Template",
644 "ticket-report-template",
645 zDefaultReport,
@@ -674,13 +670,13 @@
674 /*
675 ** WEBPAGE: tktsetup_keytplt
676 */
677 void tktsetup_keytplt_page(void){
678 static const char zDesc[] =
679 @ <p>Enter the default ticket report color-key template. This is the
680 @ the color-key that initially appears when creating a
681 @ new ticket summary report.</p>
682 ;
683 tktsetup_generic(
684 "Default Report Color-Key Template",
685 "ticket-key-template",
686 zDefaultKey,
@@ -703,34 +699,34 @@
703 if( P("setup") ){
704 cgi_redirect("tktsetup");
705 }
706 style_header("Ticket Display On Timelines");
707 db_begin_transaction();
708 @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
709 login_insert_csrf_secret();
710
711 @ <hr>
712 entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
713 @ <p>An SQL expression in a query against the TICKET table that will
714 @ return the title of the ticket for display purposes.</p>
715
716 @ <hr>
717 entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
718 @ <p>The name of the column in the TICKET table that contains the ticket
719 @ status in human-readable form. Case sensitive.</p>
720
721 @ <hr>
722 entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
723 "status='Closed'");
724 @ <p>An SQL expression that evaluates to true in a TICKET table query if
725 @ the ticket is closed.</p>
726
727 @ <hr>
728 @ <p>
729 @ <input type="submit" name="submit" value="Apply Changes">
730 @ <input type="submit" name="setup" value="Cancel">
731 @ </p>
732 @ </form>
733 db_end_transaction(0);
734 style_footer();
735
736 }
737
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -130,21 +130,21 @@
130 db_set(zDbField, z, 0);
131 if( xRebuild ) xRebuild();
132 cgi_redirect("tktsetup");
133 }
134 }
135 @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>
136 login_insert_csrf_secret();
137 @ <p>%s(zDesc)</p>
138 @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
139 @ <blockquote><p>
140 @ <input type="submit" name="submit" value="Apply Changes" />
141 @ <input type="submit" name="clear" value="Revert To Default" />
142 @ <input type="submit" name="setup" value="Cancel" />
143 @ </p></blockquote>
144 @ </div></form>
145 @ <hr />
146 @ <h2>Default %s(zTitle)</h2>
147 @ <blockquote><pre>
148 @ %h(zDfltValue)
149 @ </pre></blockquote>
150 style_footer();
@@ -153,13 +153,13 @@
153 /*
154 ** WEBPAGE: tktsetup_tab
155 */
156 void tktsetup_tab_page(void){
157 static const char zDesc[] =
158 @ Enter a valid CREATE TABLE statement for the "ticket" table. The
159 @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
160 @ with an unique index on "tkt_uuid" and "tkt_mtime".
161 ;
162 tktsetup_generic(
163 "Ticket Table Schema",
164 "ticket-table",
165 zDefaultTicketTable,
@@ -229,12 +229,12 @@
229 /*
230 ** WEBPAGE: tktsetup_com
231 */
232 void tktsetup_com_page(void){
233 static const char zDesc[] =
234 @ Enter TH1 script that initializes variables prior to generating
235 @ any of the ticket view, edit, or creation pages.
236 ;
237 tktsetup_generic(
238 "Ticket Common Script",
239 "ticket-common",
240 zDefaultTicketCommon,
@@ -250,80 +250,80 @@
250 @ if {[info exists submit]} {
251 @ set status Open
252 @ submit_ticket
253 @ }
254 @ </th1>
255 @ <h1 style="text-align: center;">Enter A New Ticket</h1>
256 @ <table cellpadding="5">
257 @ <tr>
258 @ <td colspan="2">
259 @ Enter a one-line summary of the ticket:<br />
260 @ <input type="text" name="title" size="60" value="$<title>" />
261 @ </td>
262 @ </tr>
263 @
264 @ <tr>
265 @ <td style="text-align: center;">Type:
266 @ <th1>combobox type $type_choices 1</th1>
267 @ </td>
268 @ <td>What type of ticket is this?</td>
269 @ </tr>
270 @
271 @ <tr>
272 @ <td style="text-align: center;">Version:
273 @ <input type="text" name="foundin" size="20" value="$<foundin>" />
274 @ </td>
275 @ <td>In what version or build number do you observe the problem?</td>
276 @ </tr>
277 @
278 @ <tr>
279 @ <td style="text-align: center;">Severity:
280 @ <th1>combobox severity $severity_choices 1</th1>
281 @ </td>
282 @ <td>How debilitating is the problem? How badly does the problem
283 @ affect the operation of the product?</td>
284 @ </tr>
285 @
286 @ <tr>
287 @ <td style="text-align: center;">EMail:
288 @ <input type="text" name="private_contact" value="$<private_contact>" size="30" />
289 @ </td>
290 @ <td><span style="text-decoration: underline;">Not publicly visible</span>.
291 @ Used by developers to contact you with questions.</td>
292 @ </tr>
293 @
294 @ <tr>
295 @ <td colspan="2">
296 @ Enter a detailed description of the problem.
297 @ For code defects, be sure to provide details on exactly how
298 @ the problem can be reproduced. Provide as much detail as
299 @ possible.
300 @ <br />
301 @ <th1>set nline [linecount $comment 50 10]</th1>
302 @ <textarea name="comment" cols="80" rows="$nline"
303 @ wrap="virtual" class="wikiedit">$<comment></textarea><br />
304 @ <input type="submit" name="preview" value="Preview" /></td>
305 @ </tr>
306 @
307 @ <th1>enable_output [info exists preview]</th1>
308 @ <tr><td colspan="2">
309 @ Description Preview:<br /><hr />
310 @ <th1>wiki $comment</th1>
311 @ <hr />
312 @ </td></tr>
313 @ <th1>enable_output 1</th1>
314 @
315 @ <tr>
316 @ <td style="text-align: center;">
317 @ <input type="submit" name="submit" value="Submit" />
318 @ </td>
319 @ <td>After filling in the information above, press this button to create
320 @ the new ticket</td>
321 @ </tr>
322 @ <tr>
323 @ <td style="text-align: center;">
324 @ <input type="submit" name="cancel" value="Cancel" />
325 @ </td>
326 @ <td>Abandon and forget this ticket</td>
327 @ </tr>
328 @ </table>
329 ;
@@ -338,12 +338,12 @@
338 /*
339 ** WEBPAGE: tktsetup_newpage
340 */
341 void tktsetup_newpage_page(void){
342 static const char zDesc[] =
343 @ Enter HTML with embedded TH1 script that will render the "new ticket"
344 @ page
345 ;
346 tktsetup_generic(
347 "HTML For New Tickets",
348 "ticket-newpage",
349 zDefaultNew,
@@ -354,51 +354,50 @@
354 );
355 }
356
357 static const char zDefaultView[] =
358 @ <table cellpadding="5">
359 @ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
360 @ <td class="tktDspValue" colspan="3">$<tkt_uuid></td></tr>
361 @ <tr><td class="tktDspLabel">Title:</td>
362 @ <td class="tktDspValue" colspan="3">
 
363 @ <th1>wiki $title</th1>
364 @ </td></tr>
365 @ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
366 @ $<status>
367 @ </td>
368 @ <td class="tktDspLabel">Type:</td><td class="tktDspValue">
369 @ $<type>
370 @ </td></tr>
371 @ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue">
372 @ $<severity>
373 @ </td>
374 @ <td class="tktDspLabel">Priority:</td><td class="tktDspValue">
375 @ $<priority>
376 @ </td></tr>
377 @ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue">
378 @ $<subsystem>
379 @ </td>
380 @ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
381 @ $<resolution>
382 @ </td></tr>
383 @ <tr><td class="tktDspLabel">Last&nbsp;Modified:</td><td class="tktDspValue">
384 @ $<tkt_datetime>
385 @ </td>
386 @ <th1>enable_output [hascap e]</th1>
387 @ <td class="tktDspLabel">Contact:</td><td class="tktDspValue">
388 @ $<private_contact>
389 @ </td>
390 @ <th1>enable_output 1</th1>
391 @ </tr>
392 @ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
393 @ <td colspan="3" valign="top" class="tktDspValue">
394 @ $<foundin>
395 @ </td></tr>
396 @ <tr><td>Description &amp; Comments:</td></tr>
397 @ <tr><td colspan="4" class="tktDspValue">
398 @ <th1>wiki $comment</th1>
399 @ </td></tr>
400 @ </table>
401 ;
402
403
@@ -412,12 +411,11 @@
411 /*
412 ** WEBPAGE: tktsetup_viewpage
413 */
414 void tktsetup_viewpage_page(void){
415 static const char zDesc[] =
416 @ Enter HTML with embedded TH1 script that will render the "view ticket" page
 
417 ;
418 tktsetup_generic(
419 "HTML For Viewing Tickets",
420 "ticket-viewpage",
421 zDefaultView,
@@ -432,51 +430,51 @@
430 @ <th1>
431 @ if {![info exists username]} {set username $login}
432 @ if {[info exists submit]} {
433 @ if {[info exists cmappnd]} {
434 @ if {[string length $cmappnd]>0} {
435 @ set ctxt "\n\n<hr /><i>[htmlize $login]"
436 @ if {$username ne $login} {
437 @ set ctxt "$ctxt claiming to be [htmlize $username]"
438 @ }
439 @ set ctxt "$ctxt added on [date]:</i><br />\n$cmappnd"
440 @ append_field comment $ctxt
441 @ }
442 @ }
443 @ submit_ticket
444 @ }
445 @ </th1>
446 @ <table cellpadding="5">
447 @ <tr><td class="tktDspLabel">Title:</td><td>
448 @ <input type="text" name="title" value="$<title>" size="60" />
449 @ </td></tr>
450 @ <tr><td class="tktDspLabel">Status:</td><td>
451 @ <th1>combobox status $status_choices 1</th1>
452 @ </td></tr>
453 @ <tr><td class="tktDspLabel">Type:</td><td>
454 @ <th1>combobox type $type_choices 1</th1>
455 @ </td></tr>
456 @ <tr><td class="tktDspLabel">Severity:</td><td>
457 @ <th1>combobox severity $severity_choices 1</th1>
458 @ </td></tr>
459 @ <tr><td class="tktDspLabel">Priority:</td><td>
460 @ <th1>combobox priority $priority_choices 1</th1>
461 @ </td></tr>
462 @ <tr><td class="tktDspLabel">Resolution:</td><td>
463 @ <th1>combobox resolution $resolution_choices 1</th1>
464 @ </td></tr>
465 @ <tr><td class="tktDspLabel">Subsystem:</td><td>
466 @ <th1>combobox subsystem $subsystem_choices 1</th1>
467 @ </td></tr>
468 @ <th1>enable_output [hascap e]</th1>
469 @ <tr><td class="tktDspLabel">Contact:</td><td>
470 @ <input type="text" name="private_contact" size="40"
471 @ value="$<private_contact>" />
472 @ </td></tr>
473 @ <th1>enable_output 1</th1>
474 @ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
475 @ <input type="text" name="foundin" size="50" value="$<foundin>" />
476 @ </td></tr>
477 @ <tr><td colspan="2">
478 @ <th1>
479 @ if {![info exists eall]} {set eall 0}
480 @ if {[info exists aonlybtn]} {set eall 0}
@@ -484,45 +482,45 @@
482 @ if {![hascap w]} {set eall 0}
483 @ if {![info exists cmappnd]} {set cmappnd {}}
484 @ set nline [linecount $comment 15 10]
485 @ enable_output $eall
486 @ </th1>
487 @ Description And Comments:<br />
488 @ <textarea name="comment" cols="80" rows="$nline"
489 @ wrap="virtual" class="wikiedit">$<comment></textarea><br />
490 @ <input type="hidden" name="eall" value="1" />
491 @ <input type="submit" name="aonlybtn" value="Append Remark" />
492 @ <input type="submit" name="preview1btn" value="Preview" />
493 @ <th1>enable_output [expr {!$eall}]</th1>
494 @ Append Remark from
495 @ <input type="text" name="username" value="$<username>" size="30" />:<br />
496 @ <textarea name="cmappnd" cols="80" rows="15"
497 @ wrap="virtual" class="wikiedit">$<cmappnd></textarea><br />
498 @ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
499 @ <input type="submit" name="eallbtn" value="Edit All" />
500 @ <th1>enable_output [expr {!$eall}]</th1>
501 @ <input type="submit" name="preview2btn" value="Preview" />
502 @ <th1>enable_output 1</th1>
503 @ </td></tr>
504 @
505 @ <th1>enable_output [info exists preview1btn]</th1>
506 @ <tr><td colspan="2">
507 @ Description Preview:<br /><hr />
508 @ <th1>wiki $comment</th1>
509 @ <hr />
510 @ </td></tr>
511 @ <th1>enable_output [info exists preview2btn]</th1>
512 @ <tr><td colspan="2">
513 @ Description Preview:<br /><hr />
514 @ <th1>wiki $cmappnd</th1>
515 @ <hr />
516 @ </td></tr>
517 @ <th1>enable_output 1</th1>
518 @
519 @ <tr><td align="right"></td><td>
520 @ <input type="submit" name="submit" value="Submit Changes" />
521 @ <input type="submit" name="cancel" value="Cancel" />
522 @ </td></tr>
523 @ </table>
524 ;
525
526 /*
@@ -535,12 +533,11 @@
533 /*
534 ** WEBPAGE: tktsetup_editpage
535 */
536 void tktsetup_editpage_page(void){
537 static const char zDesc[] =
538 @ Enter HTML with embedded TH1 script that will render the "edit ticket" page
 
539 ;
540 tktsetup_generic(
541 "HTML For Editing Tickets",
542 "ticket-editpage",
543 zDefaultEdit,
@@ -585,12 +582,11 @@
582 /*
583 ** WEBPAGE: tktsetup_reportlist
584 */
585 void tktsetup_reportlist(void){
586 static const char zDesc[] =
587 @ Enter HTML with embedded TH1 script that will render the "report list" page
 
588 ;
589 tktsetup_generic(
590 "HTML For Report List",
591 "ticket-reportlist",
592 zDefaultReportList,
@@ -633,13 +629,13 @@
629 /*
630 ** WEBPAGE: tktsetup_rpttplt
631 */
632 void tktsetup_rpttplt_page(void){
633 static const char zDesc[] =
634 @ Enter the default ticket report format template. This is the
635 @ the template report format that initially appears when creating a
636 @ new ticket summary report.
637 ;
638 tktsetup_generic(
639 "Default Report Template",
640 "ticket-report-template",
641 zDefaultReport,
@@ -674,13 +670,13 @@
670 /*
671 ** WEBPAGE: tktsetup_keytplt
672 */
673 void tktsetup_keytplt_page(void){
674 static const char zDesc[] =
675 @ Enter the default ticket report color-key template. This is the
676 @ the color-key that initially appears when creating a
677 @ new ticket summary report.
678 ;
679 tktsetup_generic(
680 "Default Report Color-Key Template",
681 "ticket-key-template",
682 zDefaultKey,
@@ -703,34 +699,34 @@
699 if( P("setup") ){
700 cgi_redirect("tktsetup");
701 }
702 style_header("Ticket Display On Timelines");
703 db_begin_transaction();
704 @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="post"><div>
705 login_insert_csrf_secret();
706
707 @ <hr />
708 entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
709 @ <p>An SQL expression in a query against the TICKET table that will
710 @ return the title of the ticket for display purposes.</p>
711
712 @ <hr />
713 entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
714 @ <p>The name of the column in the TICKET table that contains the ticket
715 @ status in human-readable form. Case sensitive.</p>
716
717 @ <hr />
718 entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
719 "status='Closed'");
720 @ <p>An SQL expression that evaluates to true in a TICKET table query if
721 @ the ticket is closed.</p>
722
723 @ <hr />
724 @ <p>
725 @ <input type="submit" name="submit" value="Apply Changes" />
726 @ <input type="submit" name="setup" value="Cancel" />
727 @ </p>
728 @ </div></form>
729 db_end_transaction(0);
730 style_footer();
731
732 }
733
+11 -8
--- src/translate.c
+++ src/translate.c
@@ -66,15 +66,16 @@
6666
6767
/*
6868
** Translate the input stream into the output stream
6969
*/
7070
static void trans(FILE *in, FILE *out){
71
- int i, j, k; /* Loop counters */
72
- char c1, c2; /* Characters used to start a comment */
73
- int lastWasEq = 0; /* True if last non-whitespace character was "=" */
74
- char zLine[2000]; /* A single line of input */
75
- char zOut[4000]; /* The input line translated into appropriate output */
71
+ int i, j, k; /* Loop counters */
72
+ char c1, c2; /* Characters used to start a comment */
73
+ int lastWasEq = 0; /* True if last non-whitespace character was "=" */
74
+ int lastWasComma = 0; /* True if last non-whitespace character was "," */
75
+ char zLine[2000]; /* A single line of input */
76
+ char zOut[4000]; /* The input line translated into appropriate output */
7677
7778
c1 = c2 = '-';
7879
while( fgets(zLine, sizeof(zLine), in) ){
7980
for(i=0; zLine[i] && isspace(zLine[i]); i++){}
8081
if( zLine[i]!='@' ){
@@ -85,14 +86,16 @@
8586
c1 = zLine[14];
8687
c2 = zLine[15];
8788
}
8889
i += strlen(&zLine[i]);
8990
while( i>0 && isspace(zLine[i-1]) ){ i--; }
90
- lastWasEq = i>0 && zLine[i-1]=='=';
91
- }else if( lastWasEq ){
91
+ lastWasEq = i>0 && zLine[i-1]=='=';
92
+ lastWasComma = i>0 && zLine[i-1]==',';
93
+ }else if( lastWasEq || lastWasComma){
9294
/* If the last non-whitespace character before the first @ was
93
- ** an "=" then generate a string literal. But skip comments
95
+ ** an "="(var init/set) or a ","(const definition in list) then
96
+ ** generate a string literal. But skip comments
9497
** consisting of all text between c1 and c2 (default "--")
9598
** and end of line.
9699
*/
97100
int indent, omitline;
98101
i++;
99102
--- src/translate.c
+++ src/translate.c
@@ -66,15 +66,16 @@
66
67 /*
68 ** Translate the input stream into the output stream
69 */
70 static void trans(FILE *in, FILE *out){
71 int i, j, k; /* Loop counters */
72 char c1, c2; /* Characters used to start a comment */
73 int lastWasEq = 0; /* True if last non-whitespace character was "=" */
74 char zLine[2000]; /* A single line of input */
75 char zOut[4000]; /* The input line translated into appropriate output */
 
76
77 c1 = c2 = '-';
78 while( fgets(zLine, sizeof(zLine), in) ){
79 for(i=0; zLine[i] && isspace(zLine[i]); i++){}
80 if( zLine[i]!='@' ){
@@ -85,14 +86,16 @@
85 c1 = zLine[14];
86 c2 = zLine[15];
87 }
88 i += strlen(&zLine[i]);
89 while( i>0 && isspace(zLine[i-1]) ){ i--; }
90 lastWasEq = i>0 && zLine[i-1]=='=';
91 }else if( lastWasEq ){
 
92 /* If the last non-whitespace character before the first @ was
93 ** an "=" then generate a string literal. But skip comments
 
94 ** consisting of all text between c1 and c2 (default "--")
95 ** and end of line.
96 */
97 int indent, omitline;
98 i++;
99
--- src/translate.c
+++ src/translate.c
@@ -66,15 +66,16 @@
66
67 /*
68 ** Translate the input stream into the output stream
69 */
70 static void trans(FILE *in, FILE *out){
71 int i, j, k; /* Loop counters */
72 char c1, c2; /* Characters used to start a comment */
73 int lastWasEq = 0; /* True if last non-whitespace character was "=" */
74 int lastWasComma = 0; /* True if last non-whitespace character was "," */
75 char zLine[2000]; /* A single line of input */
76 char zOut[4000]; /* The input line translated into appropriate output */
77
78 c1 = c2 = '-';
79 while( fgets(zLine, sizeof(zLine), in) ){
80 for(i=0; zLine[i] && isspace(zLine[i]); i++){}
81 if( zLine[i]!='@' ){
@@ -85,14 +86,16 @@
86 c1 = zLine[14];
87 c2 = zLine[15];
88 }
89 i += strlen(&zLine[i]);
90 while( i>0 && isspace(zLine[i-1]) ){ i--; }
91 lastWasEq = i>0 && zLine[i-1]=='=';
92 lastWasComma = i>0 && zLine[i-1]==',';
93 }else if( lastWasEq || lastWasComma){
94 /* If the last non-whitespace character before the first @ was
95 ** an "="(var init/set) or a ","(const definition in list) then
96 ** generate a string literal. But skip comments
97 ** consisting of all text between c1 and c2 (default "--")
98 ** and end of line.
99 */
100 int indent, omitline;
101 i++;
102
+2 -1
--- src/update.c
+++ src/update.c
@@ -24,11 +24,11 @@
2424
2525
/*
2626
** Return true if artifact rid is a version
2727
*/
2828
int is_a_version(int rid){
29
- return db_exists("SELECT 1 FROM plink WHERE cid=%d", rid);
29
+ return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid);
3030
}
3131
3232
/*
3333
** COMMAND: update
3434
**
@@ -111,10 +111,11 @@
111111
tid = db_int(0, "SELECT rid FROM leaves, event"
112112
" WHERE event.objid=leaves.rid"
113113
" ORDER BY event.mtime DESC");
114114
}
115115
116
+ if( tid==vid ) return; /* Nothing to update */
116117
db_begin_transaction();
117118
vfile_check_signature(vid, 1);
118119
if( !nochangeFlag ) undo_begin();
119120
load_vfile_from_rid(tid);
120121
121122
--- src/update.c
+++ src/update.c
@@ -24,11 +24,11 @@
24
25 /*
26 ** Return true if artifact rid is a version
27 */
28 int is_a_version(int rid){
29 return db_exists("SELECT 1 FROM plink WHERE cid=%d", rid);
30 }
31
32 /*
33 ** COMMAND: update
34 **
@@ -111,10 +111,11 @@
111 tid = db_int(0, "SELECT rid FROM leaves, event"
112 " WHERE event.objid=leaves.rid"
113 " ORDER BY event.mtime DESC");
114 }
115
 
116 db_begin_transaction();
117 vfile_check_signature(vid, 1);
118 if( !nochangeFlag ) undo_begin();
119 load_vfile_from_rid(tid);
120
121
--- src/update.c
+++ src/update.c
@@ -24,11 +24,11 @@
24
25 /*
26 ** Return true if artifact rid is a version
27 */
28 int is_a_version(int rid){
29 return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid);
30 }
31
32 /*
33 ** COMMAND: update
34 **
@@ -111,10 +111,11 @@
111 tid = db_int(0, "SELECT rid FROM leaves, event"
112 " WHERE event.objid=leaves.rid"
113 " ORDER BY event.mtime DESC");
114 }
115
116 if( tid==vid ) return; /* Nothing to update */
117 db_begin_transaction();
118 vfile_check_signature(vid, 1);
119 if( !nochangeFlag ) undo_begin();
120 load_vfile_from_rid(tid);
121
122
+74 -11
--- src/url.c
+++ src/url.c
@@ -17,18 +17,29 @@
1717
**
1818
** This file contains code for parsing URLs that appear on the command-line
1919
*/
2020
#include "config.h"
2121
#include "url.h"
22
+
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
+}
2232
2333
/*
2434
** Parse the given URL. Populate variables in the global "g" structure.
2535
**
2636
** g.urlIsFile True if FILE:
2737
** g.urlIsHttps True if HTTPS:
38
+** g.urlIsSsh True if SSH:
2839
** g.urlProtocol "http" or "https" or "file"
29
-** g.urlName Hostname for HTTP: or HTTPS:. Filename for FILE:
40
+** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE:
3041
** g.urlPort TCP port number for HTTP or HTTPS.
3142
** g.urlDfltPort Default TCP port number (80 or 443).
3243
** g.urlPath Path name for HTTP or HTTPS.
3344
** g.urlUser Userid.
3445
** g.urlPasswd Password.
@@ -35,50 +46,70 @@
3546
** g.urlHostname HOST:PORT or just HOST if port is the default.
3647
** g.urlCanonical The URL in canonical form, omitting the password
3748
**
3849
** HTTP url format is:
3950
**
40
-** http://userid:password@host:port/path?query#fragment
51
+** http://userid:password@host:port/path
52
+**
53
+** SSH url format is:
54
+**
55
+** ssh://userid:password@host:port/path?fossil=path/to/fossil.exe
4156
**
4257
*/
4358
void url_parse(const char *zUrl){
4459
int i, j, c;
4560
char *zFile = 0;
46
- if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
61
+ if( strncmp(zUrl, "http://", 7)==0
62
+ || strncmp(zUrl, "https://", 8)==0
63
+ || strncmp(zUrl, "ssh://", 6)==0
64
+ ){
4765
int iStart;
4866
char *zLogin;
67
+ char *zExe;
68
+
4969
g.urlIsFile = 0;
5070
if( zUrl[4]=='s' ){
5171
g.urlIsHttps = 1;
5272
g.urlProtocol = "https";
5373
g.urlDfltPort = 443;
5474
iStart = 8;
75
+ }else if( zUrl[0]=='s' ){
76
+ g.urlIsSsh = 1;
77
+ g.urlProtocol = "ssh";
78
+ g.urlDfltPort = 22;
79
+ g.urlFossil = "fossil";
80
+ iStart = 6;
5581
}else{
5682
g.urlIsHttps = 0;
5783
g.urlProtocol = "http";
5884
g.urlDfltPort = 80;
5985
iStart = 7;
6086
}
6187
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
6288
if( c=='@' ){
89
+ /* Parse up the user-id and password */
6390
for(j=iStart; j<i && zUrl[j]!=':'; j++){}
6491
g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
6592
dehttpize(g.urlUser);
6693
if( j<i ){
6794
g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
6895
dehttpize(g.urlPasswd);
6996
}
97
+ if( g.urlIsSsh && g.urlPasswd ){
98
+ zLogin = mprintf("%t:*@", g.urlUser);
99
+ }else{
100
+ zLogin = mprintf("%t@", g.urlUser);
101
+ }
70102
for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
71103
g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
72104
i = j;
73
- zLogin = mprintf("%t@", g.urlUser);
74105
}else{
75106
for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
76107
g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
77108
zLogin = mprintf("");
78109
}
79
- for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
110
+ url_tolower(g.urlName);
80111
if( c==':' ){
81112
g.urlPort = 0;
82113
i++;
83114
while( (c = zUrl[i])!=0 && isdigit(c) ){
84115
g.urlPort = g.urlPort*10 + c - '0';
@@ -87,24 +118,53 @@
87118
g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
88119
}else{
89120
g.urlPort = g.urlDfltPort;
90121
g.urlHostname = g.urlName;
91122
}
92
- g.urlPath = mprintf(&zUrl[i]);
93123
dehttpize(g.urlName);
124
+ g.urlPath = mprintf("%s", &zUrl[i]);
125
+ for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){}
126
+ if( g.urlPath[i] ){
127
+ g.urlPath[i] = 0;
128
+ i++;
129
+ }
130
+ zExe = mprintf("");
131
+ while( g.urlPath[i]!=0 ){
132
+ char *zName, *zValue;
133
+ zName = &g.urlPath[i];
134
+ zValue = zName;
135
+ while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; }
136
+ if( g.urlPath[i]=='=' ){
137
+ g.urlPath[i] = 0;
138
+ i++;
139
+ zValue = &g.urlPath[i];
140
+ while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; }
141
+ }
142
+ if( g.urlPath[i] ){
143
+ g.urlPath[i] = 0;
144
+ i++;
145
+ }
146
+ if( strcmp(zName,"fossil")==0 ){
147
+ g.urlFossil = zValue;
148
+ dehttpize(g.urlFossil);
149
+ zExe = mprintf("?fossil=%T", g.urlFossil);
150
+ }
151
+ }
152
+
94153
dehttpize(g.urlPath);
95154
if( g.urlDfltPort==g.urlPort ){
96155
g.urlCanonical = mprintf(
97
- "%s://%s%T%T",
98
- g.urlProtocol, zLogin, g.urlName, g.urlPath
156
+ "%s://%s%T%T%s",
157
+ g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe
99158
);
100159
}else{
101160
g.urlCanonical = mprintf(
102
- "%s://%s%T:%d%T",
103
- g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
161
+ "%s://%s%T:%d%T%s",
162
+ g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe
104163
);
105164
}
165
+ if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++;
106166
free(zLogin);
107167
}else if( strncmp(zUrl, "file:", 5)==0 ){
108168
g.urlIsFile = 1;
109169
if( zUrl[5]=='/' && zUrl[6]=='/' ){
110170
i = 7;
@@ -150,19 +210,22 @@
150210
}
151211
url_parse(g.argv[2]);
152212
for(i=0; i<2; i++){
153213
printf("g.urlIsFile = %d\n", g.urlIsFile);
154214
printf("g.urlIsHttps = %d\n", g.urlIsHttps);
215
+ printf("g.urlIsSsh = %d\n", g.urlIsSsh);
155216
printf("g.urlProtocol = %s\n", g.urlProtocol);
156217
printf("g.urlName = %s\n", g.urlName);
157218
printf("g.urlPort = %d\n", g.urlPort);
158219
printf("g.urlDfltPort = %d\n", g.urlDfltPort);
159220
printf("g.urlHostname = %s\n", g.urlHostname);
160221
printf("g.urlPath = %s\n", g.urlPath);
161222
printf("g.urlUser = %s\n", g.urlUser);
162223
printf("g.urlPasswd = %s\n", g.urlPasswd);
163224
printf("g.urlCanonical = %s\n", g.urlCanonical);
225
+ printf("g.urlFossil = %s\n", g.urlFossil);
226
+ if( g.urlIsFile || g.urlIsSsh ) break;
164227
if( i==0 ){
165228
printf("********\n");
166229
url_enable_proxy("Using proxy: ");
167230
}
168231
}
@@ -288,11 +351,11 @@
288351
zName2 = 0;
289352
z = zValue2;
290353
if( z==0 ) continue;
291354
}
292355
blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
293
- zSep = "&";
356
+ zSep = "&amp;";
294357
}
295358
if( zName1 && zValue1 ){
296359
blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
297360
}
298361
if( zName2 && zValue2 ){
299362
--- src/url.c
+++ src/url.c
@@ -17,18 +17,29 @@
17 **
18 ** This file contains code for parsing URLs that appear on the command-line
19 */
20 #include "config.h"
21 #include "url.h"
 
 
 
 
 
 
 
 
 
 
22
23 /*
24 ** Parse the given URL. Populate variables in the global "g" structure.
25 **
26 ** g.urlIsFile True if FILE:
27 ** g.urlIsHttps True if HTTPS:
 
28 ** g.urlProtocol "http" or "https" or "file"
29 ** g.urlName Hostname for HTTP: or HTTPS:. Filename for FILE:
30 ** g.urlPort TCP port number for HTTP or HTTPS.
31 ** g.urlDfltPort Default TCP port number (80 or 443).
32 ** g.urlPath Path name for HTTP or HTTPS.
33 ** g.urlUser Userid.
34 ** g.urlPasswd Password.
@@ -35,50 +46,70 @@
35 ** g.urlHostname HOST:PORT or just HOST if port is the default.
36 ** g.urlCanonical The URL in canonical form, omitting the password
37 **
38 ** HTTP url format is:
39 **
40 ** http://userid:password@host:port/path?query#fragment
 
 
 
 
41 **
42 */
43 void url_parse(const char *zUrl){
44 int i, j, c;
45 char *zFile = 0;
46 if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
 
 
 
47 int iStart;
48 char *zLogin;
 
 
49 g.urlIsFile = 0;
50 if( zUrl[4]=='s' ){
51 g.urlIsHttps = 1;
52 g.urlProtocol = "https";
53 g.urlDfltPort = 443;
54 iStart = 8;
 
 
 
 
 
 
55 }else{
56 g.urlIsHttps = 0;
57 g.urlProtocol = "http";
58 g.urlDfltPort = 80;
59 iStart = 7;
60 }
61 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
62 if( c=='@' ){
 
63 for(j=iStart; j<i && zUrl[j]!=':'; j++){}
64 g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
65 dehttpize(g.urlUser);
66 if( j<i ){
67 g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
68 dehttpize(g.urlPasswd);
69 }
 
 
 
 
 
70 for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
71 g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
72 i = j;
73 zLogin = mprintf("%t@", g.urlUser);
74 }else{
75 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
76 g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
77 zLogin = mprintf("");
78 }
79 for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
80 if( c==':' ){
81 g.urlPort = 0;
82 i++;
83 while( (c = zUrl[i])!=0 && isdigit(c) ){
84 g.urlPort = g.urlPort*10 + c - '0';
@@ -87,24 +118,53 @@
87 g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
88 }else{
89 g.urlPort = g.urlDfltPort;
90 g.urlHostname = g.urlName;
91 }
92 g.urlPath = mprintf(&zUrl[i]);
93 dehttpize(g.urlName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94 dehttpize(g.urlPath);
95 if( g.urlDfltPort==g.urlPort ){
96 g.urlCanonical = mprintf(
97 "%s://%s%T%T",
98 g.urlProtocol, zLogin, g.urlName, g.urlPath
99 );
100 }else{
101 g.urlCanonical = mprintf(
102 "%s://%s%T:%d%T",
103 g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
104 );
105 }
 
106 free(zLogin);
107 }else if( strncmp(zUrl, "file:", 5)==0 ){
108 g.urlIsFile = 1;
109 if( zUrl[5]=='/' && zUrl[6]=='/' ){
110 i = 7;
@@ -150,19 +210,22 @@
150 }
151 url_parse(g.argv[2]);
152 for(i=0; i<2; i++){
153 printf("g.urlIsFile = %d\n", g.urlIsFile);
154 printf("g.urlIsHttps = %d\n", g.urlIsHttps);
 
155 printf("g.urlProtocol = %s\n", g.urlProtocol);
156 printf("g.urlName = %s\n", g.urlName);
157 printf("g.urlPort = %d\n", g.urlPort);
158 printf("g.urlDfltPort = %d\n", g.urlDfltPort);
159 printf("g.urlHostname = %s\n", g.urlHostname);
160 printf("g.urlPath = %s\n", g.urlPath);
161 printf("g.urlUser = %s\n", g.urlUser);
162 printf("g.urlPasswd = %s\n", g.urlPasswd);
163 printf("g.urlCanonical = %s\n", g.urlCanonical);
 
 
164 if( i==0 ){
165 printf("********\n");
166 url_enable_proxy("Using proxy: ");
167 }
168 }
@@ -288,11 +351,11 @@
288 zName2 = 0;
289 z = zValue2;
290 if( z==0 ) continue;
291 }
292 blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
293 zSep = "&";
294 }
295 if( zName1 && zValue1 ){
296 blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
297 }
298 if( zName2 && zValue2 ){
299
--- src/url.c
+++ src/url.c
@@ -17,18 +17,29 @@
17 **
18 ** This file contains code for parsing URLs that appear on the command-line
19 */
20 #include "config.h"
21 #include "url.h"
22
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 /*
34 ** Parse the given URL. Populate variables in the global "g" structure.
35 **
36 ** g.urlIsFile True if FILE:
37 ** g.urlIsHttps True if HTTPS:
38 ** g.urlIsSsh True if SSH:
39 ** g.urlProtocol "http" or "https" or "file"
40 ** g.urlName Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE:
41 ** g.urlPort TCP port number for HTTP or HTTPS.
42 ** g.urlDfltPort Default TCP port number (80 or 443).
43 ** g.urlPath Path name for HTTP or HTTPS.
44 ** g.urlUser Userid.
45 ** g.urlPasswd Password.
@@ -35,50 +46,70 @@
46 ** g.urlHostname HOST:PORT or just HOST if port is the default.
47 ** g.urlCanonical The URL in canonical form, omitting the password
48 **
49 ** HTTP url format is:
50 **
51 ** http://userid:password@host:port/path
52 **
53 ** SSH url format is:
54 **
55 ** ssh://userid:password@host:port/path?fossil=path/to/fossil.exe
56 **
57 */
58 void url_parse(const char *zUrl){
59 int i, j, c;
60 char *zFile = 0;
61 if( strncmp(zUrl, "http://", 7)==0
62 || strncmp(zUrl, "https://", 8)==0
63 || strncmp(zUrl, "ssh://", 6)==0
64 ){
65 int iStart;
66 char *zLogin;
67 char *zExe;
68
69 g.urlIsFile = 0;
70 if( zUrl[4]=='s' ){
71 g.urlIsHttps = 1;
72 g.urlProtocol = "https";
73 g.urlDfltPort = 443;
74 iStart = 8;
75 }else if( zUrl[0]=='s' ){
76 g.urlIsSsh = 1;
77 g.urlProtocol = "ssh";
78 g.urlDfltPort = 22;
79 g.urlFossil = "fossil";
80 iStart = 6;
81 }else{
82 g.urlIsHttps = 0;
83 g.urlProtocol = "http";
84 g.urlDfltPort = 80;
85 iStart = 7;
86 }
87 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
88 if( c=='@' ){
89 /* Parse up the user-id and password */
90 for(j=iStart; j<i && zUrl[j]!=':'; j++){}
91 g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
92 dehttpize(g.urlUser);
93 if( j<i ){
94 g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
95 dehttpize(g.urlPasswd);
96 }
97 if( g.urlIsSsh && g.urlPasswd ){
98 zLogin = mprintf("%t:*@", g.urlUser);
99 }else{
100 zLogin = mprintf("%t@", g.urlUser);
101 }
102 for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
103 g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
104 i = j;
 
105 }else{
106 for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
107 g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
108 zLogin = mprintf("");
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';
@@ -87,24 +118,53 @@
118 g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort);
119 }else{
120 g.urlPort = g.urlDfltPort;
121 g.urlHostname = g.urlName;
122 }
 
123 dehttpize(g.urlName);
124 g.urlPath = mprintf("%s", &zUrl[i]);
125 for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){}
126 if( g.urlPath[i] ){
127 g.urlPath[i] = 0;
128 i++;
129 }
130 zExe = mprintf("");
131 while( g.urlPath[i]!=0 ){
132 char *zName, *zValue;
133 zName = &g.urlPath[i];
134 zValue = zName;
135 while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; }
136 if( g.urlPath[i]=='=' ){
137 g.urlPath[i] = 0;
138 i++;
139 zValue = &g.urlPath[i];
140 while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; }
141 }
142 if( g.urlPath[i] ){
143 g.urlPath[i] = 0;
144 i++;
145 }
146 if( strcmp(zName,"fossil")==0 ){
147 g.urlFossil = zValue;
148 dehttpize(g.urlFossil);
149 zExe = mprintf("?fossil=%T", g.urlFossil);
150 }
151 }
152
153 dehttpize(g.urlPath);
154 if( g.urlDfltPort==g.urlPort ){
155 g.urlCanonical = mprintf(
156 "%s://%s%T%T%s",
157 g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe
158 );
159 }else{
160 g.urlCanonical = mprintf(
161 "%s://%s%T:%d%T%s",
162 g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe
163 );
164 }
165 if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++;
166 free(zLogin);
167 }else if( strncmp(zUrl, "file:", 5)==0 ){
168 g.urlIsFile = 1;
169 if( zUrl[5]=='/' && zUrl[6]=='/' ){
170 i = 7;
@@ -150,19 +210,22 @@
210 }
211 url_parse(g.argv[2]);
212 for(i=0; i<2; i++){
213 printf("g.urlIsFile = %d\n", g.urlIsFile);
214 printf("g.urlIsHttps = %d\n", g.urlIsHttps);
215 printf("g.urlIsSsh = %d\n", g.urlIsSsh);
216 printf("g.urlProtocol = %s\n", g.urlProtocol);
217 printf("g.urlName = %s\n", g.urlName);
218 printf("g.urlPort = %d\n", g.urlPort);
219 printf("g.urlDfltPort = %d\n", g.urlDfltPort);
220 printf("g.urlHostname = %s\n", g.urlHostname);
221 printf("g.urlPath = %s\n", g.urlPath);
222 printf("g.urlUser = %s\n", g.urlUser);
223 printf("g.urlPasswd = %s\n", g.urlPasswd);
224 printf("g.urlCanonical = %s\n", g.urlCanonical);
225 printf("g.urlFossil = %s\n", g.urlFossil);
226 if( g.urlIsFile || g.urlIsSsh ) break;
227 if( i==0 ){
228 printf("********\n");
229 url_enable_proxy("Using proxy: ");
230 }
231 }
@@ -288,11 +351,11 @@
351 zName2 = 0;
352 z = zValue2;
353 if( z==0 ) continue;
354 }
355 blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
356 zSep = "&amp;";
357 }
358 if( zName1 && zValue1 ){
359 blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
360 }
361 if( zName2 && zValue2 ){
362
+3
--- src/user.c
+++ src/user.c
@@ -39,11 +39,14 @@
3939
if( z[i]<' ' ) z[i] = ' ';
4040
}
4141
blob_append(pBlob, z, -1);
4242
}
4343
44
+#if defined(_WIN32)
4445
#ifdef __MINGW32__
46
+#include <conio.h>
47
+#endif
4548
/*
4649
** getpass for Windows
4750
*/
4851
static char *getpass(const char *prompt){
4952
static char pwd[64];
5053
--- src/user.c
+++ src/user.c
@@ -39,11 +39,14 @@
39 if( z[i]<' ' ) z[i] = ' ';
40 }
41 blob_append(pBlob, z, -1);
42 }
43
 
44 #ifdef __MINGW32__
 
 
45 /*
46 ** getpass for Windows
47 */
48 static char *getpass(const char *prompt){
49 static char pwd[64];
50
--- src/user.c
+++ src/user.c
@@ -39,11 +39,14 @@
39 if( z[i]<' ' ) z[i] = ' ';
40 }
41 blob_append(pBlob, z, -1);
42 }
43
44 #if defined(_WIN32)
45 #ifdef __MINGW32__
46 #include <conio.h>
47 #endif
48 /*
49 ** getpass for Windows
50 */
51 static char *getpass(const char *prompt){
52 static char pwd[64];
53
--- src/vfile.c
+++ src/vfile.c
@@ -19,11 +19,15 @@
1919
*/
2020
#include "config.h"
2121
#include "vfile.h"
2222
#include <assert.h>
2323
#include <sys/types.h>
24
+#if defined(__DMC__)
25
+#include "dirent.h"
26
+#else
2427
#include <dirent.h>
28
+#endif
2529
2630
/*
2731
** Given a UUID, return the corresponding record ID. If the UUID
2832
** does not exist, then return 0.
2933
**
3034
--- src/vfile.c
+++ src/vfile.c
@@ -19,11 +19,15 @@
19 */
20 #include "config.h"
21 #include "vfile.h"
22 #include <assert.h>
23 #include <sys/types.h>
 
 
 
24 #include <dirent.h>
 
25
26 /*
27 ** Given a UUID, return the corresponding record ID. If the UUID
28 ** does not exist, then return 0.
29 **
30
--- src/vfile.c
+++ src/vfile.c
@@ -19,11 +19,15 @@
19 */
20 #include "config.h"
21 #include "vfile.h"
22 #include <assert.h>
23 #include <sys/types.h>
24 #if defined(__DMC__)
25 #include "dirent.h"
26 #else
27 #include <dirent.h>
28 #endif
29
30 /*
31 ** Given a UUID, return the corresponding record ID. If the UUID
32 ** does not exist, then return 0.
33 **
34
+59 -50
--- src/wiki.c
+++ src/wiki.c
@@ -48,15 +48,15 @@
4848
/*
4949
** Output rules for well-formed wiki pages
5050
*/
5151
static void well_formed_wiki_name_rules(void){
5252
@ <ul>
53
- @ <li> Must not begin or end with a space.
53
+ @ <li> Must not begin or end with a space.</li>
5454
@ <li> Must not contain any control characters, including tab or
55
- @ newline.
56
- @ <li> Must not have two or more spaces in a row internally.
57
- @ <li> Must be between 3 and 100 characters in length.
55
+ @ newline.</li>
56
+ @ <li> Must not have two or more spaces in a row internally.</li>
57
+ @ <li> Must be between 3 and 100 characters in length.</li>
5858
@ </ul>
5959
}
6060
6161
/*
6262
** Check a wiki name. If it is not well-formed, then issue an error
@@ -63,12 +63,12 @@
6363
** and return true. If it is well-formed, return false.
6464
*/
6565
static int check_name(const char *z){
6666
if( !wiki_name_is_wellformed((const unsigned char *)z) ){
6767
style_header("Wiki Page Name Error");
68
- @ The wiki name "<b>%h(z)</b>" is not well-formed. Rules for
69
- @ wiki page names:
68
+ @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
69
+ @ Rules for wiki page names:
7070
well_formed_wiki_name_rules();
7171
style_footer();
7272
return 1;
7373
}
7474
return 0;
@@ -79,14 +79,22 @@
7979
** WEBPAGE: index
8080
** WEBPAGE: not_found
8181
*/
8282
void home_page(void){
8383
char *zPageName = db_get("project-name",0);
84
+ char *zIndexPage = db_get("index-page",0);
8485
login_check_credentials();
8586
if( !g.okRdWiki ){
8687
cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
8788
}
89
+ if( zIndexPage ){
90
+ while( zIndexPage[0]=='/' ) zIndexPage++;
91
+ if( strcmp(zIndexPage, P("PATH_INFO"))==0 ) zIndexPage = 0;
92
+ }
93
+ if( zIndexPage ){
94
+ cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage);
95
+ }
8896
if( zPageName ){
8997
login_check_credentials();
9098
g.zExtra = zPageName;
9199
cgi_set_parameter_nocopy("name", g.zExtra);
92100
g.isHome = 1;
@@ -147,14 +155,14 @@
147155
if( g.okNewWiki ){
148156
@ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
149157
}
150158
@ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
151159
@ available on this server.</li>
152
- @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
153
- @ Search wiki titles: <input type="text" name="title"/>
154
- @ &nbsp; <input type="submit" />
155
- @ </li>
160
+ @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
161
+ @ Search wiki titles: <input type="text" name="title"/>
162
+ @ &nbsp; <input type="submit" /></div></form>
163
+ @ </li>
156164
@ </ul>
157165
style_footer();
158166
return;
159167
}
160168
if( check_name(zPageName) ) return;
@@ -186,11 +194,11 @@
186194
style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
187195
g.zTop, zPageName);
188196
}
189197
if( rid && g.okApndWiki && g.okAttach ){
190198
style_submenu_element("Attach", "Add An Attachment",
191
- "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
199
+ "%s/attachadd?page=%T&amp;from=%s/wiki%%3fname=%T",
192200
g.zTop, zPageName, g.zTop, zPageName);
193201
}
194202
if( rid && g.okApndWiki ){
195203
style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
196204
g.zTop, zPageName);
@@ -218,19 +226,21 @@
218226
if( cnt==0 ){
219227
@ <hr><h2>Attachments:</h2>
220228
@ <ul>
221229
}
222230
cnt++;
223
- if( g.okHistory ){
224
- @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
225
- }else{
231
+ if( g.okHistory && g.okRead ){
226232
@ <li>
233
+ @ <a href="%s(g.zTop)/attachview?page=%s(zPageName)&amp;file=%t(zFile)">
234
+ @ %h(zFile)</a>
235
+ }else{
236
+ @ <li>%h(zFile)
227237
}
228
- @ %h(zFile)</a> add by %h(zUser) on
238
+ @ added by %h(zUser) on
229239
hyperlink_to_date(zDate, ".");
230240
if( g.okWrWiki && g.okAttach ){
231
- @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
241
+ @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
232242
}
233243
}
234244
if( cnt ){
235245
@ </ul>
236246
}
@@ -342,30 +352,30 @@
342352
zHtmlPageName = mprintf("Edit: %s", zPageName);
343353
style_header(zHtmlPageName);
344354
if( P("preview")!=0 ){
345355
blob_zero(&wiki);
346356
blob_append(&wiki, zBody, -1);
347
- @ Preview:<hr>
357
+ @ Preview:<hr />
348358
wiki_convert(&wiki, 0, 0);
349
- @ <hr>
359
+ @ <hr />
350360
blob_reset(&wiki);
351361
}
352362
for(n=2, z=zBody; z[0]; z++){
353363
if( z[0]=='\n' ) n++;
354364
}
355365
if( n<20 ) n = 20;
356366
if( n>40 ) n = 40;
357
- @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
367
+ @ <form method="post" action="%s(g.zBaseURL)/wikiedit"><div>
358368
login_insert_csrf_secret();
359
- @ <input type="hidden" name="name" value="%h(zPageName)">
369
+ @ <input type="hidden" name="name" value="%h(zPageName)" />
360370
@ <textarea name="w" class="wikiedit" cols="80"
361371
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
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
- @ </form>
372
+ @ <br />
373
+ @ <input type="submit" name="preview" value="Preview Your Changes" />
374
+ @ <input type="submit" name="submit" value="Apply These Changes" />
375
+ @ <input type="submit" name="cancel" value="Cancel" />
376
+ @ </div></form>
367377
if( !isSandbox ){
368378
manifest_clear(&m);
369379
}
370380
style_footer();
371381
}
@@ -387,21 +397,20 @@
387397
zName = PD("name","");
388398
if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
389399
cgi_redirectf("wikiedit?name=%T", zName);
390400
}
391401
style_header("Create A New Wiki Page");
392
- @ <p>Rules for wiki page names:
402
+ @ <p>Rules for wiki page names:</p>
393403
well_formed_wiki_name_rules();
394
- @ </p>
395
- @ <form method="POST" action="%s(g.zBaseURL)/wikinew">
404
+ @ <form method="post" action="%s(g.zBaseURL)/wikinew">
396405
@ <p>Name of new wiki page:
397
- @ <input type="text" width="35" name="name" value="%h(zName)">
398
- @ <input type="submit" value="Create">
406
+ @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
407
+ @ <input type="submit" value="Create" />
399408
@ </p></form>
400409
if( zName[0] ){
401
- @ <p><b><font color="red">
402
- @ "%h(zName)" is not a valid wiki page name!</font></b></p>
410
+ @ <p><span class="wikiError">
411
+ @ "%h(zName)" is not a valid wiki page name!</span></p>
403412
}
404413
style_footer();
405414
}
406415
407416
@@ -528,15 +537,15 @@
528537
zUser = PD("u", g.zLogin);
529538
@ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
530539
login_insert_csrf_secret();
531540
@ <input type="hidden" name="name" value="%h(zPageName)">
532541
@ Your Name:
533
- @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
534
- @ Comment to append:<br>
542
+ @ <input type="text" name="u" size="20" value="%h(zUser)"><br />
543
+ @ Comment to append:<br />
535544
@ <textarea name="r" class="wikiedit" cols="80"
536545
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
537
- @ <br>
546
+ @ <br />
538547
@ <input type="submit" name="preview" value="Preview Your Comment">
539548
@ <input type="submit" name="submit" value="Append Your Changes">
540549
@ <input type="submit" name="cancel" value="Cancel">
541550
@ </form>
542551
style_footer();
@@ -551,11 +560,11 @@
551560
** Function called to output extra text at the end of each line in
552561
** a wiki history listing.
553562
*/
554563
static void wiki_history_extra(int rid){
555564
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
556
- @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
565
+ @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&amp;a=%d(rid)">[diff]</a>
557566
}
558567
}
559568
560569
/*
561570
** WEBPAGE: whistory
@@ -634,11 +643,11 @@
634643
if( m2.type==CFTYPE_WIKI ){
635644
blob_init(&w2, m2.zWiki, -1);
636645
}
637646
}
638647
blob_zero(&d);
639
- text_diff(&w2, &w1, &d, 5);
648
+ text_diff(&w2, &w1, &d, 5, 1);
640649
@ <pre>
641650
@ %h(blob_str(&d))
642651
@ </pre>
643652
style_footer();
644653
}
@@ -731,39 +740,39 @@
731740
@ </ol>
732741
@ <p>We call the first five rules above "wiki" formatting rules. The
733742
@ last two rules are the HTML formatting rule.</p>
734743
@ <h2>Formatting Rule Details</h2>
735744
@ <ol>
736
- @ <li> <p><b>Paragraphs</b>. Any sequence of one or more blank lines forms
745
+ @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
737746
@ a paragraph break. Centered or right-justified paragraphs are not
738747
@ supported by wiki markup, but you can do these things if you need them
739
- @ using HTML.</p>
740
- @ <li> <p><b>Bullet Lists</b>.
748
+ @ using HTML.</p></li>
749
+ @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
741750
@ A bullet list item is a line that begins with a single "*" character
742751
@ surrounded on
743752
@ both sides by two or more spaces or by a tab. Only a single level
744
- @ of bullet list is supported by wiki. For nested lists, use HTML.</p>
745
- @ <li> <p><b>Enumeration Lists</b>.
753
+ @ of bullet list is supported by wiki. For nested lists, use HTML.</p></li>
754
+ @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>.
746755
@ An enumeration list item is a line that begins with a single "#" character
747756
@ surrounded on both sides by two or more spaces or by a tab. Only a single
748757
@ level of enumeration list is supported by wiki. For nested lists or for
749
- @ enumerations that count using letters or roman numerials, use HTML.</p>
750
- @ <li> <p><b>Indented Paragraphs</b>.
758
+ @ enumerations that count using letters or roman numerials, use HTML.</p></li>
759
+ @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>.
751760
@ Any paragraph that begins with two or more spaces or a tab and
752761
@ which is not a bullet or enumeration list item is rendered
753762
@ indented. Only a single level of indentation is supported by wiki; use
754
- @ HTML for deeper indentation.</p>
755
- @ <li> <p><b>Hyperlinks</b>.
763
+ @ HTML for deeper indentation.</p></li>
764
+ @ <li> <p><span class="wikiruleHead">Hyperlinks</span>.
756765
@ Text within square brackets ("[...]") becomes a hyperlink. The
757766
@ target can be a wiki page name, the artifact ID of a check-in or ticket,
758767
@ the name of an image, or a URL. By default, the target is displayed
759768
@ as the text of the hyperlink. But you can specify alternative text
760769
@ after the target name separated by a "|" character.</p>
761770
@ <p>You can also link to internal anchor names using [#anchor-name], providing
762771
@ you have added the necessary "&lt;a name="anchor-name"&gt;&lt;/a&gt;"
763
- @ tag to your wiki page.</p>
764
- @ <li> <p><b>HTML</b>.
772
+ @ tag to your wiki page.</p></li>
773
+ @ <li> <p><span class="wikiruleHead">HTML</span>.
765774
@ The following standard HTML elements may be used:
766775
@ &lt;a&gt;
767776
@ &lt;address&gt;
768777
@ &lt;b&gt;
769778
@ &lt;big&gt;
@@ -813,16 +822,16 @@
813822
@ &lt;verbatim&gt; and &lt;nowiki&gt;.
814823
@ No other elements are allowed. All attributes are checked and
815824
@ only a few benign attributes are allowed on each element.
816825
@ In particular, any attributes that specify javascript or CSS
817826
@ are elided.</p></li>
818
- @ <li><p><b>Special Markup.</b>
827
+ @ <li><p><span class="wikiruleHead">Special Markup.</span>
819828
@ The &lt;nowiki&gt; tag disables all wiki formatting rules
820829
@ through the matching &lt;/nowiki&gt; element.
821830
@ The &lt;verbatim&gt; tag works like &lt;pre&gt; with the addition
822831
@ that it also disables all wiki and HTML markup
823
- @ through the matching &lt;/verbatim&gt;.
832
+ @ through the matching &lt;/verbatim&gt;.</p></li>
824833
@ </ol>
825834
style_footer();
826835
}
827836
828837
/*
829838
--- src/wiki.c
+++ src/wiki.c
@@ -48,15 +48,15 @@
48 /*
49 ** Output rules for well-formed wiki pages
50 */
51 static void well_formed_wiki_name_rules(void){
52 @ <ul>
53 @ <li> Must not begin or end with a space.
54 @ <li> Must not contain any control characters, including tab or
55 @ newline.
56 @ <li> Must not have two or more spaces in a row internally.
57 @ <li> Must be between 3 and 100 characters in length.
58 @ </ul>
59 }
60
61 /*
62 ** Check a wiki name. If it is not well-formed, then issue an error
@@ -63,12 +63,12 @@
63 ** and return true. If it is well-formed, return false.
64 */
65 static int check_name(const char *z){
66 if( !wiki_name_is_wellformed((const unsigned char *)z) ){
67 style_header("Wiki Page Name Error");
68 @ The wiki name "<b>%h(z)</b>" is not well-formed. Rules for
69 @ wiki page names:
70 well_formed_wiki_name_rules();
71 style_footer();
72 return 1;
73 }
74 return 0;
@@ -79,14 +79,22 @@
79 ** WEBPAGE: index
80 ** WEBPAGE: not_found
81 */
82 void home_page(void){
83 char *zPageName = db_get("project-name",0);
 
84 login_check_credentials();
85 if( !g.okRdWiki ){
86 cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
87 }
 
 
 
 
 
 
 
88 if( zPageName ){
89 login_check_credentials();
90 g.zExtra = zPageName;
91 cgi_set_parameter_nocopy("name", g.zExtra);
92 g.isHome = 1;
@@ -147,14 +155,14 @@
147 if( g.okNewWiki ){
148 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
149 }
150 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
151 @ available on this server.</li>
152 @ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
153 @ Search wiki titles: <input type="text" name="title"/>
154 @ &nbsp; <input type="submit" />
155 @ </li>
156 @ </ul>
157 style_footer();
158 return;
159 }
160 if( check_name(zPageName) ) return;
@@ -186,11 +194,11 @@
186 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
187 g.zTop, zPageName);
188 }
189 if( rid && g.okApndWiki && g.okAttach ){
190 style_submenu_element("Attach", "Add An Attachment",
191 "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
192 g.zTop, zPageName, g.zTop, zPageName);
193 }
194 if( rid && g.okApndWiki ){
195 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
196 g.zTop, zPageName);
@@ -218,19 +226,21 @@
218 if( cnt==0 ){
219 @ <hr><h2>Attachments:</h2>
220 @ <ul>
221 }
222 cnt++;
223 if( g.okHistory ){
224 @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
225 }else{
226 @ <li>
 
 
 
 
227 }
228 @ %h(zFile)</a> add by %h(zUser) on
229 hyperlink_to_date(zDate, ".");
230 if( g.okWrWiki && g.okAttach ){
231 @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
232 }
233 }
234 if( cnt ){
235 @ </ul>
236 }
@@ -342,30 +352,30 @@
342 zHtmlPageName = mprintf("Edit: %s", zPageName);
343 style_header(zHtmlPageName);
344 if( P("preview")!=0 ){
345 blob_zero(&wiki);
346 blob_append(&wiki, zBody, -1);
347 @ Preview:<hr>
348 wiki_convert(&wiki, 0, 0);
349 @ <hr>
350 blob_reset(&wiki);
351 }
352 for(n=2, z=zBody; z[0]; z++){
353 if( z[0]=='\n' ) n++;
354 }
355 if( n<20 ) n = 20;
356 if( n>40 ) n = 40;
357 @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
358 login_insert_csrf_secret();
359 @ <input type="hidden" name="name" value="%h(zPageName)">
360 @ <textarea name="w" class="wikiedit" cols="80"
361 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
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 @ </form>
367 if( !isSandbox ){
368 manifest_clear(&m);
369 }
370 style_footer();
371 }
@@ -387,21 +397,20 @@
387 zName = PD("name","");
388 if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
389 cgi_redirectf("wikiedit?name=%T", zName);
390 }
391 style_header("Create A New Wiki Page");
392 @ <p>Rules for wiki page names:
393 well_formed_wiki_name_rules();
394 @ </p>
395 @ <form method="POST" action="%s(g.zBaseURL)/wikinew">
396 @ <p>Name of new wiki page:
397 @ <input type="text" width="35" name="name" value="%h(zName)">
398 @ <input type="submit" value="Create">
399 @ </p></form>
400 if( zName[0] ){
401 @ <p><b><font color="red">
402 @ "%h(zName)" is not a valid wiki page name!</font></b></p>
403 }
404 style_footer();
405 }
406
407
@@ -528,15 +537,15 @@
528 zUser = PD("u", g.zLogin);
529 @ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
530 login_insert_csrf_secret();
531 @ <input type="hidden" name="name" value="%h(zPageName)">
532 @ Your Name:
533 @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
534 @ Comment to append:<br>
535 @ <textarea name="r" class="wikiedit" cols="80"
536 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
537 @ <br>
538 @ <input type="submit" name="preview" value="Preview Your Comment">
539 @ <input type="submit" name="submit" value="Append Your Changes">
540 @ <input type="submit" name="cancel" value="Cancel">
541 @ </form>
542 style_footer();
@@ -551,11 +560,11 @@
551 ** Function called to output extra text at the end of each line in
552 ** a wiki history listing.
553 */
554 static void wiki_history_extra(int rid){
555 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
556 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
557 }
558 }
559
560 /*
561 ** WEBPAGE: whistory
@@ -634,11 +643,11 @@
634 if( m2.type==CFTYPE_WIKI ){
635 blob_init(&w2, m2.zWiki, -1);
636 }
637 }
638 blob_zero(&d);
639 text_diff(&w2, &w1, &d, 5);
640 @ <pre>
641 @ %h(blob_str(&d))
642 @ </pre>
643 style_footer();
644 }
@@ -731,39 +740,39 @@
731 @ </ol>
732 @ <p>We call the first five rules above "wiki" formatting rules. The
733 @ last two rules are the HTML formatting rule.</p>
734 @ <h2>Formatting Rule Details</h2>
735 @ <ol>
736 @ <li> <p><b>Paragraphs</b>. Any sequence of one or more blank lines forms
737 @ a paragraph break. Centered or right-justified paragraphs are not
738 @ supported by wiki markup, but you can do these things if you need them
739 @ using HTML.</p>
740 @ <li> <p><b>Bullet Lists</b>.
741 @ A bullet list item is a line that begins with a single "*" character
742 @ surrounded on
743 @ both sides by two or more spaces or by a tab. Only a single level
744 @ of bullet list is supported by wiki. For nested lists, use HTML.</p>
745 @ <li> <p><b>Enumeration Lists</b>.
746 @ An enumeration list item is a line that begins with a single "#" character
747 @ surrounded on both sides by two or more spaces or by a tab. Only a single
748 @ level of enumeration list is supported by wiki. For nested lists or for
749 @ enumerations that count using letters or roman numerials, use HTML.</p>
750 @ <li> <p><b>Indented Paragraphs</b>.
751 @ Any paragraph that begins with two or more spaces or a tab and
752 @ which is not a bullet or enumeration list item is rendered
753 @ indented. Only a single level of indentation is supported by wiki; use
754 @ HTML for deeper indentation.</p>
755 @ <li> <p><b>Hyperlinks</b>.
756 @ Text within square brackets ("[...]") becomes a hyperlink. The
757 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
758 @ the name of an image, or a URL. By default, the target is displayed
759 @ as the text of the hyperlink. But you can specify alternative text
760 @ after the target name separated by a "|" character.</p>
761 @ <p>You can also link to internal anchor names using [#anchor-name], providing
762 @ you have added the necessary "&lt;a name="anchor-name"&gt;&lt;/a&gt;"
763 @ tag to your wiki page.</p>
764 @ <li> <p><b>HTML</b>.
765 @ The following standard HTML elements may be used:
766 @ &lt;a&gt;
767 @ &lt;address&gt;
768 @ &lt;b&gt;
769 @ &lt;big&gt;
@@ -813,16 +822,16 @@
813 @ &lt;verbatim&gt; and &lt;nowiki&gt;.
814 @ No other elements are allowed. All attributes are checked and
815 @ only a few benign attributes are allowed on each element.
816 @ In particular, any attributes that specify javascript or CSS
817 @ are elided.</p></li>
818 @ <li><p><b>Special Markup.</b>
819 @ The &lt;nowiki&gt; tag disables all wiki formatting rules
820 @ through the matching &lt;/nowiki&gt; element.
821 @ The &lt;verbatim&gt; tag works like &lt;pre&gt; with the addition
822 @ that it also disables all wiki and HTML markup
823 @ through the matching &lt;/verbatim&gt;.
824 @ </ol>
825 style_footer();
826 }
827
828 /*
829
--- src/wiki.c
+++ src/wiki.c
@@ -48,15 +48,15 @@
48 /*
49 ** Output rules for well-formed wiki pages
50 */
51 static void well_formed_wiki_name_rules(void){
52 @ <ul>
53 @ <li> Must not begin or end with a space.</li>
54 @ <li> Must not contain any control characters, including tab or
55 @ newline.</li>
56 @ <li> Must not have two or more spaces in a row internally.</li>
57 @ <li> Must be between 3 and 100 characters in length.</li>
58 @ </ul>
59 }
60
61 /*
62 ** Check a wiki name. If it is not well-formed, then issue an error
@@ -63,12 +63,12 @@
63 ** and return true. If it is well-formed, return false.
64 */
65 static int check_name(const char *z){
66 if( !wiki_name_is_wellformed((const unsigned char *)z) ){
67 style_header("Wiki Page Name Error");
68 @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
69 @ Rules for wiki page names:
70 well_formed_wiki_name_rules();
71 style_footer();
72 return 1;
73 }
74 return 0;
@@ -79,14 +79,22 @@
79 ** WEBPAGE: index
80 ** WEBPAGE: not_found
81 */
82 void home_page(void){
83 char *zPageName = db_get("project-name",0);
84 char *zIndexPage = db_get("index-page",0);
85 login_check_credentials();
86 if( !g.okRdWiki ){
87 cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL);
88 }
89 if( zIndexPage ){
90 while( zIndexPage[0]=='/' ) zIndexPage++;
91 if( strcmp(zIndexPage, P("PATH_INFO"))==0 ) zIndexPage = 0;
92 }
93 if( zIndexPage ){
94 cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage);
95 }
96 if( zPageName ){
97 login_check_credentials();
98 g.zExtra = zPageName;
99 cgi_set_parameter_nocopy("name", g.zExtra);
100 g.isHome = 1;
@@ -147,14 +155,14 @@
155 if( g.okNewWiki ){
156 @ <li> Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
157 }
158 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
159 @ available on this server.</li>
160 @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
161 @ Search wiki titles: <input type="text" name="title"/>
162 @ &nbsp; <input type="submit" /></div></form>
163 @ </li>
164 @ </ul>
165 style_footer();
166 return;
167 }
168 if( check_name(zPageName) ) return;
@@ -186,11 +194,11 @@
194 style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
195 g.zTop, zPageName);
196 }
197 if( rid && g.okApndWiki && g.okAttach ){
198 style_submenu_element("Attach", "Add An Attachment",
199 "%s/attachadd?page=%T&amp;from=%s/wiki%%3fname=%T",
200 g.zTop, zPageName, g.zTop, zPageName);
201 }
202 if( rid && g.okApndWiki ){
203 style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
204 g.zTop, zPageName);
@@ -218,19 +226,21 @@
226 if( cnt==0 ){
227 @ <hr><h2>Attachments:</h2>
228 @ <ul>
229 }
230 cnt++;
231 if( g.okHistory && g.okRead ){
 
 
232 @ <li>
233 @ <a href="%s(g.zTop)/attachview?page=%s(zPageName)&amp;file=%t(zFile)">
234 @ %h(zFile)</a>
235 }else{
236 @ <li>%h(zFile)
237 }
238 @ added by %h(zUser) on
239 hyperlink_to_date(zDate, ".");
240 if( g.okWrWiki && g.okAttach ){
241 @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
242 }
243 }
244 if( cnt ){
245 @ </ul>
246 }
@@ -342,30 +352,30 @@
352 zHtmlPageName = mprintf("Edit: %s", zPageName);
353 style_header(zHtmlPageName);
354 if( P("preview")!=0 ){
355 blob_zero(&wiki);
356 blob_append(&wiki, zBody, -1);
357 @ Preview:<hr />
358 wiki_convert(&wiki, 0, 0);
359 @ <hr />
360 blob_reset(&wiki);
361 }
362 for(n=2, z=zBody; z[0]; z++){
363 if( z[0]=='\n' ) n++;
364 }
365 if( n<20 ) n = 20;
366 if( n>40 ) n = 40;
367 @ <form method="post" action="%s(g.zBaseURL)/wikiedit"><div>
368 login_insert_csrf_secret();
369 @ <input type="hidden" name="name" value="%h(zPageName)" />
370 @ <textarea name="w" class="wikiedit" cols="80"
371 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
372 @ <br />
373 @ <input type="submit" name="preview" value="Preview Your Changes" />
374 @ <input type="submit" name="submit" value="Apply These Changes" />
375 @ <input type="submit" name="cancel" value="Cancel" />
376 @ </div></form>
377 if( !isSandbox ){
378 manifest_clear(&m);
379 }
380 style_footer();
381 }
@@ -387,21 +397,20 @@
397 zName = PD("name","");
398 if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
399 cgi_redirectf("wikiedit?name=%T", zName);
400 }
401 style_header("Create A New Wiki Page");
402 @ <p>Rules for wiki page names:</p>
403 well_formed_wiki_name_rules();
404 @ <form method="post" action="%s(g.zBaseURL)/wikinew">
 
405 @ <p>Name of new wiki page:
406 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
407 @ <input type="submit" value="Create" />
408 @ </p></form>
409 if( zName[0] ){
410 @ <p><span class="wikiError">
411 @ "%h(zName)" is not a valid wiki page name!</span></p>
412 }
413 style_footer();
414 }
415
416
@@ -528,15 +537,15 @@
537 zUser = PD("u", g.zLogin);
538 @ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
539 login_insert_csrf_secret();
540 @ <input type="hidden" name="name" value="%h(zPageName)">
541 @ Your Name:
542 @ <input type="text" name="u" size="20" value="%h(zUser)"><br />
543 @ Comment to append:<br />
544 @ <textarea name="r" class="wikiedit" cols="80"
545 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
546 @ <br />
547 @ <input type="submit" name="preview" value="Preview Your Comment">
548 @ <input type="submit" name="submit" value="Append Your Changes">
549 @ <input type="submit" name="cancel" value="Cancel">
550 @ </form>
551 style_footer();
@@ -551,11 +560,11 @@
560 ** Function called to output extra text at the end of each line in
561 ** a wiki history listing.
562 */
563 static void wiki_history_extra(int rid){
564 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
565 @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&amp;a=%d(rid)">[diff]</a>
566 }
567 }
568
569 /*
570 ** WEBPAGE: whistory
@@ -634,11 +643,11 @@
643 if( m2.type==CFTYPE_WIKI ){
644 blob_init(&w2, m2.zWiki, -1);
645 }
646 }
647 blob_zero(&d);
648 text_diff(&w2, &w1, &d, 5, 1);
649 @ <pre>
650 @ %h(blob_str(&d))
651 @ </pre>
652 style_footer();
653 }
@@ -731,39 +740,39 @@
740 @ </ol>
741 @ <p>We call the first five rules above "wiki" formatting rules. The
742 @ last two rules are the HTML formatting rule.</p>
743 @ <h2>Formatting Rule Details</h2>
744 @ <ol>
745 @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
746 @ a paragraph break. Centered or right-justified paragraphs are not
747 @ supported by wiki markup, but you can do these things if you need them
748 @ using HTML.</p></li>
749 @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
750 @ A bullet list item is a line that begins with a single "*" character
751 @ surrounded on
752 @ both sides by two or more spaces or by a tab. Only a single level
753 @ of bullet list is supported by wiki. For nested lists, use HTML.</p></li>
754 @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>.
755 @ An enumeration list item is a line that begins with a single "#" character
756 @ surrounded on both sides by two or more spaces or by a tab. Only a single
757 @ level of enumeration list is supported by wiki. For nested lists or for
758 @ enumerations that count using letters or roman numerials, use HTML.</p></li>
759 @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>.
760 @ Any paragraph that begins with two or more spaces or a tab and
761 @ which is not a bullet or enumeration list item is rendered
762 @ indented. Only a single level of indentation is supported by wiki; use
763 @ HTML for deeper indentation.</p></li>
764 @ <li> <p><span class="wikiruleHead">Hyperlinks</span>.
765 @ Text within square brackets ("[...]") becomes a hyperlink. The
766 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
767 @ the name of an image, or a URL. By default, the target is displayed
768 @ as the text of the hyperlink. But you can specify alternative text
769 @ after the target name separated by a "|" character.</p>
770 @ <p>You can also link to internal anchor names using [#anchor-name], providing
771 @ you have added the necessary "&lt;a name="anchor-name"&gt;&lt;/a&gt;"
772 @ tag to your wiki page.</p></li>
773 @ <li> <p><span class="wikiruleHead">HTML</span>.
774 @ The following standard HTML elements may be used:
775 @ &lt;a&gt;
776 @ &lt;address&gt;
777 @ &lt;b&gt;
778 @ &lt;big&gt;
@@ -813,16 +822,16 @@
822 @ &lt;verbatim&gt; and &lt;nowiki&gt;.
823 @ No other elements are allowed. All attributes are checked and
824 @ only a few benign attributes are allowed on each element.
825 @ In particular, any attributes that specify javascript or CSS
826 @ are elided.</p></li>
827 @ <li><p><span class="wikiruleHead">Special Markup.</span>
828 @ The &lt;nowiki&gt; tag disables all wiki formatting rules
829 @ through the matching &lt;/nowiki&gt; element.
830 @ The &lt;verbatim&gt; tag works like &lt;pre&gt; with the addition
831 @ that it also disables all wiki and HTML markup
832 @ through the matching &lt;/verbatim&gt;.</p></li>
833 @ </ol>
834 style_footer();
835 }
836
837 /*
838
+47 -21
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -321,21 +321,21 @@
321321
}
322322
323323
/*
324324
** Token types
325325
*/
326
-#define TOKEN_MARKUP 1 /* <...> */
327
-#define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */
328
-#define TOKEN_LINK 3 /* [...] */
329
-#define TOKEN_PARAGRAPH 4 /* blank lines */
330
-#define TOKEN_NEWLINE 5 /* A single "\n" */
331
-#define TOKEN_BUL_LI 6 /* " * " */
332
-#define TOKEN_NUM_LI 7 /* " # " */
333
-#define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */
334
-#define TOKEN_INDENT 9 /* " " */
335
-#define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
336
-#define TOKEN_TEXT 11 /* None of the above */
326
+#define TOKEN_MARKUP 1 /* <...> */
327
+#define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */
328
+#define TOKEN_LINK 3 /* [...] */
329
+#define TOKEN_PARAGRAPH 4 /* blank lines */
330
+#define TOKEN_NEWLINE 5 /* A single "\n" */
331
+#define TOKEN_BUL_LI 6 /* " * " */
332
+#define TOKEN_NUM_LI 7 /* " # " */
333
+#define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */
334
+#define TOKEN_INDENT 9 /* " " */
335
+#define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
336
+#define TOKEN_TEXT 11 /* None of the above */
337337
338338
/*
339339
** State flags
340340
*/
341341
#define AT_NEWLINE 0x001 /* At start of a line */
@@ -745,10 +745,13 @@
745745
static void renderMarkup(Blob *pOut, ParsedMarkup *p){
746746
int i;
747747
if( p->endTag ){
748748
blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
749749
}else{
750
+ /* Close active paragraph for several elements, which are not allowed
751
+ ** in paragraphs in XHTML.
752
+ */
750753
blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
751754
for(i=0; i<p->nAttr; i++){
752755
blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
753756
if( p->aAttr[i].zValue ){
754757
const char *zVal = p->aAttr[i].zValue;
@@ -756,10 +759,13 @@
756759
blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
757760
}else{
758761
blob_appendf(pOut, "=\"%s\"", zVal);
759762
}
760763
}
764
+ }
765
+ if (p->iType & MUTYPE_SINGLE){
766
+ blob_append(pOut, " /", 2);
761767
}
762768
blob_append(pOut, ">", 1);
763769
}
764770
}
765771
@@ -885,12 +891,13 @@
885891
886892
/*
887893
** Begin a new paragraph if that something that is needed.
888894
*/
889895
static void startAutoParagraph(Renderer *p){
890
- if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
891
- blob_appendf(p->pOut, "<p>", -1);
896
+ if( p->wantAutoParagraph==0 ) return;
897
+ if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
898
+ blob_appendf(p->pOut, "<p type=\"auto\">", -1);
892899
pushStack(p, MARKUP_P);
893900
p->wantAutoParagraph = 0;
894901
p->inAutoParagraph = 1;
895902
}
896903
@@ -1019,17 +1026,18 @@
10191026
/* Special display processing for tickets. Display the hyperlink
10201027
** as crossed out if the ticket is closed.
10211028
*/
10221029
if( isClosed ){
10231030
if( g.okHistory ){
1024
- blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
1025
- g.zBaseURL, zTarget
1031
+ blob_appendf(p->pOut,
1032
+ "<a href=\"%s/info/%s\"><span class=\"wikiTagCancelled\">",
1033
+ g.zBaseURL, zTarget
10261034
);
1027
- zTerm = "</s></a>";
1035
+ zTerm = "</span></a>";
10281036
}else{
1029
- blob_appendf(p->pOut,"<s>");
1030
- zTerm = "</s>";
1037
+ blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">");
1038
+ zTerm = "</span>";
10311039
}
10321040
}else{
10331041
if( g.okHistory ){
10341042
blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
10351043
g.zBaseURL, zTarget
@@ -1133,10 +1141,11 @@
11331141
}else{
11341142
if( p->wikiList!=MARKUP_UL ){
11351143
if( p->wikiList ){
11361144
popStackToTag(p, p->wikiList);
11371145
}
1146
+ endAutoParagraph(p);
11381147
pushStack(p, MARKUP_UL);
11391148
blob_append(p->pOut, "<ul>", 4);
11401149
p->wikiList = MARKUP_UL;
11411150
}
11421151
popStackToTag(p, MARKUP_LI);
@@ -1152,10 +1161,11 @@
11521161
}else{
11531162
if( p->wikiList!=MARKUP_OL ){
11541163
if( p->wikiList ){
11551164
popStackToTag(p, p->wikiList);
11561165
}
1166
+ endAutoParagraph(p);
11571167
pushStack(p, MARKUP_OL);
11581168
blob_append(p->pOut, "<ol>", 4);
11591169
p->wikiList = MARKUP_OL;
11601170
}
11611171
popStackToTag(p, MARKUP_LI);
@@ -1171,10 +1181,11 @@
11711181
}else{
11721182
if( p->wikiList!=MARKUP_OL ){
11731183
if( p->wikiList ){
11741184
popStackToTag(p, p->wikiList);
11751185
}
1186
+ endAutoParagraph(p);
11761187
pushStack(p, MARKUP_OL);
11771188
blob_append(p->pOut, "<ol>", 4);
11781189
p->wikiList = MARKUP_OL;
11791190
}
11801191
popStackToTag(p, MARKUP_LI);
@@ -1233,11 +1244,13 @@
12331244
p->state = savedState;
12341245
blob_append(p->pOut, zClose, -1);
12351246
break;
12361247
}
12371248
case TOKEN_TEXT: {
1238
- startAutoParagraph(p);
1249
+ int i;
1250
+ for(i=0; i<n && isspace(z[i]); i++){}
1251
+ if( i<n ) startAutoParagraph(p);
12391252
blob_append(p->pOut, z, n);
12401253
break;
12411254
}
12421255
case TOKEN_RAW: {
12431256
blob_append(p->pOut, z, n);
@@ -1347,16 +1360,19 @@
13471360
blob_appendf(p->pOut, "<pre name='code' class='%s'>",
13481361
markup.aAttr[vAttrIdx].zValue);
13491362
vAttrDidAppend=1;
13501363
}
13511364
}
1352
- if( !vAttrDidAppend )
1365
+ if( !vAttrDidAppend ) {
1366
+ endAutoParagraph(p);
13531367
blob_append(p->pOut, "<pre class='verbatim'>",-1);
1368
+ }
13541369
p->wantAutoParagraph = 0;
13551370
}else
13561371
if( markup.iType==MUTYPE_LI ){
13571372
if( backupToType(p, MUTYPE_LIST)==0 ){
1373
+ endAutoParagraph(p);
13581374
pushStack(p, MARKUP_UL);
13591375
blob_append(p->pOut, "<ul>", 4);
13601376
}
13611377
pushStack(p, MARKUP_LI);
13621378
renderMarkup(p->pOut, &markup);
@@ -1384,12 +1400,22 @@
13841400
pushStack(p, markup.iCode);
13851401
}else
13861402
{
13871403
if( markup.iType==MUTYPE_FONT ){
13881404
startAutoParagraph(p);
1389
- }else if( markup.iType==MUTYPE_BLOCK ){
1405
+ }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
13901406
p->wantAutoParagraph = 0;
1407
+ }
1408
+ if( markup.iCode==MARKUP_HR
1409
+ || markup.iCode==MARKUP_H1
1410
+ || markup.iCode==MARKUP_H2
1411
+ || markup.iCode==MARKUP_H3
1412
+ || markup.iCode==MARKUP_H4
1413
+ || markup.iCode==MARKUP_H5
1414
+ || markup.iCode==MARKUP_P
1415
+ ){
1416
+ endAutoParagraph(p);
13911417
}
13921418
if( (markup.iType & MUTYPE_STACK )!=0 ){
13931419
pushStack(p, markup.iCode);
13941420
}
13951421
renderMarkup(p->pOut, &markup);
13961422
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -321,21 +321,21 @@
321 }
322
323 /*
324 ** Token types
325 */
326 #define TOKEN_MARKUP 1 /* <...> */
327 #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */
328 #define TOKEN_LINK 3 /* [...] */
329 #define TOKEN_PARAGRAPH 4 /* blank lines */
330 #define TOKEN_NEWLINE 5 /* A single "\n" */
331 #define TOKEN_BUL_LI 6 /* " * " */
332 #define TOKEN_NUM_LI 7 /* " # " */
333 #define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */
334 #define TOKEN_INDENT 9 /* " " */
335 #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
336 #define TOKEN_TEXT 11 /* None of the above */
337
338 /*
339 ** State flags
340 */
341 #define AT_NEWLINE 0x001 /* At start of a line */
@@ -745,10 +745,13 @@
745 static void renderMarkup(Blob *pOut, ParsedMarkup *p){
746 int i;
747 if( p->endTag ){
748 blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
749 }else{
 
 
 
750 blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
751 for(i=0; i<p->nAttr; i++){
752 blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
753 if( p->aAttr[i].zValue ){
754 const char *zVal = p->aAttr[i].zValue;
@@ -756,10 +759,13 @@
756 blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
757 }else{
758 blob_appendf(pOut, "=\"%s\"", zVal);
759 }
760 }
 
 
 
761 }
762 blob_append(pOut, ">", 1);
763 }
764 }
765
@@ -885,12 +891,13 @@
885
886 /*
887 ** Begin a new paragraph if that something that is needed.
888 */
889 static void startAutoParagraph(Renderer *p){
890 if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
891 blob_appendf(p->pOut, "<p>", -1);
 
892 pushStack(p, MARKUP_P);
893 p->wantAutoParagraph = 0;
894 p->inAutoParagraph = 1;
895 }
896
@@ -1019,17 +1026,18 @@
1019 /* Special display processing for tickets. Display the hyperlink
1020 ** as crossed out if the ticket is closed.
1021 */
1022 if( isClosed ){
1023 if( g.okHistory ){
1024 blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
1025 g.zBaseURL, zTarget
 
1026 );
1027 zTerm = "</s></a>";
1028 }else{
1029 blob_appendf(p->pOut,"<s>");
1030 zTerm = "</s>";
1031 }
1032 }else{
1033 if( g.okHistory ){
1034 blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
1035 g.zBaseURL, zTarget
@@ -1133,10 +1141,11 @@
1133 }else{
1134 if( p->wikiList!=MARKUP_UL ){
1135 if( p->wikiList ){
1136 popStackToTag(p, p->wikiList);
1137 }
 
1138 pushStack(p, MARKUP_UL);
1139 blob_append(p->pOut, "<ul>", 4);
1140 p->wikiList = MARKUP_UL;
1141 }
1142 popStackToTag(p, MARKUP_LI);
@@ -1152,10 +1161,11 @@
1152 }else{
1153 if( p->wikiList!=MARKUP_OL ){
1154 if( p->wikiList ){
1155 popStackToTag(p, p->wikiList);
1156 }
 
1157 pushStack(p, MARKUP_OL);
1158 blob_append(p->pOut, "<ol>", 4);
1159 p->wikiList = MARKUP_OL;
1160 }
1161 popStackToTag(p, MARKUP_LI);
@@ -1171,10 +1181,11 @@
1171 }else{
1172 if( p->wikiList!=MARKUP_OL ){
1173 if( p->wikiList ){
1174 popStackToTag(p, p->wikiList);
1175 }
 
1176 pushStack(p, MARKUP_OL);
1177 blob_append(p->pOut, "<ol>", 4);
1178 p->wikiList = MARKUP_OL;
1179 }
1180 popStackToTag(p, MARKUP_LI);
@@ -1233,11 +1244,13 @@
1233 p->state = savedState;
1234 blob_append(p->pOut, zClose, -1);
1235 break;
1236 }
1237 case TOKEN_TEXT: {
1238 startAutoParagraph(p);
 
 
1239 blob_append(p->pOut, z, n);
1240 break;
1241 }
1242 case TOKEN_RAW: {
1243 blob_append(p->pOut, z, n);
@@ -1347,16 +1360,19 @@
1347 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1348 markup.aAttr[vAttrIdx].zValue);
1349 vAttrDidAppend=1;
1350 }
1351 }
1352 if( !vAttrDidAppend )
 
1353 blob_append(p->pOut, "<pre class='verbatim'>",-1);
 
1354 p->wantAutoParagraph = 0;
1355 }else
1356 if( markup.iType==MUTYPE_LI ){
1357 if( backupToType(p, MUTYPE_LIST)==0 ){
 
1358 pushStack(p, MARKUP_UL);
1359 blob_append(p->pOut, "<ul>", 4);
1360 }
1361 pushStack(p, MARKUP_LI);
1362 renderMarkup(p->pOut, &markup);
@@ -1384,12 +1400,22 @@
1384 pushStack(p, markup.iCode);
1385 }else
1386 {
1387 if( markup.iType==MUTYPE_FONT ){
1388 startAutoParagraph(p);
1389 }else if( markup.iType==MUTYPE_BLOCK ){
1390 p->wantAutoParagraph = 0;
 
 
 
 
 
 
 
 
 
 
1391 }
1392 if( (markup.iType & MUTYPE_STACK )!=0 ){
1393 pushStack(p, markup.iCode);
1394 }
1395 renderMarkup(p->pOut, &markup);
1396
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -321,21 +321,21 @@
321 }
322
323 /*
324 ** Token types
325 */
326 #define TOKEN_MARKUP 1 /* <...> */
327 #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */
328 #define TOKEN_LINK 3 /* [...] */
329 #define TOKEN_PARAGRAPH 4 /* blank lines */
330 #define TOKEN_NEWLINE 5 /* A single "\n" */
331 #define TOKEN_BUL_LI 6 /* " * " */
332 #define TOKEN_NUM_LI 7 /* " # " */
333 #define TOKEN_ENUM 8 /* " \(?\d+[.)]? " */
334 #define TOKEN_INDENT 9 /* " " */
335 #define TOKEN_RAW 10 /* Output exactly (used when wiki-use-html==1) */
336 #define TOKEN_TEXT 11 /* None of the above */
337
338 /*
339 ** State flags
340 */
341 #define AT_NEWLINE 0x001 /* At start of a line */
@@ -745,10 +745,13 @@
745 static void renderMarkup(Blob *pOut, ParsedMarkup *p){
746 int i;
747 if( p->endTag ){
748 blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
749 }else{
750 /* Close active paragraph for several elements, which are not allowed
751 ** in paragraphs in XHTML.
752 */
753 blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
754 for(i=0; i<p->nAttr; i++){
755 blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
756 if( p->aAttr[i].zValue ){
757 const char *zVal = p->aAttr[i].zValue;
@@ -756,10 +759,13 @@
759 blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
760 }else{
761 blob_appendf(pOut, "=\"%s\"", zVal);
762 }
763 }
764 }
765 if (p->iType & MUTYPE_SINGLE){
766 blob_append(pOut, " /", 2);
767 }
768 blob_append(pOut, ">", 1);
769 }
770 }
771
@@ -885,12 +891,13 @@
891
892 /*
893 ** Begin a new paragraph if that something that is needed.
894 */
895 static void startAutoParagraph(Renderer *p){
896 if( p->wantAutoParagraph==0 ) return;
897 if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
898 blob_appendf(p->pOut, "<p type=\"auto\">", -1);
899 pushStack(p, MARKUP_P);
900 p->wantAutoParagraph = 0;
901 p->inAutoParagraph = 1;
902 }
903
@@ -1019,17 +1026,18 @@
1026 /* Special display processing for tickets. Display the hyperlink
1027 ** as crossed out if the ticket is closed.
1028 */
1029 if( isClosed ){
1030 if( g.okHistory ){
1031 blob_appendf(p->pOut,
1032 "<a href=\"%s/info/%s\"><span class=\"wikiTagCancelled\">",
1033 g.zBaseURL, zTarget
1034 );
1035 zTerm = "</span></a>";
1036 }else{
1037 blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">");
1038 zTerm = "</span>";
1039 }
1040 }else{
1041 if( g.okHistory ){
1042 blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
1043 g.zBaseURL, zTarget
@@ -1133,10 +1141,11 @@
1141 }else{
1142 if( p->wikiList!=MARKUP_UL ){
1143 if( p->wikiList ){
1144 popStackToTag(p, p->wikiList);
1145 }
1146 endAutoParagraph(p);
1147 pushStack(p, MARKUP_UL);
1148 blob_append(p->pOut, "<ul>", 4);
1149 p->wikiList = MARKUP_UL;
1150 }
1151 popStackToTag(p, MARKUP_LI);
@@ -1152,10 +1161,11 @@
1161 }else{
1162 if( p->wikiList!=MARKUP_OL ){
1163 if( p->wikiList ){
1164 popStackToTag(p, p->wikiList);
1165 }
1166 endAutoParagraph(p);
1167 pushStack(p, MARKUP_OL);
1168 blob_append(p->pOut, "<ol>", 4);
1169 p->wikiList = MARKUP_OL;
1170 }
1171 popStackToTag(p, MARKUP_LI);
@@ -1171,10 +1181,11 @@
1181 }else{
1182 if( p->wikiList!=MARKUP_OL ){
1183 if( p->wikiList ){
1184 popStackToTag(p, p->wikiList);
1185 }
1186 endAutoParagraph(p);
1187 pushStack(p, MARKUP_OL);
1188 blob_append(p->pOut, "<ol>", 4);
1189 p->wikiList = MARKUP_OL;
1190 }
1191 popStackToTag(p, MARKUP_LI);
@@ -1233,11 +1244,13 @@
1244 p->state = savedState;
1245 blob_append(p->pOut, zClose, -1);
1246 break;
1247 }
1248 case TOKEN_TEXT: {
1249 int i;
1250 for(i=0; i<n && isspace(z[i]); i++){}
1251 if( i<n ) startAutoParagraph(p);
1252 blob_append(p->pOut, z, n);
1253 break;
1254 }
1255 case TOKEN_RAW: {
1256 blob_append(p->pOut, z, n);
@@ -1347,16 +1360,19 @@
1360 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1361 markup.aAttr[vAttrIdx].zValue);
1362 vAttrDidAppend=1;
1363 }
1364 }
1365 if( !vAttrDidAppend ) {
1366 endAutoParagraph(p);
1367 blob_append(p->pOut, "<pre class='verbatim'>",-1);
1368 }
1369 p->wantAutoParagraph = 0;
1370 }else
1371 if( markup.iType==MUTYPE_LI ){
1372 if( backupToType(p, MUTYPE_LIST)==0 ){
1373 endAutoParagraph(p);
1374 pushStack(p, MARKUP_UL);
1375 blob_append(p->pOut, "<ul>", 4);
1376 }
1377 pushStack(p, MARKUP_LI);
1378 renderMarkup(p->pOut, &markup);
@@ -1384,12 +1400,22 @@
1400 pushStack(p, markup.iCode);
1401 }else
1402 {
1403 if( markup.iType==MUTYPE_FONT ){
1404 startAutoParagraph(p);
1405 }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
1406 p->wantAutoParagraph = 0;
1407 }
1408 if( markup.iCode==MARKUP_HR
1409 || markup.iCode==MARKUP_H1
1410 || markup.iCode==MARKUP_H2
1411 || markup.iCode==MARKUP_H3
1412 || markup.iCode==MARKUP_H4
1413 || markup.iCode==MARKUP_H5
1414 || markup.iCode==MARKUP_P
1415 ){
1416 endAutoParagraph(p);
1417 }
1418 if( (markup.iType & MUTYPE_STACK )!=0 ){
1419 pushStack(p, markup.iCode);
1420 }
1421 renderMarkup(p->pOut, &markup);
1422
+10 -4
--- src/winhttp.c
+++ src/winhttp.c
@@ -16,12 +16,13 @@
1616
*******************************************************************************
1717
**
1818
** This file implements a very simple (and low-performance) HTTP server
1919
** for windows.
2020
*/
21
-#ifdef __MINGW32__ /* This code is for win32 only */
2221
#include "config.h"
22
+#ifdef _WIN32
23
+/* This code is for win32 only */
2324
#include "winhttp.h"
2425
#include <windows.h>
2526
2627
/*
2728
** The HttpRequest structure holds information about each incoming
@@ -133,11 +134,12 @@
133134
*/
134135
void win32_http_server(
135136
int mnPort, int mxPort, /* Range of allowed TCP port numbers */
136137
const char *zBrowser, /* Command to launch browser. (Or NULL) */
137138
const char *zStopper, /* Stop server when this file is exists (Or NULL) */
138
- const char *zNotFound /* The --notfound option, or NULL */
139
+ const char *zNotFound, /* The --notfound option, or NULL */
140
+ int flags /* One or more HTTP_SERVER_ flags */
139141
){
140142
WSADATA wd;
141143
SOCKET s = INVALID_SOCKET;
142144
SOCKADDR_IN addr;
143145
int idCnt = 0;
@@ -158,11 +160,15 @@
158160
if( s==INVALID_SOCKET ){
159161
fossil_fatal("unable to create a socket");
160162
}
161163
addr.sin_family = AF_INET;
162164
addr.sin_port = htons(iPort);
163
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
165
+ if( flags & HTTP_SERVER_LOCALHOST ){
166
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
167
+ }else{
168
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
169
+ }
164170
if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
165171
closesocket(s);
166172
iPort++;
167173
continue;
168174
}
@@ -215,6 +221,6 @@
215221
}
216222
closesocket(s);
217223
WSACleanup();
218224
}
219225
220
-#endif /* __MINGW32__ -- This code is for win32 only */
226
+#endif /* _WIN32 -- This code is for win32 only */
221227
--- src/winhttp.c
+++ src/winhttp.c
@@ -16,12 +16,13 @@
16 *******************************************************************************
17 **
18 ** This file implements a very simple (and low-performance) HTTP server
19 ** for windows.
20 */
21 #ifdef __MINGW32__ /* This code is for win32 only */
22 #include "config.h"
 
 
23 #include "winhttp.h"
24 #include <windows.h>
25
26 /*
27 ** The HttpRequest structure holds information about each incoming
@@ -133,11 +134,12 @@
133 */
134 void win32_http_server(
135 int mnPort, int mxPort, /* Range of allowed TCP port numbers */
136 const char *zBrowser, /* Command to launch browser. (Or NULL) */
137 const char *zStopper, /* Stop server when this file is exists (Or NULL) */
138 const char *zNotFound /* The --notfound option, or NULL */
 
139 ){
140 WSADATA wd;
141 SOCKET s = INVALID_SOCKET;
142 SOCKADDR_IN addr;
143 int idCnt = 0;
@@ -158,11 +160,15 @@
158 if( s==INVALID_SOCKET ){
159 fossil_fatal("unable to create a socket");
160 }
161 addr.sin_family = AF_INET;
162 addr.sin_port = htons(iPort);
163 addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
 
 
 
164 if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
165 closesocket(s);
166 iPort++;
167 continue;
168 }
@@ -215,6 +221,6 @@
215 }
216 closesocket(s);
217 WSACleanup();
218 }
219
220 #endif /* __MINGW32__ -- This code is for win32 only */
221
--- src/winhttp.c
+++ src/winhttp.c
@@ -16,12 +16,13 @@
16 *******************************************************************************
17 **
18 ** This file implements a very simple (and low-performance) HTTP server
19 ** for windows.
20 */
 
21 #include "config.h"
22 #ifdef _WIN32
23 /* This code is for win32 only */
24 #include "winhttp.h"
25 #include <windows.h>
26
27 /*
28 ** The HttpRequest structure holds information about each incoming
@@ -133,11 +134,12 @@
134 */
135 void win32_http_server(
136 int mnPort, int mxPort, /* Range of allowed TCP port numbers */
137 const char *zBrowser, /* Command to launch browser. (Or NULL) */
138 const char *zStopper, /* Stop server when this file is exists (Or NULL) */
139 const char *zNotFound, /* The --notfound option, or NULL */
140 int flags /* One or more HTTP_SERVER_ flags */
141 ){
142 WSADATA wd;
143 SOCKET s = INVALID_SOCKET;
144 SOCKADDR_IN addr;
145 int idCnt = 0;
@@ -158,11 +160,15 @@
160 if( s==INVALID_SOCKET ){
161 fossil_fatal("unable to create a socket");
162 }
163 addr.sin_family = AF_INET;
164 addr.sin_port = htons(iPort);
165 if( flags & HTTP_SERVER_LOCALHOST ){
166 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
167 }else{
168 addr.sin_addr.s_addr = htonl(INADDR_ANY);
169 }
170 if( bind(s, (struct sockaddr*)&addr, sizeof(addr))==SOCKET_ERROR ){
171 closesocket(s);
172 iPort++;
173 continue;
174 }
@@ -215,6 +221,6 @@
221 }
222 closesocket(s);
223 WSACleanup();
224 }
225
226 #endif /* _WIN32 -- This code is for win32 only */
227
+1
--- src/xfer.c
+++ src/xfer.c
@@ -986,10 +986,11 @@
986986
if( pushFlag ){
987987
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
988988
nCardSent++;
989989
}
990990
manifest_crosslink_begin();
991
+ transport_global_startup();
991992
fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
992993
993994
while( go ){
994995
int newPhantom = 0;
995996
char *zRandomness;
996997
997998
ADDED win/Makefile.dmc
998999
ADDED win/Makefile.msc
9991000
ADDED win/include/dirent.h
10001001
ADDED win/include/unistd.h
10011002
ADDED win/version.c
--- src/xfer.c
+++ src/xfer.c
@@ -986,10 +986,11 @@
986 if( pushFlag ){
987 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
988 nCardSent++;
989 }
990 manifest_crosslink_begin();
 
991 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
992
993 while( go ){
994 int newPhantom = 0;
995 char *zRandomness;
996
997 DDED win/Makefile.dmc
998 DDED win/Makefile.msc
999 DDED win/include/dirent.h
1000 DDED win/include/unistd.h
1001 DDED win/version.c
--- src/xfer.c
+++ src/xfer.c
@@ -986,10 +986,11 @@
986 if( pushFlag ){
987 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
988 nCardSent++;
989 }
990 manifest_crosslink_begin();
991 transport_global_startup();
992 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
993
994 while( go ){
995 int newPhantom = 0;
996 char *zRandomness;
997
998 DDED win/Makefile.dmc
999 DDED win/Makefile.msc
1000 DDED win/include/dirent.h
1001 DDED win/include/unistd.h
1002 DDED win/version.c
--- a/win/Makefile.dmc
+++ b/win/Makefile.dmc
@@ -0,0 +1,550 @@
1
+# DO NOT EDITrver_.c:server#
2
+### $(SQLITE_OPTIONS) $**
3
+
4
+**
5
+
6
+ENABLE_STAT3rverrverrverrver$O
7
+# "tclsh src/makemakeserverserver_.c
8
+
9
+serverservrver_.c:server**
10
+
11
+**
12
+
13
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
14
+
15
+serverservrver_.c:servertag_.tag$O S) $**
16
+
17
+**
18
+
19
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
20
+### $(SQLITE_OPTIONS) $**
21
+
22
+**
23
+
24
+ENABLE_STAT3rverrverrverrver$O : serv#
25
+### $(SQLITE_OPTIONS) $**
26
+
27
+**
28
+
29
+ENABLE_STAT3rverrver
30
+
31
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
32
+
33
+4_.tag$O S) $**
34
+
35
+**
36
+
37
+EN4ertaserver_.c serverserver_.c
38
+
39
+serverservrver_.c:server#
40
+### $(SQLITE_OPTIONS) $**
41
+
42
+**
43
+
44
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
45
+
46
+serverservrver_.c:server#
47
+### $(SQLITE_OPTIONS) $**
48
+
49
+**
50
+
51
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
52
+
53
+serverservrver_.c:serververify$O : server_.c serve#
54
+### $(SQLITE_OPTIONS) $**
55
+
56
+**
57
+
58
+ENABLE_STAT3rverrverrverrvever_.c:server#
59
+### $(SQLITE_OPTIONS) $**
60
+
61
+**
62
+
63
+ENABLE_STAT3rverrverrverrver$O
64
+# "tclsh src/makemakeserverser_.c:server#
65
+### $(SQLITE_OPTIONS) $**
66
+
67
+**
68
+
69
+ENABLE_STAT3rverrverrverrver$O
70
+# "tclsh src/makemaserver**
71
+
72
+**
73
+
74
+ENABLE_STAT3rverrverrverrver$O : server_.c serversNABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
75
+### $(SQLITE_OPTIONS) $**
76
+
77
+**
78
+
79
+ENABLE_STAT3rverrverrverrver$O : serv#
80
+### $(SQLITE_OPTIONS) $**
81
+
82
+**
83
+
84
+ENABLE_STAT3rverrver
85
+
86
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
87
+
88
+4_.tag$O S) $**
89
+
90
+**
91
+
92
+EN4ertaserver_.c serverserver_.c
93
+
94
+serverservrver_.c:server#
95
+### $(SQLITE_OPTIONS) $**
96
+
97
+**
98
+
99
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
100
+
101
+serverservrver_.c:server#
102
+### $(SQLITE_OPTIONS) $**
103
+
104
+**
105
+
106
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
107
+
108
+serverservrver_.c:serververify$O : server_.c serve#
109
+### STAT3rverrverrverrvever_.c:server#
110
+### $(SQLITE_OPTIONS) $**
111
+
112
+**
113
+
114
+ENABLE_STAT3rverrverrverrver$O
115
+# "tclsh src/makemakeserverserver_.c
116
+
117
+serverservrver_.c:server**
118
+
119
+**
120
+
121
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
122
+
123
+serverservrver_.c:servertag_.tag$O S) $**
124
+
125
+**
126
+
127
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
128
+### $(SQLITE_OPTIONS) $**
129
+
130
+**
131
+
132
+ENABLE_STAT3rverrverrverrver$O : serv#
133
+### $(SQLITE_OPTIONS) $**
134
+
135
+**
136
+
137
+ENABLE_STAT3rverrver
138
+
139
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
140
+
141
+4_.tag$O S) $**
142
+
143
+**
144
+
145
+EN4ertaserver_.c serverserver_.c
146
+
147
+serverservrver_.c:server#
148
+### $(SQLITE_OPTIONS) $**
149
+
150
+**
151
+
152
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
153
+
154
+serverservrver_.c:server#
155
+### $(SQLITE_OPTIONS) $**
156
+
157
+**
158
+
159
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
160
+
161
+serverservrver_.c:serververify$O : server_.c serve#
162
+### $(SQLITE_OPTIONS) $**
163
+
164
+**
165
+
166
+ENABLE_STAT3rverrverrverrver$O : server# DO NOT EDITrver_.c:server#
167
+### $(SQLITE_OPTIONS) $**
168
+
169
+**
170
+
171
+ENABLE_STAT3rverrverrverrver$O
172
+# "tclsh src/makemakeserverserver_.c
173
+
174
+serverservrver_.c:server**
175
+
176
+**
177
+
178
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
179
+
180
+serverservrver_.c:servertag_.tag$O S) $**
181
+
182
+**
183
+
184
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
185
+### $(SQLITE_OPTIONS) $**
186
+
187
+**
188
+
189
+ENABLE_STAT3rverrverrverrver$O : serv#
190
+### $(SQLITE_OPTIONS) $**
191
+
192
+**
193
+
194
+ENABLE_STAT3rverrver
195
+
196
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
197
+
198
+4_.tag$O S) $**
199
+
200
+**
201
+
202
+EN4ertaserver_.c serverserver_.c
203
+
204
+serverservrver_.c:server#
205
+### $(SQLITE_OPTIONS) $**
206
+
207
+**
208
+
209
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
210
+
211
+serverservrver_.c:server#
212
+### $(SQLITE_OPTIONS) $**
213
+
214
+**
215
+
216
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
217
+
218
+serverservrver_.c:serververify$O : server_.c serve#
219
+### $(SQLITE_OPTIONS) $**
220
+
221
+**
222
+
223
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
224
+
225
+serverservrver_.c:servertag_qlite3_.c:servertag_.tag$O S) $**
226
+
227
+**th
228
+### $(th
229
+### $(SQLITE_OPfserver#
230
+##:server#
231
+### $(SQLITE_I18N serv#
232
+### $(SQLITE_OPTIONS) $**
233
+
234
+*
235
+
236
+**
237
+
238
+ENABLE_STAT3rverrverrverrver$O : server_.c rver_.c
239
+
240
+serverservrver_**$(SQLITE_OPTIONS) $**
241
+
242
+**
243
+
244
+raph_.c http_.r_.c:servertag_.tag$O ver_.c:server#
245
+### $(SQLITE_OP.c serverserver_.c
246
+
247
+serverservrvererervrver_.c:server#
248
+### $(SQLITE_OPTIONS) $**
249
+
250
+**
251
+
252
+ENABLE_Sc serverserver_.c
253
+
254
+serverservrver_.c:server#
255
+### $(AT3rverrverrverrver$O : server_.c serverserver_.c
256
+
257
+serverservrver_.c:servertag_.tag$O S) $**
258
+
259
+**
260
+
261
+ENABLE_STAT3rverrverrverrver*
262
+
263
+ENABLerrverrver$O : server_.c serverserver_.c
264
+
265
+serverservrver_.c:servertag_.tag$O S) $**
266
+
267
+**
268
+
269
+ENABLE_STAT3rverrverrverrver$Over_.c serverserver_.c
270
+
271
+serverservrver_.c:server#
272
+### $(SQLITE_OPTIONS) $**
273
+
274
+**
275
+
276
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
277
+
278
+serverservrver_.c:server#
279
+### $(SQLITE_OPTIONS) $**
280
+
281
+**
282
+
283
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
284
+
285
+serverservrver_.c:server**
286
+
287
+**
288
+
289
+ENABLE_STAT3rverrverrverrver$O : server_.c serv# DO NOT EDITrver_.c:server#
290
+### $(SQLITE_OPTIONS) $**
291
+
292
+**
293
+
294
+ENABLE_STAT3rverrverrverrver$O
295
+# "tclsh src/makemakeserverserver_.c
296
+
297
+serverservrver_.c:server**
298
+
299
+**
300
+
301
+ENABLE_STAT3rverrverrverrver$O : server_.c serthSTAT3rverrverrver# er$# DO NOT EDITrver_.c:server#
302
+### $(SQLITE_OPTIONS) $**
303
+
304
+**
305
+
306
+ENABLE_STAT3rverrverrverrver$O
307
+# "tclsh src/makemakeserverserver_.c
308
+
309
+serverservrver_.c:server**
310
+
311
+**
312
+
313
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
314
+
315
+serverservrver_.c:servertag_.tag$O S) $**
316
+
317
+**
318
+
319
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
320
+### $(SQLITE_OPTIONS) $**
321
+
322
+**
323
+
324
+ENABLE_STAT3rverrverrverrver$O : serv#
325
+### $(SQLITE_OPTIONS) $**
326
+
327
+**
328
+
329
+ENABLE_STAT3rverrver
330
+
331
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
332
+
333
+4_.tag$O S) $**
334
+
335
+**
336
+
337
+EN4ertaserver_.c serverserver_.c
338
+
339
+serverservrver_.c:server#
340
+### $(SQLITE_OPTIONS) $**
341
+
342
+**
343
+
344
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
345
+
346
+serverservrver_.c:server#
347
+### $(SQLITE_OPTIONS) $**
348
+
349
+**
350
+
351
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
352
+
353
+serverservrver_.c:serververify$O : server_.c serve#
354
+### $(SQLITE_OPTIONS) $**
355
+
356
+**
357
+
358
+ENABLE_STAT3rverrverrverrvever_.c:server#
359
+### $(SQLITE_OPTIONS) $**
360
+
361
+**
362
+
363
+ENABLE_STAT3rverrverrverrver$O
364
+# "tclsh src/makemakeserverserver_.c
365
+
366
+serverservrver_.c:server**
367
+
368
+**
369
+
370
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
371
+
372
+serverservrver_.c:servertag_.tag$O S) $**
373
+
374
+**
375
+
376
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
377
+### $(SQLITE_OPTIONS) $**
378
+
379
+**
380
+
381
+ENABLE_STAT3rverrverrverrver$O : serv#
382
+### $(SQLITE_OPTIONS) $**
383
+
384
+**
385
+
386
+ENABLE_STAT3rverrver
387
+
388
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
389
+
390
+4_.tag$O S) $**
391
+
392
+**
393
+
394
+EN4ertaserver_.c serverserver_.c
395
+
396
+serverservrver_.c:server#
397
+### $(SQLITE_OPTIONS) $**
398
+
399
+**
400
+
401
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
402
+
403
+serverservrver_.c:server#
404
+### $(SQLITE_OPTIONS) $**
405
+
406
+**
407
+
408
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
409
+
410
+serverservrver_.c:serververify$O : server_.c serve#
411
+### $(SQLITE_OPTIONS) $**
412
+
413
+**
414
+
415
+ENABLE_STAT3rverrverrverrver$O : server# DO NOT EDITrver_.c:server#
416
+### $(SQLITE_OPTIONS) $**
417
+
418
+**
419
+
420
+ENABLE_STAT3rverrverrverrver$O
421
+# "tclsh src/makemakeserverserver_.c
422
+
423
+serverservrver_.c:server**
424
+
425
+**
426
+
427
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
428
+
429
+serverservrver_.c:servertag_.tag$O S) $**
430
+
431
+**
432
+
433
+ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
434
+### $(SQLITE_OPTIONS) $**
435
+
436
+**
437
+
438
+ENABLE_STAT3rverrverrverrver$O : serv#
439
+### $(SQLITE_OPTIONS) $**
440
+
441
+**
442
+
443
+ENABLE_STAT3rverrver
444
+
445
+SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
446
+
447
+4_.tag$O S) $**
448
+
449
+**
450
+
451
+EN4ertaserver_.c serverserver_.c
452
+
453
+serverservrver_.c:server#
454
+### $(SQLITE_OPTIONS) $**
455
+
456
+**
457
+
458
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
459
+
460
+serverservrver_.c:server#
461
+### $(SQLITE_OPTIONS) $**
462
+
463
+**
464
+
465
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
466
+
467
+serverservrver_.c:serververify$O : server_.c serve#
468
+### $(SQLITE_OPTIONS) $**
469
+
470
+**
471
+
472
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
473
+
474
+serverservrver_.c:servertag_qlite3_.c:servertag_.tag$O S) $**
475
+
476
+**th
477
+### $(th
478
+### $(SQLITE_OPfserver#
479
+##:server#
480
+### $(SQLITE_I18N serv#
481
+### $(SQLITE_OPTIONS) $**
482
+
483
+*
484
+
485
+**
486
+
487
+ENABLE_STAT3rverrverrverrver$O : server_.c rver_.c
488
+
489
+serverservrver_**$(SQLITE_OPTIONS) $**
490
+
491
+**
492
+
493
+raph_.c http_.r_.c:servertag_.tag$O ver_.c:server#
494
+### $(SQLITE_OP.c serverserver_.c
495
+
496
+serverservrvererervrver_.c:server#
497
+### $(SQLITE_OPTIONS) $**
498
+
499
+**
500
+
501
+ENABLE_Sc serverserver_.c
502
+
503
+serverservrver_.c:server#
504
+### $(AT3rverrverrverrver$O : server_.c serverserver_.c
505
+
506
+serverservrver_.c:servertag_.tag$O S) $**
507
+
508
+**
509
+
510
+ENABLE_STAT3rverrverrverrver*
511
+
512
+ENABLerrverrver$O : server_.c serverserver_.c
513
+
514
+serverservrver_.c:servertag_.tag$O S) $**
515
+
516
+**
517
+
518
+ENABLE_STAT3rverrverrverrver$Over_.c serverserver_.c
519
+
520
+serverservrver_.c:server#
521
+### $(SQLITE_OPTIONS) $**
522
+
523
+**
524
+
525
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
526
+
527
+serverservrver_.c:server#
528
+### $(SQLITE_OPTIONS) $**
529
+
530
+**
531
+
532
+ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
533
+
534
+serverservrver_.c:server**
535
+
536
+**
537
+
538
+ENABLE_STAT3rverrverrverrver$O : server_.c serv# DO NOT EDITrver_.c:server#
539
+### $(SQLITE_OPTIONS) $**
540
+
541
+**
542
+
543
+ENABLE_STAT3rverrverrverrver$O
544
+# "tclsh src/makemakeserverserver_.c
545
+
546
+serverservrver_.c:server**
547
+
548
+**
549
+
550
+ENABLE_STAT3rverrverrverrver$O : server_.c serthSTAT3rverrverrver# er$
--- a/win/Makefile.dmc
+++ b/win/Makefile.dmc
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/win/Makefile.dmc
+++ b/win/Makefile.dmc
@@ -0,0 +1,550 @@
1 # DO NOT EDITrver_.c:server#
2 ### $(SQLITE_OPTIONS) $**
3
4 **
5
6 ENABLE_STAT3rverrverrverrver$O
7 # "tclsh src/makemakeserverserver_.c
8
9 serverservrver_.c:server**
10
11 **
12
13 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
14
15 serverservrver_.c:servertag_.tag$O S) $**
16
17 **
18
19 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
20 ### $(SQLITE_OPTIONS) $**
21
22 **
23
24 ENABLE_STAT3rverrverrverrver$O : serv#
25 ### $(SQLITE_OPTIONS) $**
26
27 **
28
29 ENABLE_STAT3rverrver
30
31 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
32
33 4_.tag$O S) $**
34
35 **
36
37 EN4ertaserver_.c serverserver_.c
38
39 serverservrver_.c:server#
40 ### $(SQLITE_OPTIONS) $**
41
42 **
43
44 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
45
46 serverservrver_.c:server#
47 ### $(SQLITE_OPTIONS) $**
48
49 **
50
51 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
52
53 serverservrver_.c:serververify$O : server_.c serve#
54 ### $(SQLITE_OPTIONS) $**
55
56 **
57
58 ENABLE_STAT3rverrverrverrvever_.c:server#
59 ### $(SQLITE_OPTIONS) $**
60
61 **
62
63 ENABLE_STAT3rverrverrverrver$O
64 # "tclsh src/makemakeserverser_.c:server#
65 ### $(SQLITE_OPTIONS) $**
66
67 **
68
69 ENABLE_STAT3rverrverrverrver$O
70 # "tclsh src/makemaserver**
71
72 **
73
74 ENABLE_STAT3rverrverrverrver$O : server_.c serversNABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
75 ### $(SQLITE_OPTIONS) $**
76
77 **
78
79 ENABLE_STAT3rverrverrverrver$O : serv#
80 ### $(SQLITE_OPTIONS) $**
81
82 **
83
84 ENABLE_STAT3rverrver
85
86 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
87
88 4_.tag$O S) $**
89
90 **
91
92 EN4ertaserver_.c serverserver_.c
93
94 serverservrver_.c:server#
95 ### $(SQLITE_OPTIONS) $**
96
97 **
98
99 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
100
101 serverservrver_.c:server#
102 ### $(SQLITE_OPTIONS) $**
103
104 **
105
106 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
107
108 serverservrver_.c:serververify$O : server_.c serve#
109 ### STAT3rverrverrverrvever_.c:server#
110 ### $(SQLITE_OPTIONS) $**
111
112 **
113
114 ENABLE_STAT3rverrverrverrver$O
115 # "tclsh src/makemakeserverserver_.c
116
117 serverservrver_.c:server**
118
119 **
120
121 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
122
123 serverservrver_.c:servertag_.tag$O S) $**
124
125 **
126
127 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
128 ### $(SQLITE_OPTIONS) $**
129
130 **
131
132 ENABLE_STAT3rverrverrverrver$O : serv#
133 ### $(SQLITE_OPTIONS) $**
134
135 **
136
137 ENABLE_STAT3rverrver
138
139 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
140
141 4_.tag$O S) $**
142
143 **
144
145 EN4ertaserver_.c serverserver_.c
146
147 serverservrver_.c:server#
148 ### $(SQLITE_OPTIONS) $**
149
150 **
151
152 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
153
154 serverservrver_.c:server#
155 ### $(SQLITE_OPTIONS) $**
156
157 **
158
159 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
160
161 serverservrver_.c:serververify$O : server_.c serve#
162 ### $(SQLITE_OPTIONS) $**
163
164 **
165
166 ENABLE_STAT3rverrverrverrver$O : server# DO NOT EDITrver_.c:server#
167 ### $(SQLITE_OPTIONS) $**
168
169 **
170
171 ENABLE_STAT3rverrverrverrver$O
172 # "tclsh src/makemakeserverserver_.c
173
174 serverservrver_.c:server**
175
176 **
177
178 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
179
180 serverservrver_.c:servertag_.tag$O S) $**
181
182 **
183
184 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
185 ### $(SQLITE_OPTIONS) $**
186
187 **
188
189 ENABLE_STAT3rverrverrverrver$O : serv#
190 ### $(SQLITE_OPTIONS) $**
191
192 **
193
194 ENABLE_STAT3rverrver
195
196 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
197
198 4_.tag$O S) $**
199
200 **
201
202 EN4ertaserver_.c serverserver_.c
203
204 serverservrver_.c:server#
205 ### $(SQLITE_OPTIONS) $**
206
207 **
208
209 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
210
211 serverservrver_.c:server#
212 ### $(SQLITE_OPTIONS) $**
213
214 **
215
216 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
217
218 serverservrver_.c:serververify$O : server_.c serve#
219 ### $(SQLITE_OPTIONS) $**
220
221 **
222
223 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
224
225 serverservrver_.c:servertag_qlite3_.c:servertag_.tag$O S) $**
226
227 **th
228 ### $(th
229 ### $(SQLITE_OPfserver#
230 ##:server#
231 ### $(SQLITE_I18N serv#
232 ### $(SQLITE_OPTIONS) $**
233
234 *
235
236 **
237
238 ENABLE_STAT3rverrverrverrver$O : server_.c rver_.c
239
240 serverservrver_**$(SQLITE_OPTIONS) $**
241
242 **
243
244 raph_.c http_.r_.c:servertag_.tag$O ver_.c:server#
245 ### $(SQLITE_OP.c serverserver_.c
246
247 serverservrvererervrver_.c:server#
248 ### $(SQLITE_OPTIONS) $**
249
250 **
251
252 ENABLE_Sc serverserver_.c
253
254 serverservrver_.c:server#
255 ### $(AT3rverrverrverrver$O : server_.c serverserver_.c
256
257 serverservrver_.c:servertag_.tag$O S) $**
258
259 **
260
261 ENABLE_STAT3rverrverrverrver*
262
263 ENABLerrverrver$O : server_.c serverserver_.c
264
265 serverservrver_.c:servertag_.tag$O S) $**
266
267 **
268
269 ENABLE_STAT3rverrverrverrver$Over_.c serverserver_.c
270
271 serverservrver_.c:server#
272 ### $(SQLITE_OPTIONS) $**
273
274 **
275
276 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
277
278 serverservrver_.c:server#
279 ### $(SQLITE_OPTIONS) $**
280
281 **
282
283 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
284
285 serverservrver_.c:server**
286
287 **
288
289 ENABLE_STAT3rverrverrverrver$O : server_.c serv# DO NOT EDITrver_.c:server#
290 ### $(SQLITE_OPTIONS) $**
291
292 **
293
294 ENABLE_STAT3rverrverrverrver$O
295 # "tclsh src/makemakeserverserver_.c
296
297 serverservrver_.c:server**
298
299 **
300
301 ENABLE_STAT3rverrverrverrver$O : server_.c serthSTAT3rverrverrver# er$# DO NOT EDITrver_.c:server#
302 ### $(SQLITE_OPTIONS) $**
303
304 **
305
306 ENABLE_STAT3rverrverrverrver$O
307 # "tclsh src/makemakeserverserver_.c
308
309 serverservrver_.c:server**
310
311 **
312
313 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
314
315 serverservrver_.c:servertag_.tag$O S) $**
316
317 **
318
319 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
320 ### $(SQLITE_OPTIONS) $**
321
322 **
323
324 ENABLE_STAT3rverrverrverrver$O : serv#
325 ### $(SQLITE_OPTIONS) $**
326
327 **
328
329 ENABLE_STAT3rverrver
330
331 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
332
333 4_.tag$O S) $**
334
335 **
336
337 EN4ertaserver_.c serverserver_.c
338
339 serverservrver_.c:server#
340 ### $(SQLITE_OPTIONS) $**
341
342 **
343
344 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
345
346 serverservrver_.c:server#
347 ### $(SQLITE_OPTIONS) $**
348
349 **
350
351 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
352
353 serverservrver_.c:serververify$O : server_.c serve#
354 ### $(SQLITE_OPTIONS) $**
355
356 **
357
358 ENABLE_STAT3rverrverrverrvever_.c:server#
359 ### $(SQLITE_OPTIONS) $**
360
361 **
362
363 ENABLE_STAT3rverrverrverrver$O
364 # "tclsh src/makemakeserverserver_.c
365
366 serverservrver_.c:server**
367
368 **
369
370 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
371
372 serverservrver_.c:servertag_.tag$O S) $**
373
374 **
375
376 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
377 ### $(SQLITE_OPTIONS) $**
378
379 **
380
381 ENABLE_STAT3rverrverrverrver$O : serv#
382 ### $(SQLITE_OPTIONS) $**
383
384 **
385
386 ENABLE_STAT3rverrver
387
388 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
389
390 4_.tag$O S) $**
391
392 **
393
394 EN4ertaserver_.c serverserver_.c
395
396 serverservrver_.c:server#
397 ### $(SQLITE_OPTIONS) $**
398
399 **
400
401 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
402
403 serverservrver_.c:server#
404 ### $(SQLITE_OPTIONS) $**
405
406 **
407
408 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
409
410 serverservrver_.c:serververify$O : server_.c serve#
411 ### $(SQLITE_OPTIONS) $**
412
413 **
414
415 ENABLE_STAT3rverrverrverrver$O : server# DO NOT EDITrver_.c:server#
416 ### $(SQLITE_OPTIONS) $**
417
418 **
419
420 ENABLE_STAT3rverrverrverrver$O
421 # "tclsh src/makemakeserverserver_.c
422
423 serverservrver_.c:server**
424
425 **
426
427 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
428
429 serverservrver_.c:servertag_.tag$O S) $**
430
431 **
432
433 ENABLE_STAT3rverrverrverrver$OI18N = -DFOerver#
434 ### $(SQLITE_OPTIONS) $**
435
436 **
437
438 ENABLE_STAT3rverrverrverrver$O : serv#
439 ### $(SQLITE_OPTIONS) $**
440
441 **
442
443 ENABLE_STAT3rverrver
444
445 SQLITEer_.c serverserver_: serv4er_.c serverserver_4O : _THREADSAFE=0 -DSQLITEc
446
447 4_.tag$O S) $**
448
449 **
450
451 EN4ertaserver_.c serverserver_.c
452
453 serverservrver_.c:server#
454 ### $(SQLITE_OPTIONS) $**
455
456 **
457
458 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
459
460 serverservrver_.c:server#
461 ### $(SQLITE_OPTIONS) $**
462
463 **
464
465 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
466
467 serverservrver_.c:serververify$O : server_.c serve#
468 ### $(SQLITE_OPTIONS) $**
469
470 **
471
472 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
473
474 serverservrver_.c:servertag_qlite3_.c:servertag_.tag$O S) $**
475
476 **th
477 ### $(th
478 ### $(SQLITE_OPfserver#
479 ##:server#
480 ### $(SQLITE_I18N serv#
481 ### $(SQLITE_OPTIONS) $**
482
483 *
484
485 **
486
487 ENABLE_STAT3rverrverrverrver$O : server_.c rver_.c
488
489 serverservrver_**$(SQLITE_OPTIONS) $**
490
491 **
492
493 raph_.c http_.r_.c:servertag_.tag$O ver_.c:server#
494 ### $(SQLITE_OP.c serverserver_.c
495
496 serverservrvererervrver_.c:server#
497 ### $(SQLITE_OPTIONS) $**
498
499 **
500
501 ENABLE_Sc serverserver_.c
502
503 serverservrver_.c:server#
504 ### $(AT3rverrverrverrver$O : server_.c serverserver_.c
505
506 serverservrver_.c:servertag_.tag$O S) $**
507
508 **
509
510 ENABLE_STAT3rverrverrverrver*
511
512 ENABLerrverrver$O : server_.c serverserver_.c
513
514 serverservrver_.c:servertag_.tag$O S) $**
515
516 **
517
518 ENABLE_STAT3rverrverrverrver$Over_.c serverserver_.c
519
520 serverservrver_.c:server#
521 ### $(SQLITE_OPTIONS) $**
522
523 **
524
525 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
526
527 serverservrver_.c:server#
528 ### $(SQLITE_OPTIONS) $**
529
530 **
531
532 ENABLE_STAT3rverrverrverrver$O : server_.c serverserver_.c
533
534 serverservrver_.c:server**
535
536 **
537
538 ENABLE_STAT3rverrverrverrver$O : server_.c serv# DO NOT EDITrver_.c:server#
539 ### $(SQLITE_OPTIONS) $**
540
541 **
542
543 ENABLE_STAT3rverrverrverrver$O
544 # "tclsh src/makemakeserverserver_.c
545
546 serverservrver_.c:server**
547
548 **
549
550 ENABLE_STAT3rverrverrverrver$O : server_.c serthSTAT3rverrverrver# er$
--- a/win/Makefile.msc
+++ b/win/Makefile.msc
@@ -0,0 +1,46 @@
1
+# DO NOT EDITrssrssgamation.h :
2
+
3
+_
4
+
5
+_
6
+
7
+_.c"
8
+
9
+_.c"duskinstashsta_matimelin
10
+
11
+_.sqlite3thth_
12
+# "tclsh src/makemakehen using precompiled from http://zlib.net/zlib125-dll.zip
13
+#ZINCDIR =-dll\incluMSCDEF = -Dstrncasecmp=m
14
+
15
+_
16
+
17
+_.c"
18
+
19
+_.c"duskinstashsta_matimelin
20
+
21
+_.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
22
+MSCDIR = c:\msc# When using precompiled from http://zli# DO N DO NOT EDITrssrssgamation.h :
23
+
24
+_
25
+
26
+_
27
+
28
+_.c"
29
+
30
+_.c"duskinstashsta_matimelin
31
+
32
+_.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
33
+MSCDIR = c:\msc# When using precompiled from http://zlib.net/zlib125-dll.zip
34
+#ZINCDIR =-dll\incluDITrssrssgamation.h :
35
+
36
+_
37
+
38
+_
39
+
40
+_.c"
41
+
42
+_.c"duskinstashsta_matimelin
43
+
44
+_.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
45
+MSCDIR = c:\msc# When using precompiled from http://zlib.net/zlib125-dll.zip
46
+#ZINCDIR =-dll\inclu
--- a/win/Makefile.msc
+++ b/win/Makefile.msc
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/win/Makefile.msc
+++ b/win/Makefile.msc
@@ -0,0 +1,46 @@
1 # DO NOT EDITrssrssgamation.h :
2
3 _
4
5 _
6
7 _.c"
8
9 _.c"duskinstashsta_matimelin
10
11 _.sqlite3thth_
12 # "tclsh src/makemakehen using precompiled from http://zlib.net/zlib125-dll.zip
13 #ZINCDIR =-dll\incluMSCDEF = -Dstrncasecmp=m
14
15 _
16
17 _.c"
18
19 _.c"duskinstashsta_matimelin
20
21 _.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
22 MSCDIR = c:\msc# When using precompiled from http://zli# DO N DO NOT EDITrssrssgamation.h :
23
24 _
25
26 _
27
28 _.c"
29
30 _.c"duskinstashsta_matimelin
31
32 _.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
33 MSCDIR = c:\msc# When using precompiled from http://zlib.net/zlib125-dll.zip
34 #ZINCDIR =-dll\incluDITrssrssgamation.h :
35
36 _
37
38 _
39
40 _.c"
41
42 _.c"duskinstashsta_matimelin
43
44 _.sqlite3thth_lang$Odbsrc/Maybe MSCDIR, stment
45 MSCDIR = c:\msc# When using precompiled from http://zlib.net/zlib125-dll.zip
46 #ZINCDIR =-dll\inclu
--- a/win/include/dirent.h
+++ b/win/include/dirent.h
@@ -0,0 +1,67 @@
1
+/ FFIFO _S_IFFIFOdle != INVALID_HAND/* Pipe */
2
+ if (n > 0) le n****
3
+ * dirent.h - dirent API for Microsoft Visual Studio
4
+ *
5
+ * Copyright d
6
+ * Peron is hereby granted, free of charge, to any person obtaining
7
+ * a copy of this software and associated documentation files (the
8
+ * ``Software''), to deal in the Software without restriction, including
9
+ * without limitation the rights to use, copy, modify, merge, publish,
10
+ * distribute, sublicense, and/or sell copies of the Software, and to
11
+ * permit persons to whom the Software is furnished to do so, subject to
12
+ * the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included
15
+ * in all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ * IN NO EVENT SHALL TONI RONKKO BE LIABLEdirent
21
+{
22
+ charLIABLE FOR ANY CLAmulti-bytAdirent;
23
+
24
+
25
+typedef struct DIR
26
+{
27
+ LINGS IN THE SOFTWARE.
28
+ *
29
+ * Sept 23, 2012, Joe MistachkRonkko
30
+ * Define PATH_MAX and NAME_MAX. Added wide-character va
31
+ *
32
+ *iants _wDIR_wdirent, _wopendir(),
33
+ *)charDo notdi
34
+ *as and code.
35
+ *
36
+ * Do notding windows.h make This allows dows.h make ThDIR *opendir (const char *dirname);
37
+static struct dirent *readdir (DIR *dirp);
38
+static int closedir (DIR *dirp);
39
+static void rewinddir(DIR* winddirix-code to access some file names despite of unicode
40
+ * characters, although file names may seem unfamiliar to the user.
41
+ *
42
+ strer.
43
+ *
44
+ * Be ware that the code below cannot come up with a strrt file
45
+ * name unless the file system provides o attributes */
46
+ attr = datap->dwFileAttributes;
47
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
48
+ entry->d_type
49
+typedef struct _w /* Compute number of entries in the enlarged pointer tabDIR *opendir
50
+ numstrtries = init_size;
51
+ _w
52
+ * dirent.h - dirent / FFIFO _S_IFFIFOdle != INVAL_IFFstrAdirent *readdir(Aclosedir(
53
+/ FFIFO _S_IFFIFOdle != INVALID_HAND/* Pipe */
54
+ if (n > 0) le n****
55
+ * Resets the position of thto which dirp refers to the
56
+ * beginning of the directory. It also causes thto refer
57
+ * to the current state of the corresponding directory, as a call to opendir()
58
+ * would have done. If dirp does not refer to a directory stream, the effect
59
+ * is undefined.
60
+ */
61
+static void rewinddir(DIR* dirp)
62
+{open new search handle FindFirstFileAfree (dirp);
63
+ return;
64
+ }
65
+
66
+dirp->cached = 1;
67
+}
--- a/win/include/dirent.h
+++ b/win/include/dirent.h
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/win/include/dirent.h
+++ b/win/include/dirent.h
@@ -0,0 +1,67 @@
1 / FFIFO _S_IFFIFOdle != INVALID_HAND/* Pipe */
2 if (n > 0) le n****
3 * dirent.h - dirent API for Microsoft Visual Studio
4 *
5 * Copyright d
6 * Peron is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * ``Software''), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL TONI RONKKO BE LIABLEdirent
21 {
22 charLIABLE FOR ANY CLAmulti-bytAdirent;
23
24
25 typedef struct DIR
26 {
27 LINGS IN THE SOFTWARE.
28 *
29 * Sept 23, 2012, Joe MistachkRonkko
30 * Define PATH_MAX and NAME_MAX. Added wide-character va
31 *
32 *iants _wDIR_wdirent, _wopendir(),
33 *)charDo notdi
34 *as and code.
35 *
36 * Do notding windows.h make This allows dows.h make ThDIR *opendir (const char *dirname);
37 static struct dirent *readdir (DIR *dirp);
38 static int closedir (DIR *dirp);
39 static void rewinddir(DIR* winddirix-code to access some file names despite of unicode
40 * characters, although file names may seem unfamiliar to the user.
41 *
42 strer.
43 *
44 * Be ware that the code below cannot come up with a strrt file
45 * name unless the file system provides o attributes */
46 attr = datap->dwFileAttributes;
47 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
48 entry->d_type
49 typedef struct _w /* Compute number of entries in the enlarged pointer tabDIR *opendir
50 numstrtries = init_size;
51 _w
52 * dirent.h - dirent / FFIFO _S_IFFIFOdle != INVAL_IFFstrAdirent *readdir(Aclosedir(
53 / FFIFO _S_IFFIFOdle != INVALID_HAND/* Pipe */
54 if (n > 0) le n****
55 * Resets the position of thto which dirp refers to the
56 * beginning of the directory. It also causes thto refer
57 * to the current state of the corresponding directory, as a call to opendir()
58 * would have done. If dirp does not refer to a directory stream, the effect
59 * is undefined.
60 */
61 static void rewinddir(DIR* dirp)
62 {open new search handle FindFirstFileAfree (dirp);
63 return;
64 }
65
66 dirp->cached = 1;
67 }
--- a/win/include/unistd.h
+++ b/win/include/unistd.h
@@ -0,0 +1,43 @@
1
+#ifndef _UNISTD_H
2
+#define _UNISTD_H 1
3
+
4
+/* This file intended to serve as a drop-in replacement for
5
+ * unistd.h on Windows
6
+ * Please add functionality as neeeded
7
+ */
8
+
9
+#include <stdlib.h>
10
+#include <io.h>
11
+#define srandom srand
12
+#define random rand
13
+#if defined(__DMC__)
14
+#endif
15
+
16
+#if defined(_WIN32)
17
+#define _CRT_SECURE_NO_WARNINGS 1
18
+
19
+#ifndef F_OK
20
+#define F_OK 0
21
+#endif /* not F_OK */
22
+
23
+#ifndef X_OK
24
+#define X_OK 1
25
+#endR_OK
26
+#define R_OK 2 _UNISTD_H
27
+#define _UNIST#ifndef _UNISTD_H
28
+#define
29
+#endif /* not W_OK */
30
+
31
+#ifndef R_OK
32
+#define R_OK 4
33
+#endif /* not R_OK */
34
+
35
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
36
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
37
+
38
+
39
+
40
+#endif
41
+
42
+#define access _access
43
+#define ftruncate _c
--- a/win/include/unistd.h
+++ b/win/include/unistd.h
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/win/include/unistd.h
+++ b/win/include/unistd.h
@@ -0,0 +1,43 @@
1 #ifndef _UNISTD_H
2 #define _UNISTD_H 1
3
4 /* This file intended to serve as a drop-in replacement for
5 * unistd.h on Windows
6 * Please add functionality as neeeded
7 */
8
9 #include <stdlib.h>
10 #include <io.h>
11 #define srandom srand
12 #define random rand
13 #if defined(__DMC__)
14 #endif
15
16 #if defined(_WIN32)
17 #define _CRT_SECURE_NO_WARNINGS 1
18
19 #ifndef F_OK
20 #define F_OK 0
21 #endif /* not F_OK */
22
23 #ifndef X_OK
24 #define X_OK 1
25 #endR_OK
26 #define R_OK 2 _UNISTD_H
27 #define _UNIST#ifndef _UNISTD_H
28 #define
29 #endif /* not W_OK */
30
31 #ifndef R_OK
32 #define R_OK 4
33 #endif /* not R_OK */
34
35 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
36 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
37
38
39
40 #endif
41
42 #define access _access
43 #define ftruncate _c
--- a/win/version.c
+++ b/win/version.c
@@ -0,0 +1,4 @@
1
+0;
2
+}
3
+b2b2,0,sizeof(b[1024b[strlen(b)-1] =0; return 0"gv[1],"fMSC_VER)
4
+ fopen(argv[1
--- a/win/version.c
+++ b/win/version.c
@@ -0,0 +1,4 @@
 
 
 
 
--- a/win/version.c
+++ b/win/version.c
@@ -0,0 +1,4 @@
1 0;
2 }
3 b2b2,0,sizeof(b[1024b[strlen(b)-1] =0; return 0"gv[1],"fMSC_VER)
4 fopen(argv[1
+1 -1
--- www/index.wiki
+++ www/index.wiki
@@ -88,11 +88,11 @@
8888
7. <b>Robust &amp; Reliable</b> -
8989
Fossil stores content using an [./fileformat.wiki | enduring file format]
9090
in an SQLite database so that transactions are
9191
atomic even if interrupted by a power loss or system crash. Furthermore,
9292
automatic [./selfcheck.wiki | self-checks] verify that all aspects of
93
- the repository are consistent prior to each commit. In nearly three years
93
+ the repository are consistent prior to each commit. In over three years
9494
of operation, no work has ever been lost after having been committed to
9595
a Fossil repository.
9696
9797
<hr>
9898
<h3>Links For Fossil Users:</h3>
9999
--- www/index.wiki
+++ www/index.wiki
@@ -88,11 +88,11 @@
88 7. <b>Robust &amp; Reliable</b> -
89 Fossil stores content using an [./fileformat.wiki | enduring file format]
90 in an SQLite database so that transactions are
91 atomic even if interrupted by a power loss or system crash. Furthermore,
92 automatic [./selfcheck.wiki | self-checks] verify that all aspects of
93 the repository are consistent prior to each commit. In nearly three years
94 of operation, no work has ever been lost after having been committed to
95 a Fossil repository.
96
97 <hr>
98 <h3>Links For Fossil Users:</h3>
99
--- www/index.wiki
+++ www/index.wiki
@@ -88,11 +88,11 @@
88 7. <b>Robust &amp; Reliable</b> -
89 Fossil stores content using an [./fileformat.wiki | enduring file format]
90 in an SQLite database so that transactions are
91 atomic even if interrupted by a power loss or system crash. Furthermore,
92 automatic [./selfcheck.wiki | self-checks] verify that all aspects of
93 the repository are consistent prior to each commit. In over three years
94 of operation, no work has ever been lost after having been committed to
95 a Fossil repository.
96
97 <hr>
98 <h3>Links For Fossil Users:</h3>
99
--- www/password.wiki
+++ www/password.wiki
@@ -60,20 +60,20 @@
6060
the web interface or direct SQL manipulation of the USER table.
6161
Note also that the password field is
6262
essentially ignored for the special users named "anonymous", "developer",
6363
"reader", and "nobody". It is not possible to authenticate as users
6464
"developer", "reader", or "nobody" and the authentication protocol
65
-for "anonymous" use one-time captchas not persistent passwords.
65
+for "anonymous" uses one-time captchas not persistent passwords.
6666
6767
<h2>Web Interface Authentication</h2>
6868
6969
When a user logs into Fossil using the web interface, the login name
7070
and password are sent in the clear to the server. The server then
7171
hashes the password and compares it against the value stored in USER.PW.
7272
If they match, the server sets a cookie on the client to record the
7373
login. This cookie contains a large amount of high-quality randomness
74
-and is thus impossible to guess. The value of the cookie and the IP
74
+and is thus intractable to guess. The value of the cookie and the IP
7575
address of the client is stored in the USER.COOKIE and USER.IPADDR fields
7676
of the USER table on the server.
7777
The USER.CEXPIRE field holds an expiration date
7878
for the cookie, encoded as a julian day number. On all subsequent
7979
HTTP requests, the cookie value is matched against the USER table to
8080
--- www/password.wiki
+++ www/password.wiki
@@ -60,20 +60,20 @@
60 the web interface or direct SQL manipulation of the USER table.
61 Note also that the password field is
62 essentially ignored for the special users named "anonymous", "developer",
63 "reader", and "nobody". It is not possible to authenticate as users
64 "developer", "reader", or "nobody" and the authentication protocol
65 for "anonymous" use one-time captchas not persistent passwords.
66
67 <h2>Web Interface Authentication</h2>
68
69 When a user logs into Fossil using the web interface, the login name
70 and password are sent in the clear to the server. The server then
71 hashes the password and compares it against the value stored in USER.PW.
72 If they match, the server sets a cookie on the client to record the
73 login. This cookie contains a large amount of high-quality randomness
74 and is thus impossible to guess. The value of the cookie and the IP
75 address of the client is stored in the USER.COOKIE and USER.IPADDR fields
76 of the USER table on the server.
77 The USER.CEXPIRE field holds an expiration date
78 for the cookie, encoded as a julian day number. On all subsequent
79 HTTP requests, the cookie value is matched against the USER table to
80
--- www/password.wiki
+++ www/password.wiki
@@ -60,20 +60,20 @@
60 the web interface or direct SQL manipulation of the USER table.
61 Note also that the password field is
62 essentially ignored for the special users named "anonymous", "developer",
63 "reader", and "nobody". It is not possible to authenticate as users
64 "developer", "reader", or "nobody" and the authentication protocol
65 for "anonymous" uses one-time captchas not persistent passwords.
66
67 <h2>Web Interface Authentication</h2>
68
69 When a user logs into Fossil using the web interface, the login name
70 and password are sent in the clear to the server. The server then
71 hashes the password and compares it against the value stored in USER.PW.
72 If they match, the server sets a cookie on the client to record the
73 login. This cookie contains a large amount of high-quality randomness
74 and is thus intractable to guess. The value of the cookie and the IP
75 address of the client is stored in the USER.COOKIE and USER.IPADDR fields
76 of the USER table on the server.
77 The USER.CEXPIRE field holds an expiration date
78 for the cookie, encoded as a julian day number. On all subsequent
79 HTTP requests, the cookie value is matched against the USER table to
80
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -232,16 +232,19 @@
232232
233233
<blockquote>
234234
<b>fossil ui</b> <i>repository-filename</i>
235235
</blockquote>
236236
237
- <p>The difference between these two command is that <b>ui</b>
238
- attempts to automatically start your web browser pointing at the
239
- server whereas <b>server</b> does not.
237
+ <p>The <b>ui</b> command is intended for accessing the web interface
238
+ from a local desktop. The <b>ui</b> command binds to the loopback IP
239
+ address only (and is thus makes the web interface visible only on the
240
+ local machine) and it automatically start your web browser pointing at the
241
+ server. For cross-machine collaboration, use the <b>server</b> command,
242
+ which binds on all IP addresses and does not try to start a web browser.
240243
You can omit the <i>repository-filename</i> if you are within
241
- a checked-out local tree. This server uses port 8080 by default
242
- but you can specify a different port using the <b>-port</b> command.</p>
244
+ a checked-out local tree. The <b>server</b> uses port 8080 by default
245
+ but you can specify a different port using the <b>-port</b> option.</p>
243246
244247
<p>Command-line servers like this are useful when two people want
245248
to share a repository on temporary or ad-hoc basis. For a more
246249
permanent installation, you should use either the CGI server or the
247250
inetd server.
248251
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -232,16 +232,19 @@
232
233 <blockquote>
234 <b>fossil ui</b> <i>repository-filename</i>
235 </blockquote>
236
237 <p>The difference between these two command is that <b>ui</b>
238 attempts to automatically start your web browser pointing at the
239 server whereas <b>server</b> does not.
 
 
 
240 You can omit the <i>repository-filename</i> if you are within
241 a checked-out local tree. This server uses port 8080 by default
242 but you can specify a different port using the <b>-port</b> command.</p>
243
244 <p>Command-line servers like this are useful when two people want
245 to share a repository on temporary or ad-hoc basis. For a more
246 permanent installation, you should use either the CGI server or the
247 inetd server.
248
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -232,16 +232,19 @@
232
233 <blockquote>
234 <b>fossil ui</b> <i>repository-filename</i>
235 </blockquote>
236
237 <p>The <b>ui</b> command is intended for accessing the web interface
238 from a local desktop. The <b>ui</b> command binds to the loopback IP
239 address only (and is thus makes the web interface visible only on the
240 local machine) and it automatically start your web browser pointing at the
241 server. For cross-machine collaboration, use the <b>server</b> command,
242 which binds on all IP addresses and does not try to start a web browser.
243 You can omit the <i>repository-filename</i> if you are within
244 a checked-out local tree. The <b>server</b> uses port 8080 by default
245 but you can specify a different port using the <b>-port</b> option.</p>
246
247 <p>Command-line servers like this are useful when two people want
248 to share a repository on temporary or ad-hoc basis. For a more
249 permanent installation, you should use either the CGI server or the
250 inetd server.
251

Keyboard Shortcuts

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