Fossil SCM

merge trunk

jan.nijtmans 2012-11-27 12:10 improve_commit_warning merge
Commit 2e1f2e69d545c699c7c6f7085690ef88039f09f0
+2 -1
--- src/add.c
+++ src/add.c
@@ -138,11 +138,12 @@
138138
int vid, /* Add to this VFILE */
139139
int caseSensitive /* True if filenames are case sensitive */
140140
){
141141
const char *zCollate = caseSensitive ? "binary" : "nocase";
142142
if( !file_is_simple_pathname(zPath) ){
143
- fossil_fatal("filename contains illegal characters: %s", zPath);
143
+ fossil_warning("filename contains illegal characters: %s", zPath);
144
+ return 0;
144145
}
145146
if( db_exists("SELECT 1 FROM vfile"
146147
" WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
147148
db_multi_exec("UPDATE vfile SET deleted=0"
148149
" WHERE pathname=%Q COLLATE %s", zPath, zCollate);
149150
--- src/add.c
+++ src/add.c
@@ -138,11 +138,12 @@
138 int vid, /* Add to this VFILE */
139 int caseSensitive /* True if filenames are case sensitive */
140 ){
141 const char *zCollate = caseSensitive ? "binary" : "nocase";
142 if( !file_is_simple_pathname(zPath) ){
143 fossil_fatal("filename contains illegal characters: %s", zPath);
 
144 }
145 if( db_exists("SELECT 1 FROM vfile"
146 " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
147 db_multi_exec("UPDATE vfile SET deleted=0"
148 " WHERE pathname=%Q COLLATE %s", zPath, zCollate);
149
--- src/add.c
+++ src/add.c
@@ -138,11 +138,12 @@
138 int vid, /* Add to this VFILE */
139 int caseSensitive /* True if filenames are case sensitive */
140 ){
141 const char *zCollate = caseSensitive ? "binary" : "nocase";
142 if( !file_is_simple_pathname(zPath) ){
143 fossil_warning("filename contains illegal characters: %s", zPath);
144 return 0;
145 }
146 if( db_exists("SELECT 1 FROM vfile"
147 " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
148 db_multi_exec("UPDATE vfile SET deleted=0"
149 " WHERE pathname=%Q COLLATE %s", zPath, zCollate);
150
+17 -9
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
7171
const char *zFilename = db_column_text(&q, 3);
7272
const char *zComment = db_column_text(&q, 4);
7373
const char *zUser = db_column_text(&q, 5);
7474
const char *zUuid = db_column_text(&q, 6);
7575
int attachid = db_column_int(&q, 7);
76
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
7677
int i;
7778
char *zUrlTail;
7879
for(i=0; zFilename[i]; i++){
7980
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
8081
zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
115116
@ Deleted
116117
}else {
117118
@ Added
118119
}
119120
}
120
- @ by %h(zUser) on
121
+ @ by %h(zDispUser) on
121122
hyperlink_to_date(zDate, ".");
122123
free(zUrlTail);
123124
}
124125
db_finalize(&q);
125126
@ </ol>
@@ -235,10 +236,11 @@
235236
const char *aContent = P("f");
236237
const char *zName = PD("f:filename","unknown");
237238
const char *zTarget;
238239
const char *zTargetType;
239240
int szContent = atoi(PD("f:bytes","0"));
241
+ int goodCaptcha = 1;
240242
241243
if( P("cancel") ) cgi_redirect(zFrom);
242244
if( zPage && zTkt ) fossil_redirect_home();
243245
if( zPage==0 && zTkt==0 ) fossil_redirect_home();
244246
login_check_credentials();
@@ -263,11 +265,11 @@
263265
}
264266
if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
265267
if( P("cancel") ){
266268
cgi_redirect(zFrom);
267269
}
268
- if( P("ok") && szContent>0 ){
270
+ if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
269271
Blob content;
270272
Blob manifest;
271273
Blob cksum;
272274
char *zUUID;
273275
const char *zComment;
@@ -316,13 +318,16 @@
316318
assert( blob_is_reset(&manifest) );
317319
db_end_transaction(0);
318320
cgi_redirect(zFrom);
319321
}
320322
style_header("Add Attachment");
323
+ if( !goodCaptcha ){
324
+ @ <p class="generalError">Error: Incorrect security code.</p>
325
+ }
321326
@ <h2>Add Attachment To %s(zTargetType)</h2>
322
- @ <form action="%s(g.zTop)/attachadd" method="post"
323
- @ enctype="multipart/form-data"><div>
327
+ form_begin("enctype='multipart/form-data'", "%R/attachadd");
328
+ @ <div>
324329
@ File to Attach:
325330
@ <input type="file" name="f" size="60" /><br />
326331
@ Description:<br />
327332
@ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
328333
if( zTkt ){
@@ -331,11 +336,13 @@
331336
@ <input type="hidden" name="page" value="%h(zPage)" />
332337
}
333338
@ <input type="hidden" name="from" value="%h(zFrom)" />
334339
@ <input type="submit" name="ok" value="Add Attachment" />
335340
@ <input type="submit" name="cancel" value="Cancel" />
336
- @ </div></form>
341
+ @ </div>
342
+ captcha_generate();
343
+ @ </form>
337344
style_footer();
338345
}
339346
340347
/*
341348
** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
432439
}
433440
434441
if( P("del")
435442
&& ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
436443
){
437
- @ <form method="post" action="%R/ainfo/%s(zUuid)">
444
+ form_begin(0, "%R/ainfo/%s", zUuid);
438445
@ <p>Confirm you want to delete the attachment shown below.
439446
@ <input type="submit" name="confirm" value="Confirm">
440447
@ </form>
441448
}
442449
@@ -496,11 +503,11 @@
496503
@ </table>
497504
498505
if( isModerator && modPending ){
499506
@ <div class="section">Moderation</div>
500507
@ <blockquote>
501
- @ <form method="POST" action="%R/ainfo/%s(zUuid)">
508
+ form_begin(0, "%R/ainfo/%s", zUuid);
502509
@ <label><input type="radio" name="modaction" value="delete">
503510
@ Delete this change</label><br />
504511
@ <label><input type="radio" name="modaction" value="approve">
505512
@ Approve this change</label><br />
506513
@ <input type="submit" value="Submit">
@@ -513,11 +520,11 @@
513520
blob_zero(&attach);
514521
if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
515522
const char *z;
516523
const char *zLn = P("ln");
517524
content_get(ridSrc, &attach);
518
- blob_strip_bom(&attach, 0);
525
+ blob_to_utf8_no_bom(&attach, 0);
519526
z = blob_str(&attach);
520527
if( zLn ){
521528
output_text_with_line_numbers(z, zLn);
522529
}else{
523530
@ <pre>
@@ -557,17 +564,18 @@
557564
const char *zDate = db_column_text(&q, 0);
558565
const char *zFile = db_column_text(&q, 1);
559566
const char *zUser = db_column_text(&q, 2);
560567
const char *zUuid = db_column_text(&q, 3);
561568
const char *zSrc = db_column_text(&q, 4);
569
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
562570
if( cnt==0 ){
563571
@ %s(zHeader)
564572
}
565573
cnt++;
566574
@ <li>
567575
@ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
568
- @ added by %h(zUser) on
576
+ @ added by %h(zDispUser) on
569577
hyperlink_to_date(zDate, ".");
570578
@ [%z(href("%R/ainfo/%s",zUuid))details</a>]
571579
@ </li>
572580
}
573581
if( cnt ){
574582
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
71 const char *zFilename = db_column_text(&q, 3);
72 const char *zComment = db_column_text(&q, 4);
73 const char *zUser = db_column_text(&q, 5);
74 const char *zUuid = db_column_text(&q, 6);
75 int attachid = db_column_int(&q, 7);
 
76 int i;
77 char *zUrlTail;
78 for(i=0; zFilename[i]; i++){
79 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
80 zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
115 @ Deleted
116 }else {
117 @ Added
118 }
119 }
120 @ by %h(zUser) on
121 hyperlink_to_date(zDate, ".");
122 free(zUrlTail);
123 }
124 db_finalize(&q);
125 @ </ol>
@@ -235,10 +236,11 @@
235 const char *aContent = P("f");
236 const char *zName = PD("f:filename","unknown");
237 const char *zTarget;
238 const char *zTargetType;
239 int szContent = atoi(PD("f:bytes","0"));
 
240
241 if( P("cancel") ) cgi_redirect(zFrom);
242 if( zPage && zTkt ) fossil_redirect_home();
243 if( zPage==0 && zTkt==0 ) fossil_redirect_home();
244 login_check_credentials();
@@ -263,11 +265,11 @@
263 }
264 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
265 if( P("cancel") ){
266 cgi_redirect(zFrom);
267 }
268 if( P("ok") && szContent>0 ){
269 Blob content;
270 Blob manifest;
271 Blob cksum;
272 char *zUUID;
273 const char *zComment;
@@ -316,13 +318,16 @@
316 assert( blob_is_reset(&manifest) );
317 db_end_transaction(0);
318 cgi_redirect(zFrom);
319 }
320 style_header("Add Attachment");
 
 
 
321 @ <h2>Add Attachment To %s(zTargetType)</h2>
322 @ <form action="%s(g.zTop)/attachadd" method="post"
323 @ enctype="multipart/form-data"><div>
324 @ File to Attach:
325 @ <input type="file" name="f" size="60" /><br />
326 @ Description:<br />
327 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
328 if( zTkt ){
@@ -331,11 +336,13 @@
331 @ <input type="hidden" name="page" value="%h(zPage)" />
332 }
333 @ <input type="hidden" name="from" value="%h(zFrom)" />
334 @ <input type="submit" name="ok" value="Add Attachment" />
335 @ <input type="submit" name="cancel" value="Cancel" />
336 @ </div></form>
 
 
337 style_footer();
338 }
339
340 /*
341 ** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
432 }
433
434 if( P("del")
435 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
436 ){
437 @ <form method="post" action="%R/ainfo/%s(zUuid)">
438 @ <p>Confirm you want to delete the attachment shown below.
439 @ <input type="submit" name="confirm" value="Confirm">
440 @ </form>
441 }
442
@@ -496,11 +503,11 @@
496 @ </table>
497
498 if( isModerator && modPending ){
499 @ <div class="section">Moderation</div>
500 @ <blockquote>
501 @ <form method="POST" action="%R/ainfo/%s(zUuid)">
502 @ <label><input type="radio" name="modaction" value="delete">
503 @ Delete this change</label><br />
504 @ <label><input type="radio" name="modaction" value="approve">
505 @ Approve this change</label><br />
506 @ <input type="submit" value="Submit">
@@ -513,11 +520,11 @@
513 blob_zero(&attach);
514 if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
515 const char *z;
516 const char *zLn = P("ln");
517 content_get(ridSrc, &attach);
518 blob_strip_bom(&attach, 0);
519 z = blob_str(&attach);
520 if( zLn ){
521 output_text_with_line_numbers(z, zLn);
522 }else{
523 @ <pre>
@@ -557,17 +564,18 @@
557 const char *zDate = db_column_text(&q, 0);
558 const char *zFile = db_column_text(&q, 1);
559 const char *zUser = db_column_text(&q, 2);
560 const char *zUuid = db_column_text(&q, 3);
561 const char *zSrc = db_column_text(&q, 4);
 
562 if( cnt==0 ){
563 @ %s(zHeader)
564 }
565 cnt++;
566 @ <li>
567 @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
568 @ added by %h(zUser) on
569 hyperlink_to_date(zDate, ".");
570 @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
571 @ </li>
572 }
573 if( cnt ){
574
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
71 const char *zFilename = db_column_text(&q, 3);
72 const char *zComment = db_column_text(&q, 4);
73 const char *zUser = db_column_text(&q, 5);
74 const char *zUuid = db_column_text(&q, 6);
75 int attachid = db_column_int(&q, 7);
76 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
77 int i;
78 char *zUrlTail;
79 for(i=0; zFilename[i]; i++){
80 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
81 zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
116 @ Deleted
117 }else {
118 @ Added
119 }
120 }
121 @ by %h(zDispUser) on
122 hyperlink_to_date(zDate, ".");
123 free(zUrlTail);
124 }
125 db_finalize(&q);
126 @ </ol>
@@ -235,10 +236,11 @@
236 const char *aContent = P("f");
237 const char *zName = PD("f:filename","unknown");
238 const char *zTarget;
239 const char *zTargetType;
240 int szContent = atoi(PD("f:bytes","0"));
241 int goodCaptcha = 1;
242
243 if( P("cancel") ) cgi_redirect(zFrom);
244 if( zPage && zTkt ) fossil_redirect_home();
245 if( zPage==0 && zTkt==0 ) fossil_redirect_home();
246 login_check_credentials();
@@ -263,11 +265,11 @@
265 }
266 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
267 if( P("cancel") ){
268 cgi_redirect(zFrom);
269 }
270 if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
271 Blob content;
272 Blob manifest;
273 Blob cksum;
274 char *zUUID;
275 const char *zComment;
@@ -316,13 +318,16 @@
318 assert( blob_is_reset(&manifest) );
319 db_end_transaction(0);
320 cgi_redirect(zFrom);
321 }
322 style_header("Add Attachment");
323 if( !goodCaptcha ){
324 @ <p class="generalError">Error: Incorrect security code.</p>
325 }
326 @ <h2>Add Attachment To %s(zTargetType)</h2>
327 form_begin("enctype='multipart/form-data'", "%R/attachadd");
328 @ <div>
329 @ File to Attach:
330 @ <input type="file" name="f" size="60" /><br />
331 @ Description:<br />
332 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
333 if( zTkt ){
@@ -331,11 +336,13 @@
336 @ <input type="hidden" name="page" value="%h(zPage)" />
337 }
338 @ <input type="hidden" name="from" value="%h(zFrom)" />
339 @ <input type="submit" name="ok" value="Add Attachment" />
340 @ <input type="submit" name="cancel" value="Cancel" />
341 @ </div>
342 captcha_generate();
343 @ </form>
344 style_footer();
345 }
346
347 /*
348 ** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
439 }
440
441 if( P("del")
442 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
443 ){
444 form_begin(0, "%R/ainfo/%s", zUuid);
445 @ <p>Confirm you want to delete the attachment shown below.
446 @ <input type="submit" name="confirm" value="Confirm">
447 @ </form>
448 }
449
@@ -496,11 +503,11 @@
503 @ </table>
504
505 if( isModerator && modPending ){
506 @ <div class="section">Moderation</div>
507 @ <blockquote>
508 form_begin(0, "%R/ainfo/%s", zUuid);
509 @ <label><input type="radio" name="modaction" value="delete">
510 @ Delete this change</label><br />
511 @ <label><input type="radio" name="modaction" value="approve">
512 @ Approve this change</label><br />
513 @ <input type="submit" value="Submit">
@@ -513,11 +520,11 @@
520 blob_zero(&attach);
521 if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
522 const char *z;
523 const char *zLn = P("ln");
524 content_get(ridSrc, &attach);
525 blob_to_utf8_no_bom(&attach, 0);
526 z = blob_str(&attach);
527 if( zLn ){
528 output_text_with_line_numbers(z, zLn);
529 }else{
530 @ <pre>
@@ -557,17 +564,18 @@
564 const char *zDate = db_column_text(&q, 0);
565 const char *zFile = db_column_text(&q, 1);
566 const char *zUser = db_column_text(&q, 2);
567 const char *zUuid = db_column_text(&q, 3);
568 const char *zSrc = db_column_text(&q, 4);
569 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
570 if( cnt==0 ){
571 @ %s(zHeader)
572 }
573 cnt++;
574 @ <li>
575 @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
576 @ added by %h(zDispUser) on
577 hyperlink_to_date(zDate, ".");
578 @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
579 @ </li>
580 }
581 if( cnt ){
582
+43 -51
--- src/blob.c
+++ src/blob.c
@@ -1088,61 +1088,53 @@
10881088
*pLeft = *pRight;
10891089
*pRight = swap;
10901090
}
10911091
10921092
/*
1093
-** Strip a possible BOM from the blob. On Windows, if there
1094
-** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion
1095
-** to UTF-8 is done.
1096
-** If useMbcs is false and there is no BOM, the input string
1097
-** is assumed to be UTF-8 already, so no conversion is done.
1098
-*/
1099
-void blob_strip_bom(Blob *pBlob, int useMbcs){
1100
- static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
1101
-#ifdef _WIN32
1102
- static const unsigned short ubom = 0xfeff;
1103
- static const unsigned short urbom = 0xfffe;
1104
-#endif /* _WIN32 */
1105
- char *zUtf8;
1106
- if( blob_size(pBlob)>2 && memcmp(blob_buffer(pBlob), bom, 3)==0 ) {
1107
- struct Blob temp;
1108
- zUtf8 = blob_str(pBlob) + 3;
1109
- blob_zero(&temp);
1110
- blob_append(&temp, zUtf8, -1);
1111
- fossil_mbcs_free(zUtf8);
1112
- blob_swap(pBlob, &temp);
1113
- blob_reset(&temp);
1114
-#ifdef _WIN32
1115
- }else if( blob_size(pBlob)>1 && (blob_size(pBlob)&1)==0
1116
- && memcmp(blob_buffer(pBlob), &ubom, 2)==0 ) {
1117
- /* Make sure the blob contains two terminating 0-bytes */
1118
- blob_append(pBlob, "", 1);
1119
- zUtf8 = blob_str(pBlob) + 2;
1120
- zUtf8 = fossil_unicode_to_utf8(zUtf8);
1121
- blob_zero(pBlob);
1122
- blob_append(pBlob, zUtf8, -1);
1123
- fossil_mbcs_free(zUtf8);
1124
- }else if( blob_size(pBlob)>1 && (blob_size(pBlob)&1)==0
1125
- && memcmp(blob_buffer(pBlob), &urbom, 2)==0 ) {
1126
- unsigned int i = blob_size(pBlob);
1127
- zUtf8 = blob_buffer(pBlob);
1128
- while( i > 0 ){
1129
- /* swap bytes of unicode representation */
1130
- char temp = zUtf8[--i];
1131
- zUtf8[i] = zUtf8[i-1];
1132
- zUtf8[--i] = temp;
1133
- }
1134
- /* Make sure the blob contains two terminating 0-bytes */
1135
- blob_append(pBlob, "", 1);
1136
- zUtf8 = blob_str(pBlob) + 2;
1137
- zUtf8 = fossil_unicode_to_utf8(zUtf8);
1138
- blob_zero(pBlob);
1139
- blob_append(pBlob, zUtf8, -1);
1140
- fossil_mbcs_free(zUtf8);
1141
- }else if (useMbcs) {
1142
- zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1143
- blob_zero(pBlob);
1093
+** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there
1094
+** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is
1095
+** done. If useMbcs is false and there is no BOM, the input string is assumed
1096
+** to be UTF-8 already, so no conversion is done.
1097
+*/
1098
+void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
1099
+ char *zUtf8;
1100
+ int bomSize = 0;
1101
+ if( starts_with_utf8_bom(pBlob, &bomSize) ){
1102
+ struct Blob temp;
1103
+ zUtf8 = blob_str(pBlob) + bomSize;
1104
+ blob_zero(&temp);
1105
+ blob_append(&temp, zUtf8, -1);
1106
+ blob_swap(pBlob, &temp);
1107
+ blob_reset(&temp);
1108
+#ifdef _WIN32
1109
+ }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){
1110
+ /* Make sure the blob contains two terminating 0-bytes */
1111
+ blob_append(pBlob, "", 1);
1112
+ zUtf8 = blob_str(pBlob) + bomSize;
1113
+ zUtf8 = fossil_unicode_to_utf8(zUtf8);
1114
+ blob_zero(pBlob);
1115
+ blob_append(pBlob, zUtf8, -1);
1116
+ fossil_mbcs_free(zUtf8);
1117
+ }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){
1118
+ unsigned int i = blob_size(pBlob);
1119
+ zUtf8 = blob_buffer(pBlob);
1120
+ while( i > 0 ){
1121
+ /* swap bytes of unicode representation */
1122
+ char zTemp = zUtf8[--i];
1123
+ zUtf8[i] = zUtf8[i-1];
1124
+ zUtf8[--i] = zTemp;
1125
+ }
1126
+ /* Make sure the blob contains two terminating 0-bytes */
1127
+ blob_append(pBlob, "", 1);
1128
+ zUtf8 = blob_str(pBlob) + bomSize;
1129
+ zUtf8 = fossil_unicode_to_utf8(zUtf8);
1130
+ blob_zero(pBlob);
1131
+ blob_append(pBlob, zUtf8, -1);
1132
+ fossil_mbcs_free(zUtf8);
1133
+ }else if( useMbcs ){
1134
+ zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1135
+ blob_reset(pBlob);
11441136
blob_append(pBlob, zUtf8, -1);
11451137
fossil_mbcs_free(zUtf8);
11461138
#endif /* _WIN32 */
11471139
}
11481140
}
11491141
--- src/blob.c
+++ src/blob.c
@@ -1088,61 +1088,53 @@
1088 *pLeft = *pRight;
1089 *pRight = swap;
1090 }
1091
1092 /*
1093 ** Strip a possible BOM from the blob. On Windows, if there
1094 ** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion
1095 ** to UTF-8 is done.
1096 ** If useMbcs is false and there is no BOM, the input string
1097 ** is assumed to be UTF-8 already, so no conversion is done.
1098 */
1099 void blob_strip_bom(Blob *pBlob, int useMbcs){
1100 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
1101 #ifdef _WIN32
1102 static const unsigned short ubom = 0xfeff;
1103 static const unsigned short urbom = 0xfffe;
1104 #endif /* _WIN32 */
1105 char *zUtf8;
1106 if( blob_size(pBlob)>2 && memcmp(blob_buffer(pBlob), bom, 3)==0 ) {
1107 struct Blob temp;
1108 zUtf8 = blob_str(pBlob) + 3;
1109 blob_zero(&temp);
1110 blob_append(&temp, zUtf8, -1);
1111 fossil_mbcs_free(zUtf8);
1112 blob_swap(pBlob, &temp);
1113 blob_reset(&temp);
1114 #ifdef _WIN32
1115 }else if( blob_size(pBlob)>1 && (blob_size(pBlob)&1)==0
1116 && memcmp(blob_buffer(pBlob), &ubom, 2)==0 ) {
1117 /* Make sure the blob contains two terminating 0-bytes */
1118 blob_append(pBlob, "", 1);
1119 zUtf8 = blob_str(pBlob) + 2;
1120 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1121 blob_zero(pBlob);
1122 blob_append(pBlob, zUtf8, -1);
1123 fossil_mbcs_free(zUtf8);
1124 }else if( blob_size(pBlob)>1 && (blob_size(pBlob)&1)==0
1125 && memcmp(blob_buffer(pBlob), &urbom, 2)==0 ) {
1126 unsigned int i = blob_size(pBlob);
1127 zUtf8 = blob_buffer(pBlob);
1128 while( i > 0 ){
1129 /* swap bytes of unicode representation */
1130 char temp = zUtf8[--i];
1131 zUtf8[i] = zUtf8[i-1];
1132 zUtf8[--i] = temp;
1133 }
1134 /* Make sure the blob contains two terminating 0-bytes */
1135 blob_append(pBlob, "", 1);
1136 zUtf8 = blob_str(pBlob) + 2;
1137 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1138 blob_zero(pBlob);
1139 blob_append(pBlob, zUtf8, -1);
1140 fossil_mbcs_free(zUtf8);
1141 }else if (useMbcs) {
1142 zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1143 blob_zero(pBlob);
1144 blob_append(pBlob, zUtf8, -1);
1145 fossil_mbcs_free(zUtf8);
1146 #endif /* _WIN32 */
1147 }
1148 }
1149
--- src/blob.c
+++ src/blob.c
@@ -1088,61 +1088,53 @@
1088 *pLeft = *pRight;
1089 *pRight = swap;
1090 }
1091
1092 /*
1093 ** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there
1094 ** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is
1095 ** done. If useMbcs is false and there is no BOM, the input string is assumed
1096 ** to be UTF-8 already, so no conversion is done.
1097 */
1098 void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
1099 char *zUtf8;
1100 int bomSize = 0;
1101 if( starts_with_utf8_bom(pBlob, &bomSize) ){
1102 struct Blob temp;
1103 zUtf8 = blob_str(pBlob) + bomSize;
1104 blob_zero(&temp);
1105 blob_append(&temp, zUtf8, -1);
1106 blob_swap(pBlob, &temp);
1107 blob_reset(&temp);
1108 #ifdef _WIN32
1109 }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){
1110 /* Make sure the blob contains two terminating 0-bytes */
1111 blob_append(pBlob, "", 1);
1112 zUtf8 = blob_str(pBlob) + bomSize;
1113 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1114 blob_zero(pBlob);
1115 blob_append(pBlob, zUtf8, -1);
1116 fossil_mbcs_free(zUtf8);
1117 }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){
1118 unsigned int i = blob_size(pBlob);
1119 zUtf8 = blob_buffer(pBlob);
1120 while( i > 0 ){
1121 /* swap bytes of unicode representation */
1122 char zTemp = zUtf8[--i];
1123 zUtf8[i] = zUtf8[i-1];
1124 zUtf8[--i] = zTemp;
1125 }
1126 /* Make sure the blob contains two terminating 0-bytes */
1127 blob_append(pBlob, "", 1);
1128 zUtf8 = blob_str(pBlob) + bomSize;
1129 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1130 blob_zero(pBlob);
1131 blob_append(pBlob, zUtf8, -1);
1132 fossil_mbcs_free(zUtf8);
1133 }else if( useMbcs ){
1134 zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
1135 blob_reset(pBlob);
 
 
 
 
 
 
 
 
1136 blob_append(pBlob, zUtf8, -1);
1137 fossil_mbcs_free(zUtf8);
1138 #endif /* _WIN32 */
1139 }
1140 }
1141
+1 -1
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176176
177177
/* Commit */
178178
db_end_transaction(0);
179179
180180
/* Do an autosync push, if requested */
181
- if( !isPrivate ) autosync(AUTOSYNC_PUSH);
181
+ if( !isPrivate ) autosync(SYNC_PUSH);
182182
}
183183
184184
/*
185185
** Prepare a query that will list branches.
186186
**
187187
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176
177 /* Commit */
178 db_end_transaction(0);
179
180 /* Do an autosync push, if requested */
181 if( !isPrivate ) autosync(AUTOSYNC_PUSH);
182 }
183
184 /*
185 ** Prepare a query that will list branches.
186 **
187
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176
177 /* Commit */
178 db_end_transaction(0);
179
180 /* Do an autosync push, if requested */
181 if( !isPrivate ) autosync(SYNC_PUSH);
182 }
183
184 /*
185 ** Prepare a query that will list branches.
186 **
187
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438438
z = blob_buffer(&b);
439439
memcpy(zRes, z, 8);
440440
zRes[8] = 0;
441441
return zRes;
442442
}
443
+
444
+/*
445
+** Return true if a CAPTCHA is required for editing wiki or tickets or for
446
+** adding attachments.
447
+**
448
+** A CAPTCHA is required in those cases if the user is not logged in (if they
449
+** are user "nobody") and if the "require-captcha" setting is true. The
450
+** "require-captcha" setting is controlled on the Admin/Access page. It
451
+** defaults to true.
452
+*/
453
+int captcha_needed(void){
454
+ if( g.zLogin!=0 ) return 0;
455
+ return db_get_boolean("require-captcha", 1);
456
+}
457
+
458
+/*
459
+** If a captcha is required but the correct captcha code is not supplied
460
+** in the query parameters, then return false (0).
461
+**
462
+** If no captcha is required or if the correct captcha is supplied, return
463
+** true (non-zero).
464
+**
465
+** The query parameters examined are "captchaseed" for the seed value and
466
+** "captcha" for text that the user types in response to the captcha prompt.
467
+*/
468
+int captcha_is_correct(void){
469
+ const char *zSeed;
470
+ const char *zEntered;
471
+ const char *zDecode;
472
+ if( !captcha_needed() ){
473
+ return 1; /* No captcha needed */
474
+ }
475
+ zSeed = P("captchaseed");
476
+ if( zSeed==0 ) return 0;
477
+ zEntered = P("captcha");
478
+ if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
479
+ zDecode = captcha_decode((unsigned int)atoi(zSeed));
480
+ if( strcmp(zDecode,zEntered)!=0 ) return 0;
481
+ return 1;
482
+}
483
+
484
+/*
485
+** Generate a captcha display together with the necessary hidden parameter
486
+** for the seed and the entry box into which the user will type the text of
487
+** the captcha. This is typically done at the very bottom of a form.
488
+**
489
+** This routine is a no-op if no captcha is required.
490
+*/
491
+void captcha_generate(void){
492
+ unsigned int uSeed;
493
+ const char *zDecoded;
494
+ char *zCaptcha;
495
+
496
+ if( !captcha_needed() ) return;
497
+ uSeed = captcha_seed();
498
+ zDecoded = captcha_decode(uSeed);
499
+ zCaptcha = captcha_render(zDecoded);
500
+ @ <div class="captcha"><table class="captcha"><tr><td><pre>
501
+ @ %h(zCaptcha)
502
+ @ </pre>
503
+ @ Enter security code shown above:
504
+ @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
505
+ @ <input type="text" name="captcha" size=8 />
506
+ @ </td></tr></table></div>
507
+}
443508
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438 z = blob_buffer(&b);
439 memcpy(zRes, z, 8);
440 zRes[8] = 0;
441 return zRes;
442 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438 z = blob_buffer(&b);
439 memcpy(zRes, z, 8);
440 zRes[8] = 0;
441 return zRes;
442 }
443
444 /*
445 ** Return true if a CAPTCHA is required for editing wiki or tickets or for
446 ** adding attachments.
447 **
448 ** A CAPTCHA is required in those cases if the user is not logged in (if they
449 ** are user "nobody") and if the "require-captcha" setting is true. The
450 ** "require-captcha" setting is controlled on the Admin/Access page. It
451 ** defaults to true.
452 */
453 int captcha_needed(void){
454 if( g.zLogin!=0 ) return 0;
455 return db_get_boolean("require-captcha", 1);
456 }
457
458 /*
459 ** If a captcha is required but the correct captcha code is not supplied
460 ** in the query parameters, then return false (0).
461 **
462 ** If no captcha is required or if the correct captcha is supplied, return
463 ** true (non-zero).
464 **
465 ** The query parameters examined are "captchaseed" for the seed value and
466 ** "captcha" for text that the user types in response to the captcha prompt.
467 */
468 int captcha_is_correct(void){
469 const char *zSeed;
470 const char *zEntered;
471 const char *zDecode;
472 if( !captcha_needed() ){
473 return 1; /* No captcha needed */
474 }
475 zSeed = P("captchaseed");
476 if( zSeed==0 ) return 0;
477 zEntered = P("captcha");
478 if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
479 zDecode = captcha_decode((unsigned int)atoi(zSeed));
480 if( strcmp(zDecode,zEntered)!=0 ) return 0;
481 return 1;
482 }
483
484 /*
485 ** Generate a captcha display together with the necessary hidden parameter
486 ** for the seed and the entry box into which the user will type the text of
487 ** the captcha. This is typically done at the very bottom of a form.
488 **
489 ** This routine is a no-op if no captcha is required.
490 */
491 void captcha_generate(void){
492 unsigned int uSeed;
493 const char *zDecoded;
494 char *zCaptcha;
495
496 if( !captcha_needed() ) return;
497 uSeed = captcha_seed();
498 zDecoded = captcha_decode(uSeed);
499 zCaptcha = captcha_render(zDecoded);
500 @ <div class="captcha"><table class="captcha"><tr><td><pre>
501 @ %h(zCaptcha)
502 @ </pre>
503 @ Enter security code shown above:
504 @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
505 @ <input type="text" name="captcha" size=8 />
506 @ </td></tr></table></div>
507 }
508
+73 -36
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521521
break;
522522
}
523523
blob_append(&reply, zIn, -1);
524524
}
525525
}
526
- blob_strip_bom(&reply, 1);
526
+ blob_to_utf8_no_bom(&reply, 1);
527527
blob_remove_cr(&reply);
528528
file_delete(zFile);
529529
free(zFile);
530530
blob_zero(pComment);
531531
while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570570
int parent_rid,
571571
const char *zUserOvrd
572572
){
573573
Blob prompt;
574574
#ifdef _WIN32
575
- static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
576
- blob_init(&prompt, (const char *) bom, 3);
575
+ int bomSize;
576
+ const unsigned char *bom = get_utf8_bom(&bomSize);
577
+ blob_init(&prompt, (const char *) bom, bomSize);
577578
if( zInit && zInit[0]) {
578579
blob_append(&prompt, zInit, -1);
579580
}
580581
#else
581582
blob_init(&prompt, zInit, -1);
582583
#endif
583584
blob_append(&prompt,
584585
"\n"
585586
"# Enter comments on this check-in. Lines beginning with # are ignored.\n"
586
- "# The check-in comment follows wiki formatting rules.\n"
587587
"#\n", -1
588588
);
589589
blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590590
if( zBranch && zBranch[0] ){
591591
blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657657
" AND type='ci' AND objid=%d",
658658
zDate, rid
659659
);
660660
if( b ){
661661
fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662
- " Use -f to override.", zUuid, zDate);
662
+ " Use --allow-older to override.", zUuid, zDate);
663663
}
664664
#endif
665665
}
666666
667667
/*
@@ -891,20 +891,21 @@
891891
*/
892892
static void commit_warning(
893893
const Blob *p, /* The content of the file being committed. */
894894
int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895895
int binOk, /* Non-zero if binary warnings should be disabled. */
896
+ int unicodeOk, /* Non-zero if unicode warnings should be disabled. */
896897
const char *zFilename /* The full name of the file being committed. */
897898
){
898899
int eType; /* return value of looks_like_utf8/utf16() */
899900
int fUnicode; /* return value of starts_with_utf16_bom() */
900901
char *zMsg; /* Warning message */
901902
Blob fname; /* Relative pathname of the file */
902903
static int allOk = 0; /* Set to true to disable this routine */
903904
904905
if( allOk ) return;
905
- fUnicode = starts_with_utf16_bom(p);
906
+ fUnicode = starts_with_utf16_bom(p, 0);
906907
eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
907908
if( eType<-2){
908909
const char *zWarning;
909910
Blob ans;
910911
char cReply;
@@ -933,10 +934,13 @@
933934
const char *zWarning;
934935
Blob ans;
935936
char cReply;
936937
937938
if( eType==-1 && fUnicode ){
939
+ if ( crnlOk && unicodeOk ){
940
+ return; /* We don't want Unicode/CR/NL warnings for this file. */
941
+ }
938942
zWarning = "Unicode and CR/NL line endings";
939943
}else if( eType==-1 ){
940944
if( crnlOk ){
941945
return; /* We don't want CR/NL warnings for this file. */
942946
}
@@ -945,10 +949,13 @@
945949
if( binOk ){
946950
return; /* We don't want binary warnings for this file. */
947951
}
948952
zWarning = "binary data";
949953
}else{
954
+ if ( unicodeOk ){
955
+ return; /* We don't want unicode warnings for this file. */
956
+ }
950957
zWarning = "Unicode";
951958
}
952959
file_relative_name(zFilename, &fname, 0);
953960
blob_zero(&ans);
954961
zMsg = mprintf(
@@ -1006,32 +1013,45 @@
10061013
**
10071014
** The --bgcolor option works like --branchcolor but only sets the
10081015
** background color for a single check-in. Subsequent check-ins revert
10091016
** to the default color.
10101017
**
1011
-** A check-in is not permitted to fork unless the --force or -f
1012
-** option appears. A check-in is not allowed against a closed leaf.
1018
+** A check-in is not permitted to fork unless the --allow-fork option
1019
+** appears. An empty check-in (i.e. with nothing changed) is not
1020
+** allowed unless the --allow-empty option appears. A check-in may not
1021
+** be older than its ancestor unless the --allow-older option appears.
1022
+** If any of files in the check-in appear to contain unresolved merge
1023
+** conflicts, the check-in will not be allowed unless the
1024
+** --allow-conflict option is present. In addition, the entire
1025
+** check-in process may be aborted if a file contains content that
1026
+** appears to be binary, Unicode text, or text with CR/NL line endings
1027
+** unless the interactive user chooses to proceed. If there is no
1028
+** interactive user or these warnings should be skipped for some other
1029
+** reason, the --no-warnings option may be used. A check-in is not
1030
+** allowed against a closed leaf.
10131031
**
10141032
** The --private option creates a private check-in that is never synced.
10151033
** Children of private check-ins are automatically private.
10161034
**
10171035
** the --tag option applies the symbolic tag name to the check-in.
10181036
**
10191037
** Options:
1038
+** --allow-conflict allow unresolved merge conflicts
1039
+** --allow-empty allow a commit with no changes
1040
+** --allow-fork allow the commit to fork
1041
+** --allow-older allow a commit older than its ancestor
10201042
** --baseline use a baseline manifest in the commit process
10211043
** --bgcolor COLOR apply COLOR to this one check-in only
10221044
** --branch NEW-BRANCH-NAME check in to this new branch
10231045
** --branchcolor COLOR apply given COLOR to the branch
10241046
** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
10251047
** --delta use a delta manifest in the commit process
1026
-** --force|-f allow forking with this commit
10271048
** --message-file|-M FILE read the commit comment from given file
1049
+** --no-warnings omit all warnings about file contents
10281050
** --nosign do not attempt to sign this commit with gpg
10291051
** --private do not sync changes and their descendants
10301052
** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1031
-** --conflict allow unresolved merge conflicts
1032
-** --binary-ok do not warn about committing binary files
10331053
**
10341054
** See also: branch, changes, checkout, extra, sync
10351055
*/
10361056
void commit_cmd(void){
10371057
int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
10421062
const char *zComment; /* Check-in comment */
10431063
Stmt q; /* Query to find files that have been modified */
10441064
char *zUuid; /* UUID of the new check-in */
10451065
int noSign = 0; /* True to omit signing the manifest using GPG */
10461066
int isAMerge = 0; /* True if checking in a merge */
1047
- int forceFlag = 0; /* Force a fork */
1067
+ int noWarningFlag = 0; /* True if skipping all warnings */
1068
+ int forceFlag = 0; /* Undocumented: Disables all checks */
10481069
int forceDelta = 0; /* Force a delta-manifest */
10491070
int forceBaseline = 0; /* Force a baseline-manifest */
10501071
int allowConflict = 0; /* Allow unresolve merge conflicts */
1051
- int binaryOk = 0; /* The --binary-ok flag */
1072
+ int allowEmpty = 0; /* Allow a commit with no changes */
1073
+ int allowFork = 0; /* Allow the commit to fork */
1074
+ int allowOlder = 0; /* Allow a commit older than its ancestor */
10521075
char *zManifestFile; /* Name of the manifest file */
10531076
int useCksum; /* True if checksums should be computed and verified */
10541077
int outputManifest; /* True to output "manifest" and "manifest.uuid" */
10551078
int testRun; /* True for a test run. Debugging only */
10561079
const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
10801103
fossil_fatal("cannot use --delta and --baseline together");
10811104
}
10821105
testRun = find_option("test",0,0)!=0;
10831106
zComment = find_option("comment","m",1);
10841107
forceFlag = find_option("force", "f", 0)!=0;
1108
+ allowConflict = find_option("allow-conflict",0,0)!=0;
1109
+ allowEmpty = find_option("allow-empty",0,0)!=0;
1110
+ allowFork = find_option("allow-fork",0,0)!=0;
1111
+ allowOlder = find_option("allow-older",0,0)!=0;
1112
+ noWarningFlag = find_option("no-warnings", 0, 0)!=0;
10851113
zBranch = find_option("branch","b",1);
10861114
zColor = find_option("bgcolor",0,1);
10871115
zBrClr = find_option("branchcolor",0,1);
1088
- binaryOk = find_option("binary-ok",0,0)!=0;
10891116
while( (zTag = find_option("tag",0,1))!=0 ){
10901117
if( zTag[0]==0 ) continue;
10911118
azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
10921119
azTag[nTag++] = zTag;
10931120
azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
10981125
if( zBranch==0 ) zBranch = "private";
10991126
if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
11001127
}
11011128
zDateOvrd = find_option("date-override",0,1);
11021129
zUserOvrd = find_option("user-override",0,1);
1103
- allowConflict = find_option("conflict",0,0)!=0;
11041130
db_must_be_within_tree();
11051131
noSign = db_get_boolean("omitsign", 0)|noSign;
11061132
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
11071133
useCksum = db_get_boolean("repo-cksum", 1);
11081134
outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
11331159
11341160
/*
11351161
** Autosync if autosync is enabled and this is not a private check-in.
11361162
*/
11371163
if( !g.markPrivate ){
1138
- if( autosync(AUTOSYNC_PULL) ){
1164
+ if( autosync(SYNC_PULL) ){
11391165
blob_zero(&ans);
11401166
prompt_user("continue in spite of sync failure (y/N)? ", &ans);
11411167
cReply = blob_str(&ans)[0];
11421168
if( cReply!='y' && cReply!='Y' ){
11431169
fossil_exit(1);
@@ -1205,34 +1231,38 @@
12051231
}
12061232
12071233
hasChanges = unsaved_changes();
12081234
db_begin_transaction();
12091235
db_record_repository_filename(0);
1210
- if( hasChanges==0 && !isAMerge && !forceFlag ){
1211
- fossil_fatal("nothing has changed");
1236
+ if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1237
+ fossil_fatal("nothing has changed; use --allow-empty to override");
12121238
}
12131239
12141240
/* If none of the files that were named on the command line have
1215
- ** been modified, bail out now unless the --force flag is used.
1241
+ ** been modified, bail out now unless the --allow-empty or --force
1242
+ ** flags is used.
12161243
*/
12171244
if( g.aCommitFile
1245
+ && !allowEmpty
12181246
&& !forceFlag
12191247
&& !db_exists(
12201248
"SELECT 1 FROM vfile "
12211249
" WHERE is_selected(id)"
12221250
" AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
12231251
){
1224
- fossil_fatal("none of the selected files have changed; use -f"
1225
- " or --force.");
1252
+ fossil_fatal("none of the selected files have changed; use "
1253
+ "--allow-empty to override.");
12261254
}
12271255
12281256
/*
1229
- ** Do not allow a commit that will cause a fork unless the --force flag
1230
- ** is used or unless this is a private check-in.
1257
+ ** Do not allow a commit that will cause a fork unless the --allow-fork
1258
+ ** or --force flags is used, or unless this is a private check-in.
12311259
*/
1232
- if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){
1233
- fossil_fatal("would fork. \"update\" first or use -f or --force.");
1260
+ if( zBranch==0 && allowFork==0 && forceFlag==0
1261
+ && g.markPrivate==0 && !is_a_leaf(vid)
1262
+ ){
1263
+ fossil_fatal("would fork. \"update\" first or use --allow-fork.");
12341264
}
12351265
12361266
/*
12371267
** Do not allow a commit against a closed leaf
12381268
*/
@@ -1247,11 +1277,11 @@
12471277
blob_zero(&comment);
12481278
blob_append(&comment, zComment, -1);
12491279
}else if( zComFile ){
12501280
blob_zero(&comment);
12511281
blob_read_from_file(&comment, zComFile);
1252
- blob_strip_bom(&comment, 1);
1282
+ blob_to_utf8_no_bom(&comment, 1);
12531283
}else{
12541284
char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
12551285
prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
12561286
if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
12571287
blob_zero(&ans);
@@ -1277,36 +1307,42 @@
12771307
/* Step 1: Insert records for all modified files into the blob
12781308
** table. If there were arguments passed to this command, only
12791309
** the identified files are inserted (if they have been modified).
12801310
*/
12811311
db_prepare(&q,
1282
- "SELECT id, %Q || pathname, mrid, %s, chnged, %s FROM vfile "
1312
+ "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile "
12831313
"WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1284
- g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")),
1285
- glob_expr("pathname", db_get("binary-glob",""))
1314
+ g.zLocalRoot,
1315
+ glob_expr("pathname", db_get("crnl-glob","")),
1316
+ glob_expr("pathname", db_get("binary-glob","")),
1317
+ glob_expr("pathname", db_get("unicode-glob",""))
12861318
);
12871319
while( db_step(&q)==SQLITE_ROW ){
12881320
int id, rid;
12891321
const char *zFullname;
12901322
Blob content;
1291
- int crnlOk, binOk, chnged;
1323
+ int crnlOk, binOk, unicodeOk, chnged;
12921324
12931325
id = db_column_int(&q, 0);
12941326
zFullname = db_column_text(&q, 1);
12951327
rid = db_column_int(&q, 2);
12961328
crnlOk = db_column_int(&q, 3);
12971329
chnged = db_column_int(&q, 4);
1298
- binOk = binaryOk || db_column_int(&q, 5);
1330
+ binOk = db_column_int(&q, 5);
1331
+ unicodeOk = db_column_int(&q, 6);
12991332
13001333
blob_zero(&content);
13011334
if( file_wd_islink(zFullname) ){
13021335
/* Instead of file content, put link destination path */
13031336
blob_read_link(&content, zFullname);
13041337
}else{
13051338
blob_read_from_file(&content, zFullname);
13061339
}
1307
- commit_warning(&content, crnlOk, binOk, zFullname);
1340
+ /* Do not emit any warnings when they are disabled. */
1341
+ if( !noWarningFlag ){
1342
+ commit_warning(&content, crnlOk, binOk, unicodeOk, zFullname);
1343
+ }
13081344
if( chnged==1 && contains_merge_marker(&content) ){
13091345
Blob fname; /* Relative pathname of the file */
13101346
13111347
nConflict++;
13121348
file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
13221358
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
13231359
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
13241360
}
13251361
db_finalize(&q);
13261362
if( nConflict && !allowConflict ){
1327
- fossil_fatal("abort due to unresolve merge conflicts");
1363
+ fossil_fatal("abort due to unresolved merge conflicts; "
1364
+ "use --allow-conflict to override");
13281365
}
13291366
13301367
/* Create the new manifest */
13311368
if( blob_size(&comment)==0 ){
13321369
blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
13331370
}
13341371
if( forceDelta ){
13351372
blob_zero(&manifest);
13361373
}else{
13371374
create_manifest(&manifest, 0, 0, &comment, vid,
1338
- !forceFlag, useCksum ? &cksum1 : 0,
1375
+ !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
13391376
zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
13401377
azTag, &szB);
13411378
}
13421379
13431380
/* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
13541391
pBaseline = pParent;
13551392
}
13561393
if( pBaseline ){
13571394
Blob delta;
13581395
create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1359
- !forceFlag, useCksum ? &cksum1 : 0,
1396
+ !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
13601397
zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
13611398
azTag, &szD);
13621399
/*
13631400
** At this point, two manifests have been constructed, either of
13641401
** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
14841521
exit(1);
14851522
}
14861523
db_end_transaction(0);
14871524
14881525
if( !g.markPrivate ){
1489
- autosync(AUTOSYNC_PUSH);
1526
+ autosync(SYNC_PUSH);
14901527
}
14911528
if( count_nonbranch_children(vid)>1 ){
14921529
fossil_print("**** warning: a fork has occurred *****\n");
14931530
}
14941531
}
14951532
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521 break;
522 }
523 blob_append(&reply, zIn, -1);
524 }
525 }
526 blob_strip_bom(&reply, 1);
527 blob_remove_cr(&reply);
528 file_delete(zFile);
529 free(zFile);
530 blob_zero(pComment);
531 while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570 int parent_rid,
571 const char *zUserOvrd
572 ){
573 Blob prompt;
574 #ifdef _WIN32
575 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
576 blob_init(&prompt, (const char *) bom, 3);
 
577 if( zInit && zInit[0]) {
578 blob_append(&prompt, zInit, -1);
579 }
580 #else
581 blob_init(&prompt, zInit, -1);
582 #endif
583 blob_append(&prompt,
584 "\n"
585 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
586 "# The check-in comment follows wiki formatting rules.\n"
587 "#\n", -1
588 );
589 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590 if( zBranch && zBranch[0] ){
591 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657 " AND type='ci' AND objid=%d",
658 zDate, rid
659 );
660 if( b ){
661 fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662 " Use -f to override.", zUuid, zDate);
663 }
664 #endif
665 }
666
667 /*
@@ -891,20 +891,21 @@
891 */
892 static void commit_warning(
893 const Blob *p, /* The content of the file being committed. */
894 int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895 int binOk, /* Non-zero if binary warnings should be disabled. */
 
896 const char *zFilename /* The full name of the file being committed. */
897 ){
898 int eType; /* return value of looks_like_utf8/utf16() */
899 int fUnicode; /* return value of starts_with_utf16_bom() */
900 char *zMsg; /* Warning message */
901 Blob fname; /* Relative pathname of the file */
902 static int allOk = 0; /* Set to true to disable this routine */
903
904 if( allOk ) return;
905 fUnicode = starts_with_utf16_bom(p);
906 eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
907 if( eType<-2){
908 const char *zWarning;
909 Blob ans;
910 char cReply;
@@ -933,10 +934,13 @@
933 const char *zWarning;
934 Blob ans;
935 char cReply;
936
937 if( eType==-1 && fUnicode ){
 
 
 
938 zWarning = "Unicode and CR/NL line endings";
939 }else if( eType==-1 ){
940 if( crnlOk ){
941 return; /* We don't want CR/NL warnings for this file. */
942 }
@@ -945,10 +949,13 @@
945 if( binOk ){
946 return; /* We don't want binary warnings for this file. */
947 }
948 zWarning = "binary data";
949 }else{
 
 
 
950 zWarning = "Unicode";
951 }
952 file_relative_name(zFilename, &fname, 0);
953 blob_zero(&ans);
954 zMsg = mprintf(
@@ -1006,32 +1013,45 @@
1006 **
1007 ** The --bgcolor option works like --branchcolor but only sets the
1008 ** background color for a single check-in. Subsequent check-ins revert
1009 ** to the default color.
1010 **
1011 ** A check-in is not permitted to fork unless the --force or -f
1012 ** option appears. A check-in is not allowed against a closed leaf.
 
 
 
 
 
 
 
 
 
 
 
1013 **
1014 ** The --private option creates a private check-in that is never synced.
1015 ** Children of private check-ins are automatically private.
1016 **
1017 ** the --tag option applies the symbolic tag name to the check-in.
1018 **
1019 ** Options:
 
 
 
 
1020 ** --baseline use a baseline manifest in the commit process
1021 ** --bgcolor COLOR apply COLOR to this one check-in only
1022 ** --branch NEW-BRANCH-NAME check in to this new branch
1023 ** --branchcolor COLOR apply given COLOR to the branch
1024 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
1025 ** --delta use a delta manifest in the commit process
1026 ** --force|-f allow forking with this commit
1027 ** --message-file|-M FILE read the commit comment from given file
 
1028 ** --nosign do not attempt to sign this commit with gpg
1029 ** --private do not sync changes and their descendants
1030 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1031 ** --conflict allow unresolved merge conflicts
1032 ** --binary-ok do not warn about committing binary files
1033 **
1034 ** See also: branch, changes, checkout, extra, sync
1035 */
1036 void commit_cmd(void){
1037 int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
1042 const char *zComment; /* Check-in comment */
1043 Stmt q; /* Query to find files that have been modified */
1044 char *zUuid; /* UUID of the new check-in */
1045 int noSign = 0; /* True to omit signing the manifest using GPG */
1046 int isAMerge = 0; /* True if checking in a merge */
1047 int forceFlag = 0; /* Force a fork */
 
1048 int forceDelta = 0; /* Force a delta-manifest */
1049 int forceBaseline = 0; /* Force a baseline-manifest */
1050 int allowConflict = 0; /* Allow unresolve merge conflicts */
1051 int binaryOk = 0; /* The --binary-ok flag */
 
 
1052 char *zManifestFile; /* Name of the manifest file */
1053 int useCksum; /* True if checksums should be computed and verified */
1054 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
1055 int testRun; /* True for a test run. Debugging only */
1056 const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
1080 fossil_fatal("cannot use --delta and --baseline together");
1081 }
1082 testRun = find_option("test",0,0)!=0;
1083 zComment = find_option("comment","m",1);
1084 forceFlag = find_option("force", "f", 0)!=0;
 
 
 
 
 
1085 zBranch = find_option("branch","b",1);
1086 zColor = find_option("bgcolor",0,1);
1087 zBrClr = find_option("branchcolor",0,1);
1088 binaryOk = find_option("binary-ok",0,0)!=0;
1089 while( (zTag = find_option("tag",0,1))!=0 ){
1090 if( zTag[0]==0 ) continue;
1091 azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
1092 azTag[nTag++] = zTag;
1093 azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
1098 if( zBranch==0 ) zBranch = "private";
1099 if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
1100 }
1101 zDateOvrd = find_option("date-override",0,1);
1102 zUserOvrd = find_option("user-override",0,1);
1103 allowConflict = find_option("conflict",0,0)!=0;
1104 db_must_be_within_tree();
1105 noSign = db_get_boolean("omitsign", 0)|noSign;
1106 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
1107 useCksum = db_get_boolean("repo-cksum", 1);
1108 outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
1133
1134 /*
1135 ** Autosync if autosync is enabled and this is not a private check-in.
1136 */
1137 if( !g.markPrivate ){
1138 if( autosync(AUTOSYNC_PULL) ){
1139 blob_zero(&ans);
1140 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1141 cReply = blob_str(&ans)[0];
1142 if( cReply!='y' && cReply!='Y' ){
1143 fossil_exit(1);
@@ -1205,34 +1231,38 @@
1205 }
1206
1207 hasChanges = unsaved_changes();
1208 db_begin_transaction();
1209 db_record_repository_filename(0);
1210 if( hasChanges==0 && !isAMerge && !forceFlag ){
1211 fossil_fatal("nothing has changed");
1212 }
1213
1214 /* If none of the files that were named on the command line have
1215 ** been modified, bail out now unless the --force flag is used.
 
1216 */
1217 if( g.aCommitFile
 
1218 && !forceFlag
1219 && !db_exists(
1220 "SELECT 1 FROM vfile "
1221 " WHERE is_selected(id)"
1222 " AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
1223 ){
1224 fossil_fatal("none of the selected files have changed; use -f"
1225 " or --force.");
1226 }
1227
1228 /*
1229 ** Do not allow a commit that will cause a fork unless the --force flag
1230 ** is used or unless this is a private check-in.
1231 */
1232 if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){
1233 fossil_fatal("would fork. \"update\" first or use -f or --force.");
 
 
1234 }
1235
1236 /*
1237 ** Do not allow a commit against a closed leaf
1238 */
@@ -1247,11 +1277,11 @@
1247 blob_zero(&comment);
1248 blob_append(&comment, zComment, -1);
1249 }else if( zComFile ){
1250 blob_zero(&comment);
1251 blob_read_from_file(&comment, zComFile);
1252 blob_strip_bom(&comment, 1);
1253 }else{
1254 char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
1255 prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
1256 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
1257 blob_zero(&ans);
@@ -1277,36 +1307,42 @@
1277 /* Step 1: Insert records for all modified files into the blob
1278 ** table. If there were arguments passed to this command, only
1279 ** the identified files are inserted (if they have been modified).
1280 */
1281 db_prepare(&q,
1282 "SELECT id, %Q || pathname, mrid, %s, chnged, %s FROM vfile "
1283 "WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1284 g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")),
1285 glob_expr("pathname", db_get("binary-glob",""))
 
 
1286 );
1287 while( db_step(&q)==SQLITE_ROW ){
1288 int id, rid;
1289 const char *zFullname;
1290 Blob content;
1291 int crnlOk, binOk, chnged;
1292
1293 id = db_column_int(&q, 0);
1294 zFullname = db_column_text(&q, 1);
1295 rid = db_column_int(&q, 2);
1296 crnlOk = db_column_int(&q, 3);
1297 chnged = db_column_int(&q, 4);
1298 binOk = binaryOk || db_column_int(&q, 5);
 
1299
1300 blob_zero(&content);
1301 if( file_wd_islink(zFullname) ){
1302 /* Instead of file content, put link destination path */
1303 blob_read_link(&content, zFullname);
1304 }else{
1305 blob_read_from_file(&content, zFullname);
1306 }
1307 commit_warning(&content, crnlOk, binOk, zFullname);
 
 
 
1308 if( chnged==1 && contains_merge_marker(&content) ){
1309 Blob fname; /* Relative pathname of the file */
1310
1311 nConflict++;
1312 file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
1322 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
1323 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
1324 }
1325 db_finalize(&q);
1326 if( nConflict && !allowConflict ){
1327 fossil_fatal("abort due to unresolve merge conflicts");
 
1328 }
1329
1330 /* Create the new manifest */
1331 if( blob_size(&comment)==0 ){
1332 blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
1333 }
1334 if( forceDelta ){
1335 blob_zero(&manifest);
1336 }else{
1337 create_manifest(&manifest, 0, 0, &comment, vid,
1338 !forceFlag, useCksum ? &cksum1 : 0,
1339 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1340 azTag, &szB);
1341 }
1342
1343 /* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
1354 pBaseline = pParent;
1355 }
1356 if( pBaseline ){
1357 Blob delta;
1358 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1359 !forceFlag, useCksum ? &cksum1 : 0,
1360 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1361 azTag, &szD);
1362 /*
1363 ** At this point, two manifests have been constructed, either of
1364 ** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
1484 exit(1);
1485 }
1486 db_end_transaction(0);
1487
1488 if( !g.markPrivate ){
1489 autosync(AUTOSYNC_PUSH);
1490 }
1491 if( count_nonbranch_children(vid)>1 ){
1492 fossil_print("**** warning: a fork has occurred *****\n");
1493 }
1494 }
1495
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521 break;
522 }
523 blob_append(&reply, zIn, -1);
524 }
525 }
526 blob_to_utf8_no_bom(&reply, 1);
527 blob_remove_cr(&reply);
528 file_delete(zFile);
529 free(zFile);
530 blob_zero(pComment);
531 while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570 int parent_rid,
571 const char *zUserOvrd
572 ){
573 Blob prompt;
574 #ifdef _WIN32
575 int bomSize;
576 const unsigned char *bom = get_utf8_bom(&bomSize);
577 blob_init(&prompt, (const char *) bom, bomSize);
578 if( zInit && zInit[0]) {
579 blob_append(&prompt, zInit, -1);
580 }
581 #else
582 blob_init(&prompt, zInit, -1);
583 #endif
584 blob_append(&prompt,
585 "\n"
586 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
 
587 "#\n", -1
588 );
589 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590 if( zBranch && zBranch[0] ){
591 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657 " AND type='ci' AND objid=%d",
658 zDate, rid
659 );
660 if( b ){
661 fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662 " Use --allow-older to override.", zUuid, zDate);
663 }
664 #endif
665 }
666
667 /*
@@ -891,20 +891,21 @@
891 */
892 static void commit_warning(
893 const Blob *p, /* The content of the file being committed. */
894 int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895 int binOk, /* Non-zero if binary warnings should be disabled. */
896 int unicodeOk, /* Non-zero if unicode warnings should be disabled. */
897 const char *zFilename /* The full name of the file being committed. */
898 ){
899 int eType; /* return value of looks_like_utf8/utf16() */
900 int fUnicode; /* return value of starts_with_utf16_bom() */
901 char *zMsg; /* Warning message */
902 Blob fname; /* Relative pathname of the file */
903 static int allOk = 0; /* Set to true to disable this routine */
904
905 if( allOk ) return;
906 fUnicode = starts_with_utf16_bom(p, 0);
907 eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
908 if( eType<-2){
909 const char *zWarning;
910 Blob ans;
911 char cReply;
@@ -933,10 +934,13 @@
934 const char *zWarning;
935 Blob ans;
936 char cReply;
937
938 if( eType==-1 && fUnicode ){
939 if ( crnlOk && unicodeOk ){
940 return; /* We don't want Unicode/CR/NL warnings for this file. */
941 }
942 zWarning = "Unicode and CR/NL line endings";
943 }else if( eType==-1 ){
944 if( crnlOk ){
945 return; /* We don't want CR/NL warnings for this file. */
946 }
@@ -945,10 +949,13 @@
949 if( binOk ){
950 return; /* We don't want binary warnings for this file. */
951 }
952 zWarning = "binary data";
953 }else{
954 if ( unicodeOk ){
955 return; /* We don't want unicode warnings for this file. */
956 }
957 zWarning = "Unicode";
958 }
959 file_relative_name(zFilename, &fname, 0);
960 blob_zero(&ans);
961 zMsg = mprintf(
@@ -1006,32 +1013,45 @@
1013 **
1014 ** The --bgcolor option works like --branchcolor but only sets the
1015 ** background color for a single check-in. Subsequent check-ins revert
1016 ** to the default color.
1017 **
1018 ** A check-in is not permitted to fork unless the --allow-fork option
1019 ** appears. An empty check-in (i.e. with nothing changed) is not
1020 ** allowed unless the --allow-empty option appears. A check-in may not
1021 ** be older than its ancestor unless the --allow-older option appears.
1022 ** If any of files in the check-in appear to contain unresolved merge
1023 ** conflicts, the check-in will not be allowed unless the
1024 ** --allow-conflict option is present. In addition, the entire
1025 ** check-in process may be aborted if a file contains content that
1026 ** appears to be binary, Unicode text, or text with CR/NL line endings
1027 ** unless the interactive user chooses to proceed. If there is no
1028 ** interactive user or these warnings should be skipped for some other
1029 ** reason, the --no-warnings option may be used. A check-in is not
1030 ** allowed against a closed leaf.
1031 **
1032 ** The --private option creates a private check-in that is never synced.
1033 ** Children of private check-ins are automatically private.
1034 **
1035 ** the --tag option applies the symbolic tag name to the check-in.
1036 **
1037 ** Options:
1038 ** --allow-conflict allow unresolved merge conflicts
1039 ** --allow-empty allow a commit with no changes
1040 ** --allow-fork allow the commit to fork
1041 ** --allow-older allow a commit older than its ancestor
1042 ** --baseline use a baseline manifest in the commit process
1043 ** --bgcolor COLOR apply COLOR to this one check-in only
1044 ** --branch NEW-BRANCH-NAME check in to this new branch
1045 ** --branchcolor COLOR apply given COLOR to the branch
1046 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
1047 ** --delta use a delta manifest in the commit process
 
1048 ** --message-file|-M FILE read the commit comment from given file
1049 ** --no-warnings omit all warnings about file contents
1050 ** --nosign do not attempt to sign this commit with gpg
1051 ** --private do not sync changes and their descendants
1052 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
 
 
1053 **
1054 ** See also: branch, changes, checkout, extra, sync
1055 */
1056 void commit_cmd(void){
1057 int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
1062 const char *zComment; /* Check-in comment */
1063 Stmt q; /* Query to find files that have been modified */
1064 char *zUuid; /* UUID of the new check-in */
1065 int noSign = 0; /* True to omit signing the manifest using GPG */
1066 int isAMerge = 0; /* True if checking in a merge */
1067 int noWarningFlag = 0; /* True if skipping all warnings */
1068 int forceFlag = 0; /* Undocumented: Disables all checks */
1069 int forceDelta = 0; /* Force a delta-manifest */
1070 int forceBaseline = 0; /* Force a baseline-manifest */
1071 int allowConflict = 0; /* Allow unresolve merge conflicts */
1072 int allowEmpty = 0; /* Allow a commit with no changes */
1073 int allowFork = 0; /* Allow the commit to fork */
1074 int allowOlder = 0; /* Allow a commit older than its ancestor */
1075 char *zManifestFile; /* Name of the manifest file */
1076 int useCksum; /* True if checksums should be computed and verified */
1077 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
1078 int testRun; /* True for a test run. Debugging only */
1079 const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
1103 fossil_fatal("cannot use --delta and --baseline together");
1104 }
1105 testRun = find_option("test",0,0)!=0;
1106 zComment = find_option("comment","m",1);
1107 forceFlag = find_option("force", "f", 0)!=0;
1108 allowConflict = find_option("allow-conflict",0,0)!=0;
1109 allowEmpty = find_option("allow-empty",0,0)!=0;
1110 allowFork = find_option("allow-fork",0,0)!=0;
1111 allowOlder = find_option("allow-older",0,0)!=0;
1112 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
1113 zBranch = find_option("branch","b",1);
1114 zColor = find_option("bgcolor",0,1);
1115 zBrClr = find_option("branchcolor",0,1);
 
1116 while( (zTag = find_option("tag",0,1))!=0 ){
1117 if( zTag[0]==0 ) continue;
1118 azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
1119 azTag[nTag++] = zTag;
1120 azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
1125 if( zBranch==0 ) zBranch = "private";
1126 if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
1127 }
1128 zDateOvrd = find_option("date-override",0,1);
1129 zUserOvrd = find_option("user-override",0,1);
 
1130 db_must_be_within_tree();
1131 noSign = db_get_boolean("omitsign", 0)|noSign;
1132 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
1133 useCksum = db_get_boolean("repo-cksum", 1);
1134 outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
1159
1160 /*
1161 ** Autosync if autosync is enabled and this is not a private check-in.
1162 */
1163 if( !g.markPrivate ){
1164 if( autosync(SYNC_PULL) ){
1165 blob_zero(&ans);
1166 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1167 cReply = blob_str(&ans)[0];
1168 if( cReply!='y' && cReply!='Y' ){
1169 fossil_exit(1);
@@ -1205,34 +1231,38 @@
1231 }
1232
1233 hasChanges = unsaved_changes();
1234 db_begin_transaction();
1235 db_record_repository_filename(0);
1236 if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1237 fossil_fatal("nothing has changed; use --allow-empty to override");
1238 }
1239
1240 /* If none of the files that were named on the command line have
1241 ** been modified, bail out now unless the --allow-empty or --force
1242 ** flags is used.
1243 */
1244 if( g.aCommitFile
1245 && !allowEmpty
1246 && !forceFlag
1247 && !db_exists(
1248 "SELECT 1 FROM vfile "
1249 " WHERE is_selected(id)"
1250 " AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
1251 ){
1252 fossil_fatal("none of the selected files have changed; use "
1253 "--allow-empty to override.");
1254 }
1255
1256 /*
1257 ** Do not allow a commit that will cause a fork unless the --allow-fork
1258 ** or --force flags is used, or unless this is a private check-in.
1259 */
1260 if( zBranch==0 && allowFork==0 && forceFlag==0
1261 && g.markPrivate==0 && !is_a_leaf(vid)
1262 ){
1263 fossil_fatal("would fork. \"update\" first or use --allow-fork.");
1264 }
1265
1266 /*
1267 ** Do not allow a commit against a closed leaf
1268 */
@@ -1247,11 +1277,11 @@
1277 blob_zero(&comment);
1278 blob_append(&comment, zComment, -1);
1279 }else if( zComFile ){
1280 blob_zero(&comment);
1281 blob_read_from_file(&comment, zComFile);
1282 blob_to_utf8_no_bom(&comment, 1);
1283 }else{
1284 char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
1285 prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
1286 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
1287 blob_zero(&ans);
@@ -1277,36 +1307,42 @@
1307 /* Step 1: Insert records for all modified files into the blob
1308 ** table. If there were arguments passed to this command, only
1309 ** the identified files are inserted (if they have been modified).
1310 */
1311 db_prepare(&q,
1312 "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile "
1313 "WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1314 g.zLocalRoot,
1315 glob_expr("pathname", db_get("crnl-glob","")),
1316 glob_expr("pathname", db_get("binary-glob","")),
1317 glob_expr("pathname", db_get("unicode-glob",""))
1318 );
1319 while( db_step(&q)==SQLITE_ROW ){
1320 int id, rid;
1321 const char *zFullname;
1322 Blob content;
1323 int crnlOk, binOk, unicodeOk, chnged;
1324
1325 id = db_column_int(&q, 0);
1326 zFullname = db_column_text(&q, 1);
1327 rid = db_column_int(&q, 2);
1328 crnlOk = db_column_int(&q, 3);
1329 chnged = db_column_int(&q, 4);
1330 binOk = db_column_int(&q, 5);
1331 unicodeOk = db_column_int(&q, 6);
1332
1333 blob_zero(&content);
1334 if( file_wd_islink(zFullname) ){
1335 /* Instead of file content, put link destination path */
1336 blob_read_link(&content, zFullname);
1337 }else{
1338 blob_read_from_file(&content, zFullname);
1339 }
1340 /* Do not emit any warnings when they are disabled. */
1341 if( !noWarningFlag ){
1342 commit_warning(&content, crnlOk, binOk, unicodeOk, zFullname);
1343 }
1344 if( chnged==1 && contains_merge_marker(&content) ){
1345 Blob fname; /* Relative pathname of the file */
1346
1347 nConflict++;
1348 file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
1358 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
1359 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
1360 }
1361 db_finalize(&q);
1362 if( nConflict && !allowConflict ){
1363 fossil_fatal("abort due to unresolved merge conflicts; "
1364 "use --allow-conflict to override");
1365 }
1366
1367 /* Create the new manifest */
1368 if( blob_size(&comment)==0 ){
1369 blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
1370 }
1371 if( forceDelta ){
1372 blob_zero(&manifest);
1373 }else{
1374 create_manifest(&manifest, 0, 0, &comment, vid,
1375 !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
1376 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1377 azTag, &szB);
1378 }
1379
1380 /* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
1391 pBaseline = pParent;
1392 }
1393 if( pBaseline ){
1394 Blob delta;
1395 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1396 !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
1397 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1398 azTag, &szD);
1399 /*
1400 ** At this point, two manifests have been constructed, either of
1401 ** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
1521 exit(1);
1522 }
1523 db_end_transaction(0);
1524
1525 if( !g.markPrivate ){
1526 autosync(SYNC_PUSH);
1527 }
1528 if( count_nonbranch_children(vid)>1 ){
1529 fossil_print("**** warning: a fork has occurred *****\n");
1530 }
1531 }
1532
+73 -36
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521521
break;
522522
}
523523
blob_append(&reply, zIn, -1);
524524
}
525525
}
526
- blob_strip_bom(&reply, 1);
526
+ blob_to_utf8_no_bom(&reply, 1);
527527
blob_remove_cr(&reply);
528528
file_delete(zFile);
529529
free(zFile);
530530
blob_zero(pComment);
531531
while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570570
int parent_rid,
571571
const char *zUserOvrd
572572
){
573573
Blob prompt;
574574
#ifdef _WIN32
575
- static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
576
- blob_init(&prompt, (const char *) bom, 3);
575
+ int bomSize;
576
+ const unsigned char *bom = get_utf8_bom(&bomSize);
577
+ blob_init(&prompt, (const char *) bom, bomSize);
577578
if( zInit && zInit[0]) {
578579
blob_append(&prompt, zInit, -1);
579580
}
580581
#else
581582
blob_init(&prompt, zInit, -1);
582583
#endif
583584
blob_append(&prompt,
584585
"\n"
585586
"# Enter comments on this check-in. Lines beginning with # are ignored.\n"
586
- "# The check-in comment follows wiki formatting rules.\n"
587587
"#\n", -1
588588
);
589589
blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590590
if( zBranch && zBranch[0] ){
591591
blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657657
" AND type='ci' AND objid=%d",
658658
zDate, rid
659659
);
660660
if( b ){
661661
fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662
- " Use -f to override.", zUuid, zDate);
662
+ " Use --allow-older to override.", zUuid, zDate);
663663
}
664664
#endif
665665
}
666666
667667
/*
@@ -891,20 +891,21 @@
891891
*/
892892
static void commit_warning(
893893
const Blob *p, /* The content of the file being committed. */
894894
int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895895
int binOk, /* Non-zero if binary warnings should be disabled. */
896
+ int unicodeOk, /* Non-zero if unicode warnings should be disabled. */
896897
const char *zFilename /* The full name of the file being committed. */
897898
){
898899
int eType; /* return value of looks_like_utf8/utf16() */
899900
int fUnicode; /* return value of starts_with_utf16_bom() */
900901
char *zMsg; /* Warning message */
901902
Blob fname; /* Relative pathname of the file */
902903
static int allOk = 0; /* Set to true to disable this routine */
903904
904905
if( allOk ) return;
905
- fUnicode = starts_with_utf16_bom(p);
906
+ fUnicode = starts_with_utf16_bom(p, 0);
906907
eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
907908
if( eType<-2){
908909
const char *zWarning;
909910
Blob ans;
910911
char cReply;
@@ -933,10 +934,13 @@
933934
const char *zWarning;
934935
Blob ans;
935936
char cReply;
936937
937938
if( eType==-1 && fUnicode ){
939
+ if ( crnlOk && unicodeOk ){
940
+ return; /* We don't want Unicode/CR/NL warnings for this file. */
941
+ }
938942
zWarning = "Unicode and CR/NL line endings";
939943
}else if( eType==-1 ){
940944
if( crnlOk ){
941945
return; /* We don't want CR/NL warnings for this file. */
942946
}
@@ -945,10 +949,13 @@
945949
if( binOk ){
946950
return; /* We don't want binary warnings for this file. */
947951
}
948952
zWarning = "binary data";
949953
}else{
954
+ if ( unicodeOk ){
955
+ return; /* We don't want unicode warnings for this file. */
956
+ }
950957
zWarning = "Unicode";
951958
}
952959
file_relative_name(zFilename, &fname, 0);
953960
blob_zero(&ans);
954961
zMsg = mprintf(
@@ -1006,32 +1013,45 @@
10061013
**
10071014
** The --bgcolor option works like --branchcolor but only sets the
10081015
** background color for a single check-in. Subsequent check-ins revert
10091016
** to the default color.
10101017
**
1011
-** A check-in is not permitted to fork unless the --force or -f
1012
-** option appears. A check-in is not allowed against a closed leaf.
1018
+** A check-in is not permitted to fork unless the --allow-fork option
1019
+** appears. An empty check-in (i.e. with nothing changed) is not
1020
+** allowed unless the --allow-empty option appears. A check-in may not
1021
+** be older than its ancestor unless the --allow-older option appears.
1022
+** If any of files in the check-in appear to contain unresolved merge
1023
+** conflicts, the check-in will not be allowed unless the
1024
+** --allow-conflict option is present. In addition, the entire
1025
+** check-in process may be aborted if a file contains content that
1026
+** appears to be binary, Unicode text, or text with CR/NL line endings
1027
+** unless the interactive user chooses to proceed. If there is no
1028
+** interactive user or these warnings should be skipped for some other
1029
+** reason, the --no-warnings option may be used. A check-in is not
1030
+** allowed against a closed leaf.
10131031
**
10141032
** The --private option creates a private check-in that is never synced.
10151033
** Children of private check-ins are automatically private.
10161034
**
10171035
** the --tag option applies the symbolic tag name to the check-in.
10181036
**
10191037
** Options:
1038
+** --allow-conflict allow unresolved merge conflicts
1039
+** --allow-empty allow a commit with no changes
1040
+** --allow-fork allow the commit to fork
1041
+** --allow-older allow a commit older than its ancestor
10201042
** --baseline use a baseline manifest in the commit process
10211043
** --bgcolor COLOR apply COLOR to this one check-in only
10221044
** --branch NEW-BRANCH-NAME check in to this new branch
10231045
** --branchcolor COLOR apply given COLOR to the branch
10241046
** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
10251047
** --delta use a delta manifest in the commit process
1026
-** --force|-f allow forking with this commit
10271048
** --message-file|-M FILE read the commit comment from given file
1049
+** --no-warnings omit all warnings about file contents
10281050
** --nosign do not attempt to sign this commit with gpg
10291051
** --private do not sync changes and their descendants
10301052
** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1031
-** --conflict allow unresolved merge conflicts
1032
-** --binary-ok do not warn about committing binary files
10331053
**
10341054
** See also: branch, changes, checkout, extra, sync
10351055
*/
10361056
void commit_cmd(void){
10371057
int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
10421062
const char *zComment; /* Check-in comment */
10431063
Stmt q; /* Query to find files that have been modified */
10441064
char *zUuid; /* UUID of the new check-in */
10451065
int noSign = 0; /* True to omit signing the manifest using GPG */
10461066
int isAMerge = 0; /* True if checking in a merge */
1047
- int forceFlag = 0; /* Force a fork */
1067
+ int noWarningFlag = 0; /* True if skipping all warnings */
1068
+ int forceFlag = 0; /* Undocumented: Disables all checks */
10481069
int forceDelta = 0; /* Force a delta-manifest */
10491070
int forceBaseline = 0; /* Force a baseline-manifest */
10501071
int allowConflict = 0; /* Allow unresolve merge conflicts */
1051
- int binaryOk = 0; /* The --binary-ok flag */
1072
+ int allowEmpty = 0; /* Allow a commit with no changes */
1073
+ int allowFork = 0; /* Allow the commit to fork */
1074
+ int allowOlder = 0; /* Allow a commit older than its ancestor */
10521075
char *zManifestFile; /* Name of the manifest file */
10531076
int useCksum; /* True if checksums should be computed and verified */
10541077
int outputManifest; /* True to output "manifest" and "manifest.uuid" */
10551078
int testRun; /* True for a test run. Debugging only */
10561079
const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
10801103
fossil_fatal("cannot use --delta and --baseline together");
10811104
}
10821105
testRun = find_option("test",0,0)!=0;
10831106
zComment = find_option("comment","m",1);
10841107
forceFlag = find_option("force", "f", 0)!=0;
1108
+ allowConflict = find_option("allow-conflict",0,0)!=0;
1109
+ allowEmpty = find_option("allow-empty",0,0)!=0;
1110
+ allowFork = find_option("allow-fork",0,0)!=0;
1111
+ allowOlder = find_option("allow-older",0,0)!=0;
1112
+ noWarningFlag = find_option("no-warnings", 0, 0)!=0;
10851113
zBranch = find_option("branch","b",1);
10861114
zColor = find_option("bgcolor",0,1);
10871115
zBrClr = find_option("branchcolor",0,1);
1088
- binaryOk = find_option("binary-ok",0,0)!=0;
10891116
while( (zTag = find_option("tag",0,1))!=0 ){
10901117
if( zTag[0]==0 ) continue;
10911118
azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
10921119
azTag[nTag++] = zTag;
10931120
azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
10981125
if( zBranch==0 ) zBranch = "private";
10991126
if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
11001127
}
11011128
zDateOvrd = find_option("date-override",0,1);
11021129
zUserOvrd = find_option("user-override",0,1);
1103
- allowConflict = find_option("conflict",0,0)!=0;
11041130
db_must_be_within_tree();
11051131
noSign = db_get_boolean("omitsign", 0)|noSign;
11061132
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
11071133
useCksum = db_get_boolean("repo-cksum", 1);
11081134
outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
11331159
11341160
/*
11351161
** Autosync if autosync is enabled and this is not a private check-in.
11361162
*/
11371163
if( !g.markPrivate ){
1138
- if( autosync(AUTOSYNC_PULL) ){
1164
+ if( autosync(SYNC_PULL) ){
11391165
blob_zero(&ans);
11401166
prompt_user("continue in spite of sync failure (y/N)? ", &ans);
11411167
cReply = blob_str(&ans)[0];
11421168
if( cReply!='y' && cReply!='Y' ){
11431169
fossil_exit(1);
@@ -1205,34 +1231,38 @@
12051231
}
12061232
12071233
hasChanges = unsaved_changes();
12081234
db_begin_transaction();
12091235
db_record_repository_filename(0);
1210
- if( hasChanges==0 && !isAMerge && !forceFlag ){
1211
- fossil_fatal("nothing has changed");
1236
+ if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1237
+ fossil_fatal("nothing has changed; use --allow-empty to override");
12121238
}
12131239
12141240
/* If none of the files that were named on the command line have
1215
- ** been modified, bail out now unless the --force flag is used.
1241
+ ** been modified, bail out now unless the --allow-empty or --force
1242
+ ** flags is used.
12161243
*/
12171244
if( g.aCommitFile
1245
+ && !allowEmpty
12181246
&& !forceFlag
12191247
&& !db_exists(
12201248
"SELECT 1 FROM vfile "
12211249
" WHERE is_selected(id)"
12221250
" AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
12231251
){
1224
- fossil_fatal("none of the selected files have changed; use -f"
1225
- " or --force.");
1252
+ fossil_fatal("none of the selected files have changed; use "
1253
+ "--allow-empty to override.");
12261254
}
12271255
12281256
/*
1229
- ** Do not allow a commit that will cause a fork unless the --force flag
1230
- ** is used or unless this is a private check-in.
1257
+ ** Do not allow a commit that will cause a fork unless the --allow-fork
1258
+ ** or --force flags is used, or unless this is a private check-in.
12311259
*/
1232
- if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){
1233
- fossil_fatal("would fork. \"update\" first or use -f or --force.");
1260
+ if( zBranch==0 && allowFork==0 && forceFlag==0
1261
+ && g.markPrivate==0 && !is_a_leaf(vid)
1262
+ ){
1263
+ fossil_fatal("would fork. \"update\" first or use --allow-fork.");
12341264
}
12351265
12361266
/*
12371267
** Do not allow a commit against a closed leaf
12381268
*/
@@ -1247,11 +1277,11 @@
12471277
blob_zero(&comment);
12481278
blob_append(&comment, zComment, -1);
12491279
}else if( zComFile ){
12501280
blob_zero(&comment);
12511281
blob_read_from_file(&comment, zComFile);
1252
- blob_strip_bom(&comment, 1);
1282
+ blob_to_utf8_no_bom(&comment, 1);
12531283
}else{
12541284
char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
12551285
prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
12561286
if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
12571287
blob_zero(&ans);
@@ -1277,36 +1307,42 @@
12771307
/* Step 1: Insert records for all modified files into the blob
12781308
** table. If there were arguments passed to this command, only
12791309
** the identified files are inserted (if they have been modified).
12801310
*/
12811311
db_prepare(&q,
1282
- "SELECT id, %Q || pathname, mrid, %s, chnged, %s FROM vfile "
1312
+ "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile "
12831313
"WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1284
- g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")),
1285
- glob_expr("pathname", db_get("binary-glob",""))
1314
+ g.zLocalRoot,
1315
+ glob_expr("pathname", db_get("crnl-glob","")),
1316
+ glob_expr("pathname", db_get("binary-glob","")),
1317
+ glob_expr("pathname", db_get("unicode-glob",""))
12861318
);
12871319
while( db_step(&q)==SQLITE_ROW ){
12881320
int id, rid;
12891321
const char *zFullname;
12901322
Blob content;
1291
- int crnlOk, binOk, chnged;
1323
+ int crnlOk, binOk, unicodeOk, chnged;
12921324
12931325
id = db_column_int(&q, 0);
12941326
zFullname = db_column_text(&q, 1);
12951327
rid = db_column_int(&q, 2);
12961328
crnlOk = db_column_int(&q, 3);
12971329
chnged = db_column_int(&q, 4);
1298
- binOk = binaryOk || db_column_int(&q, 5);
1330
+ binOk = db_column_int(&q, 5);
1331
+ unicodeOk = db_column_int(&q, 6);
12991332
13001333
blob_zero(&content);
13011334
if( file_wd_islink(zFullname) ){
13021335
/* Instead of file content, put link destination path */
13031336
blob_read_link(&content, zFullname);
13041337
}else{
13051338
blob_read_from_file(&content, zFullname);
13061339
}
1307
- commit_warning(&content, crnlOk, binOk, zFullname);
1340
+ /* Do not emit any warnings when they are disabled. */
1341
+ if( !noWarningFlag ){
1342
+ commit_warning(&content, crnlOk, binOk, unicodeOk, zFullname);
1343
+ }
13081344
if( chnged==1 && contains_merge_marker(&content) ){
13091345
Blob fname; /* Relative pathname of the file */
13101346
13111347
nConflict++;
13121348
file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
13221358
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
13231359
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
13241360
}
13251361
db_finalize(&q);
13261362
if( nConflict && !allowConflict ){
1327
- fossil_fatal("abort due to unresolve merge conflicts");
1363
+ fossil_fatal("abort due to unresolved merge conflicts; "
1364
+ "use --allow-conflict to override");
13281365
}
13291366
13301367
/* Create the new manifest */
13311368
if( blob_size(&comment)==0 ){
13321369
blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
13331370
}
13341371
if( forceDelta ){
13351372
blob_zero(&manifest);
13361373
}else{
13371374
create_manifest(&manifest, 0, 0, &comment, vid,
1338
- !forceFlag, useCksum ? &cksum1 : 0,
1375
+ !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
13391376
zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
13401377
azTag, &szB);
13411378
}
13421379
13431380
/* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
13541391
pBaseline = pParent;
13551392
}
13561393
if( pBaseline ){
13571394
Blob delta;
13581395
create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1359
- !forceFlag, useCksum ? &cksum1 : 0,
1396
+ !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
13601397
zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
13611398
azTag, &szD);
13621399
/*
13631400
** At this point, two manifests have been constructed, either of
13641401
** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
14841521
exit(1);
14851522
}
14861523
db_end_transaction(0);
14871524
14881525
if( !g.markPrivate ){
1489
- autosync(AUTOSYNC_PUSH);
1526
+ autosync(SYNC_PUSH);
14901527
}
14911528
if( count_nonbranch_children(vid)>1 ){
14921529
fossil_print("**** warning: a fork has occurred *****\n");
14931530
}
14941531
}
14951532
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521 break;
522 }
523 blob_append(&reply, zIn, -1);
524 }
525 }
526 blob_strip_bom(&reply, 1);
527 blob_remove_cr(&reply);
528 file_delete(zFile);
529 free(zFile);
530 blob_zero(pComment);
531 while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570 int parent_rid,
571 const char *zUserOvrd
572 ){
573 Blob prompt;
574 #ifdef _WIN32
575 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
576 blob_init(&prompt, (const char *) bom, 3);
 
577 if( zInit && zInit[0]) {
578 blob_append(&prompt, zInit, -1);
579 }
580 #else
581 blob_init(&prompt, zInit, -1);
582 #endif
583 blob_append(&prompt,
584 "\n"
585 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
586 "# The check-in comment follows wiki formatting rules.\n"
587 "#\n", -1
588 );
589 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590 if( zBranch && zBranch[0] ){
591 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657 " AND type='ci' AND objid=%d",
658 zDate, rid
659 );
660 if( b ){
661 fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662 " Use -f to override.", zUuid, zDate);
663 }
664 #endif
665 }
666
667 /*
@@ -891,20 +891,21 @@
891 */
892 static void commit_warning(
893 const Blob *p, /* The content of the file being committed. */
894 int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895 int binOk, /* Non-zero if binary warnings should be disabled. */
 
896 const char *zFilename /* The full name of the file being committed. */
897 ){
898 int eType; /* return value of looks_like_utf8/utf16() */
899 int fUnicode; /* return value of starts_with_utf16_bom() */
900 char *zMsg; /* Warning message */
901 Blob fname; /* Relative pathname of the file */
902 static int allOk = 0; /* Set to true to disable this routine */
903
904 if( allOk ) return;
905 fUnicode = starts_with_utf16_bom(p);
906 eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
907 if( eType<-2){
908 const char *zWarning;
909 Blob ans;
910 char cReply;
@@ -933,10 +934,13 @@
933 const char *zWarning;
934 Blob ans;
935 char cReply;
936
937 if( eType==-1 && fUnicode ){
 
 
 
938 zWarning = "Unicode and CR/NL line endings";
939 }else if( eType==-1 ){
940 if( crnlOk ){
941 return; /* We don't want CR/NL warnings for this file. */
942 }
@@ -945,10 +949,13 @@
945 if( binOk ){
946 return; /* We don't want binary warnings for this file. */
947 }
948 zWarning = "binary data";
949 }else{
 
 
 
950 zWarning = "Unicode";
951 }
952 file_relative_name(zFilename, &fname, 0);
953 blob_zero(&ans);
954 zMsg = mprintf(
@@ -1006,32 +1013,45 @@
1006 **
1007 ** The --bgcolor option works like --branchcolor but only sets the
1008 ** background color for a single check-in. Subsequent check-ins revert
1009 ** to the default color.
1010 **
1011 ** A check-in is not permitted to fork unless the --force or -f
1012 ** option appears. A check-in is not allowed against a closed leaf.
 
 
 
 
 
 
 
 
 
 
 
1013 **
1014 ** The --private option creates a private check-in that is never synced.
1015 ** Children of private check-ins are automatically private.
1016 **
1017 ** the --tag option applies the symbolic tag name to the check-in.
1018 **
1019 ** Options:
 
 
 
 
1020 ** --baseline use a baseline manifest in the commit process
1021 ** --bgcolor COLOR apply COLOR to this one check-in only
1022 ** --branch NEW-BRANCH-NAME check in to this new branch
1023 ** --branchcolor COLOR apply given COLOR to the branch
1024 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
1025 ** --delta use a delta manifest in the commit process
1026 ** --force|-f allow forking with this commit
1027 ** --message-file|-M FILE read the commit comment from given file
 
1028 ** --nosign do not attempt to sign this commit with gpg
1029 ** --private do not sync changes and their descendants
1030 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
1031 ** --conflict allow unresolved merge conflicts
1032 ** --binary-ok do not warn about committing binary files
1033 **
1034 ** See also: branch, changes, checkout, extra, sync
1035 */
1036 void commit_cmd(void){
1037 int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
1042 const char *zComment; /* Check-in comment */
1043 Stmt q; /* Query to find files that have been modified */
1044 char *zUuid; /* UUID of the new check-in */
1045 int noSign = 0; /* True to omit signing the manifest using GPG */
1046 int isAMerge = 0; /* True if checking in a merge */
1047 int forceFlag = 0; /* Force a fork */
 
1048 int forceDelta = 0; /* Force a delta-manifest */
1049 int forceBaseline = 0; /* Force a baseline-manifest */
1050 int allowConflict = 0; /* Allow unresolve merge conflicts */
1051 int binaryOk = 0; /* The --binary-ok flag */
 
 
1052 char *zManifestFile; /* Name of the manifest file */
1053 int useCksum; /* True if checksums should be computed and verified */
1054 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
1055 int testRun; /* True for a test run. Debugging only */
1056 const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
1080 fossil_fatal("cannot use --delta and --baseline together");
1081 }
1082 testRun = find_option("test",0,0)!=0;
1083 zComment = find_option("comment","m",1);
1084 forceFlag = find_option("force", "f", 0)!=0;
 
 
 
 
 
1085 zBranch = find_option("branch","b",1);
1086 zColor = find_option("bgcolor",0,1);
1087 zBrClr = find_option("branchcolor",0,1);
1088 binaryOk = find_option("binary-ok",0,0)!=0;
1089 while( (zTag = find_option("tag",0,1))!=0 ){
1090 if( zTag[0]==0 ) continue;
1091 azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
1092 azTag[nTag++] = zTag;
1093 azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
1098 if( zBranch==0 ) zBranch = "private";
1099 if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
1100 }
1101 zDateOvrd = find_option("date-override",0,1);
1102 zUserOvrd = find_option("user-override",0,1);
1103 allowConflict = find_option("conflict",0,0)!=0;
1104 db_must_be_within_tree();
1105 noSign = db_get_boolean("omitsign", 0)|noSign;
1106 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
1107 useCksum = db_get_boolean("repo-cksum", 1);
1108 outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
1133
1134 /*
1135 ** Autosync if autosync is enabled and this is not a private check-in.
1136 */
1137 if( !g.markPrivate ){
1138 if( autosync(AUTOSYNC_PULL) ){
1139 blob_zero(&ans);
1140 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1141 cReply = blob_str(&ans)[0];
1142 if( cReply!='y' && cReply!='Y' ){
1143 fossil_exit(1);
@@ -1205,34 +1231,38 @@
1205 }
1206
1207 hasChanges = unsaved_changes();
1208 db_begin_transaction();
1209 db_record_repository_filename(0);
1210 if( hasChanges==0 && !isAMerge && !forceFlag ){
1211 fossil_fatal("nothing has changed");
1212 }
1213
1214 /* If none of the files that were named on the command line have
1215 ** been modified, bail out now unless the --force flag is used.
 
1216 */
1217 if( g.aCommitFile
 
1218 && !forceFlag
1219 && !db_exists(
1220 "SELECT 1 FROM vfile "
1221 " WHERE is_selected(id)"
1222 " AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
1223 ){
1224 fossil_fatal("none of the selected files have changed; use -f"
1225 " or --force.");
1226 }
1227
1228 /*
1229 ** Do not allow a commit that will cause a fork unless the --force flag
1230 ** is used or unless this is a private check-in.
1231 */
1232 if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){
1233 fossil_fatal("would fork. \"update\" first or use -f or --force.");
 
 
1234 }
1235
1236 /*
1237 ** Do not allow a commit against a closed leaf
1238 */
@@ -1247,11 +1277,11 @@
1247 blob_zero(&comment);
1248 blob_append(&comment, zComment, -1);
1249 }else if( zComFile ){
1250 blob_zero(&comment);
1251 blob_read_from_file(&comment, zComFile);
1252 blob_strip_bom(&comment, 1);
1253 }else{
1254 char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
1255 prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
1256 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
1257 blob_zero(&ans);
@@ -1277,36 +1307,42 @@
1277 /* Step 1: Insert records for all modified files into the blob
1278 ** table. If there were arguments passed to this command, only
1279 ** the identified files are inserted (if they have been modified).
1280 */
1281 db_prepare(&q,
1282 "SELECT id, %Q || pathname, mrid, %s, chnged, %s FROM vfile "
1283 "WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1284 g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")),
1285 glob_expr("pathname", db_get("binary-glob",""))
 
 
1286 );
1287 while( db_step(&q)==SQLITE_ROW ){
1288 int id, rid;
1289 const char *zFullname;
1290 Blob content;
1291 int crnlOk, binOk, chnged;
1292
1293 id = db_column_int(&q, 0);
1294 zFullname = db_column_text(&q, 1);
1295 rid = db_column_int(&q, 2);
1296 crnlOk = db_column_int(&q, 3);
1297 chnged = db_column_int(&q, 4);
1298 binOk = binaryOk || db_column_int(&q, 5);
 
1299
1300 blob_zero(&content);
1301 if( file_wd_islink(zFullname) ){
1302 /* Instead of file content, put link destination path */
1303 blob_read_link(&content, zFullname);
1304 }else{
1305 blob_read_from_file(&content, zFullname);
1306 }
1307 commit_warning(&content, crnlOk, binOk, zFullname);
 
 
 
1308 if( chnged==1 && contains_merge_marker(&content) ){
1309 Blob fname; /* Relative pathname of the file */
1310
1311 nConflict++;
1312 file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
1322 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
1323 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
1324 }
1325 db_finalize(&q);
1326 if( nConflict && !allowConflict ){
1327 fossil_fatal("abort due to unresolve merge conflicts");
 
1328 }
1329
1330 /* Create the new manifest */
1331 if( blob_size(&comment)==0 ){
1332 blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
1333 }
1334 if( forceDelta ){
1335 blob_zero(&manifest);
1336 }else{
1337 create_manifest(&manifest, 0, 0, &comment, vid,
1338 !forceFlag, useCksum ? &cksum1 : 0,
1339 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1340 azTag, &szB);
1341 }
1342
1343 /* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
1354 pBaseline = pParent;
1355 }
1356 if( pBaseline ){
1357 Blob delta;
1358 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1359 !forceFlag, useCksum ? &cksum1 : 0,
1360 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1361 azTag, &szD);
1362 /*
1363 ** At this point, two manifests have been constructed, either of
1364 ** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
1484 exit(1);
1485 }
1486 db_end_transaction(0);
1487
1488 if( !g.markPrivate ){
1489 autosync(AUTOSYNC_PUSH);
1490 }
1491 if( count_nonbranch_children(vid)>1 ){
1492 fossil_print("**** warning: a fork has occurred *****\n");
1493 }
1494 }
1495
--- src/checkin.c
+++ src/checkin.c
@@ -521,11 +521,11 @@
521 break;
522 }
523 blob_append(&reply, zIn, -1);
524 }
525 }
526 blob_to_utf8_no_bom(&reply, 1);
527 blob_remove_cr(&reply);
528 file_delete(zFile);
529 free(zFile);
530 blob_zero(pComment);
531 while( blob_line(&reply, &line) ){
@@ -570,22 +570,22 @@
570 int parent_rid,
571 const char *zUserOvrd
572 ){
573 Blob prompt;
574 #ifdef _WIN32
575 int bomSize;
576 const unsigned char *bom = get_utf8_bom(&bomSize);
577 blob_init(&prompt, (const char *) bom, bomSize);
578 if( zInit && zInit[0]) {
579 blob_append(&prompt, zInit, -1);
580 }
581 #else
582 blob_init(&prompt, zInit, -1);
583 #endif
584 blob_append(&prompt,
585 "\n"
586 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
 
587 "#\n", -1
588 );
589 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
590 if( zBranch && zBranch[0] ){
591 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
@@ -657,11 +657,11 @@
657 " AND type='ci' AND objid=%d",
658 zDate, rid
659 );
660 if( b ){
661 fossil_fatal("ancestor check-in [%.10s] (%s) is not older (clock skew?)"
662 " Use --allow-older to override.", zUuid, zDate);
663 }
664 #endif
665 }
666
667 /*
@@ -891,20 +891,21 @@
891 */
892 static void commit_warning(
893 const Blob *p, /* The content of the file being committed. */
894 int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */
895 int binOk, /* Non-zero if binary warnings should be disabled. */
896 int unicodeOk, /* Non-zero if unicode warnings should be disabled. */
897 const char *zFilename /* The full name of the file being committed. */
898 ){
899 int eType; /* return value of looks_like_utf8/utf16() */
900 int fUnicode; /* return value of starts_with_utf16_bom() */
901 char *zMsg; /* Warning message */
902 Blob fname; /* Relative pathname of the file */
903 static int allOk = 0; /* Set to true to disable this routine */
904
905 if( allOk ) return;
906 fUnicode = starts_with_utf16_bom(p, 0);
907 eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p);
908 if( eType<-2){
909 const char *zWarning;
910 Blob ans;
911 char cReply;
@@ -933,10 +934,13 @@
934 const char *zWarning;
935 Blob ans;
936 char cReply;
937
938 if( eType==-1 && fUnicode ){
939 if ( crnlOk && unicodeOk ){
940 return; /* We don't want Unicode/CR/NL warnings for this file. */
941 }
942 zWarning = "Unicode and CR/NL line endings";
943 }else if( eType==-1 ){
944 if( crnlOk ){
945 return; /* We don't want CR/NL warnings for this file. */
946 }
@@ -945,10 +949,13 @@
949 if( binOk ){
950 return; /* We don't want binary warnings for this file. */
951 }
952 zWarning = "binary data";
953 }else{
954 if ( unicodeOk ){
955 return; /* We don't want unicode warnings for this file. */
956 }
957 zWarning = "Unicode";
958 }
959 file_relative_name(zFilename, &fname, 0);
960 blob_zero(&ans);
961 zMsg = mprintf(
@@ -1006,32 +1013,45 @@
1013 **
1014 ** The --bgcolor option works like --branchcolor but only sets the
1015 ** background color for a single check-in. Subsequent check-ins revert
1016 ** to the default color.
1017 **
1018 ** A check-in is not permitted to fork unless the --allow-fork option
1019 ** appears. An empty check-in (i.e. with nothing changed) is not
1020 ** allowed unless the --allow-empty option appears. A check-in may not
1021 ** be older than its ancestor unless the --allow-older option appears.
1022 ** If any of files in the check-in appear to contain unresolved merge
1023 ** conflicts, the check-in will not be allowed unless the
1024 ** --allow-conflict option is present. In addition, the entire
1025 ** check-in process may be aborted if a file contains content that
1026 ** appears to be binary, Unicode text, or text with CR/NL line endings
1027 ** unless the interactive user chooses to proceed. If there is no
1028 ** interactive user or these warnings should be skipped for some other
1029 ** reason, the --no-warnings option may be used. A check-in is not
1030 ** allowed against a closed leaf.
1031 **
1032 ** The --private option creates a private check-in that is never synced.
1033 ** Children of private check-ins are automatically private.
1034 **
1035 ** the --tag option applies the symbolic tag name to the check-in.
1036 **
1037 ** Options:
1038 ** --allow-conflict allow unresolved merge conflicts
1039 ** --allow-empty allow a commit with no changes
1040 ** --allow-fork allow the commit to fork
1041 ** --allow-older allow a commit older than its ancestor
1042 ** --baseline use a baseline manifest in the commit process
1043 ** --bgcolor COLOR apply COLOR to this one check-in only
1044 ** --branch NEW-BRANCH-NAME check in to this new branch
1045 ** --branchcolor COLOR apply given COLOR to the branch
1046 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment
1047 ** --delta use a delta manifest in the commit process
 
1048 ** --message-file|-M FILE read the commit comment from given file
1049 ** --no-warnings omit all warnings about file contents
1050 ** --nosign do not attempt to sign this commit with gpg
1051 ** --private do not sync changes and their descendants
1052 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin
 
 
1053 **
1054 ** See also: branch, changes, checkout, extra, sync
1055 */
1056 void commit_cmd(void){
1057 int hasChanges; /* True if unsaved changes exist */
@@ -1042,15 +1062,18 @@
1062 const char *zComment; /* Check-in comment */
1063 Stmt q; /* Query to find files that have been modified */
1064 char *zUuid; /* UUID of the new check-in */
1065 int noSign = 0; /* True to omit signing the manifest using GPG */
1066 int isAMerge = 0; /* True if checking in a merge */
1067 int noWarningFlag = 0; /* True if skipping all warnings */
1068 int forceFlag = 0; /* Undocumented: Disables all checks */
1069 int forceDelta = 0; /* Force a delta-manifest */
1070 int forceBaseline = 0; /* Force a baseline-manifest */
1071 int allowConflict = 0; /* Allow unresolve merge conflicts */
1072 int allowEmpty = 0; /* Allow a commit with no changes */
1073 int allowFork = 0; /* Allow the commit to fork */
1074 int allowOlder = 0; /* Allow a commit older than its ancestor */
1075 char *zManifestFile; /* Name of the manifest file */
1076 int useCksum; /* True if checksums should be computed and verified */
1077 int outputManifest; /* True to output "manifest" and "manifest.uuid" */
1078 int testRun; /* True for a test run. Debugging only */
1079 const char *zBranch; /* Create a new branch with this name */
@@ -1080,14 +1103,18 @@
1103 fossil_fatal("cannot use --delta and --baseline together");
1104 }
1105 testRun = find_option("test",0,0)!=0;
1106 zComment = find_option("comment","m",1);
1107 forceFlag = find_option("force", "f", 0)!=0;
1108 allowConflict = find_option("allow-conflict",0,0)!=0;
1109 allowEmpty = find_option("allow-empty",0,0)!=0;
1110 allowFork = find_option("allow-fork",0,0)!=0;
1111 allowOlder = find_option("allow-older",0,0)!=0;
1112 noWarningFlag = find_option("no-warnings", 0, 0)!=0;
1113 zBranch = find_option("branch","b",1);
1114 zColor = find_option("bgcolor",0,1);
1115 zBrClr = find_option("branchcolor",0,1);
 
1116 while( (zTag = find_option("tag",0,1))!=0 ){
1117 if( zTag[0]==0 ) continue;
1118 azTag = fossil_realloc((void *)azTag, sizeof(char*)*(nTag+2));
1119 azTag[nTag++] = zTag;
1120 azTag[nTag] = 0;
@@ -1098,11 +1125,10 @@
1125 if( zBranch==0 ) zBranch = "private";
1126 if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */
1127 }
1128 zDateOvrd = find_option("date-override",0,1);
1129 zUserOvrd = find_option("user-override",0,1);
 
1130 db_must_be_within_tree();
1131 noSign = db_get_boolean("omitsign", 0)|noSign;
1132 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
1133 useCksum = db_get_boolean("repo-cksum", 1);
1134 outputManifest = db_get_boolean("manifest", 0);
@@ -1133,11 +1159,11 @@
1159
1160 /*
1161 ** Autosync if autosync is enabled and this is not a private check-in.
1162 */
1163 if( !g.markPrivate ){
1164 if( autosync(SYNC_PULL) ){
1165 blob_zero(&ans);
1166 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1167 cReply = blob_str(&ans)[0];
1168 if( cReply!='y' && cReply!='Y' ){
1169 fossil_exit(1);
@@ -1205,34 +1231,38 @@
1231 }
1232
1233 hasChanges = unsaved_changes();
1234 db_begin_transaction();
1235 db_record_repository_filename(0);
1236 if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
1237 fossil_fatal("nothing has changed; use --allow-empty to override");
1238 }
1239
1240 /* If none of the files that were named on the command line have
1241 ** been modified, bail out now unless the --allow-empty or --force
1242 ** flags is used.
1243 */
1244 if( g.aCommitFile
1245 && !allowEmpty
1246 && !forceFlag
1247 && !db_exists(
1248 "SELECT 1 FROM vfile "
1249 " WHERE is_selected(id)"
1250 " AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
1251 ){
1252 fossil_fatal("none of the selected files have changed; use "
1253 "--allow-empty to override.");
1254 }
1255
1256 /*
1257 ** Do not allow a commit that will cause a fork unless the --allow-fork
1258 ** or --force flags is used, or unless this is a private check-in.
1259 */
1260 if( zBranch==0 && allowFork==0 && forceFlag==0
1261 && g.markPrivate==0 && !is_a_leaf(vid)
1262 ){
1263 fossil_fatal("would fork. \"update\" first or use --allow-fork.");
1264 }
1265
1266 /*
1267 ** Do not allow a commit against a closed leaf
1268 */
@@ -1247,11 +1277,11 @@
1277 blob_zero(&comment);
1278 blob_append(&comment, zComment, -1);
1279 }else if( zComFile ){
1280 blob_zero(&comment);
1281 blob_read_from_file(&comment, zComFile);
1282 blob_to_utf8_no_bom(&comment, 1);
1283 }else{
1284 char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
1285 prepare_commit_comment(&comment, zInit, zBranch, vid, zUserOvrd);
1286 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
1287 blob_zero(&ans);
@@ -1277,36 +1307,42 @@
1307 /* Step 1: Insert records for all modified files into the blob
1308 ** table. If there were arguments passed to this command, only
1309 ** the identified files are inserted (if they have been modified).
1310 */
1311 db_prepare(&q,
1312 "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile "
1313 "WHERE chnged==1 AND NOT deleted AND is_selected(id)",
1314 g.zLocalRoot,
1315 glob_expr("pathname", db_get("crnl-glob","")),
1316 glob_expr("pathname", db_get("binary-glob","")),
1317 glob_expr("pathname", db_get("unicode-glob",""))
1318 );
1319 while( db_step(&q)==SQLITE_ROW ){
1320 int id, rid;
1321 const char *zFullname;
1322 Blob content;
1323 int crnlOk, binOk, unicodeOk, chnged;
1324
1325 id = db_column_int(&q, 0);
1326 zFullname = db_column_text(&q, 1);
1327 rid = db_column_int(&q, 2);
1328 crnlOk = db_column_int(&q, 3);
1329 chnged = db_column_int(&q, 4);
1330 binOk = db_column_int(&q, 5);
1331 unicodeOk = db_column_int(&q, 6);
1332
1333 blob_zero(&content);
1334 if( file_wd_islink(zFullname) ){
1335 /* Instead of file content, put link destination path */
1336 blob_read_link(&content, zFullname);
1337 }else{
1338 blob_read_from_file(&content, zFullname);
1339 }
1340 /* Do not emit any warnings when they are disabled. */
1341 if( !noWarningFlag ){
1342 commit_warning(&content, crnlOk, binOk, unicodeOk, zFullname);
1343 }
1344 if( chnged==1 && contains_merge_marker(&content) ){
1345 Blob fname; /* Relative pathname of the file */
1346
1347 nConflict++;
1348 file_relative_name(zFullname, &fname, 0);
@@ -1322,11 +1358,12 @@
1358 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
1359 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
1360 }
1361 db_finalize(&q);
1362 if( nConflict && !allowConflict ){
1363 fossil_fatal("abort due to unresolved merge conflicts; "
1364 "use --allow-conflict to override");
1365 }
1366
1367 /* Create the new manifest */
1368 if( blob_size(&comment)==0 ){
1369 blob_append(&comment, "(no comment)", -1);
@@ -1333,11 +1370,11 @@
1370 }
1371 if( forceDelta ){
1372 blob_zero(&manifest);
1373 }else{
1374 create_manifest(&manifest, 0, 0, &comment, vid,
1375 !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
1376 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1377 azTag, &szB);
1378 }
1379
1380 /* See if a delta-manifest would be more appropriate */
@@ -1354,11 +1391,11 @@
1391 pBaseline = pParent;
1392 }
1393 if( pBaseline ){
1394 Blob delta;
1395 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid,
1396 !allowOlder && !forceFlag, useCksum ? &cksum1 : 0,
1397 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr,
1398 azTag, &szD);
1399 /*
1400 ** At this point, two manifests have been constructed, either of
1401 ** which would work for this checkin. The first manifest (held
@@ -1484,11 +1521,11 @@
1521 exit(1);
1522 }
1523 db_end_transaction(0);
1524
1525 if( !g.markPrivate ){
1526 autosync(SYNC_PUSH);
1527 }
1528 if( count_nonbranch_children(vid)>1 ){
1529 fossil_print("**** warning: a fork has occurred *****\n");
1530 }
1531 }
1532
+1 -1
--- src/checkout.c
+++ src/checkout.c
@@ -106,11 +106,11 @@
106106
/* Check the EXE permission status of all files
107107
*/
108108
pManifest = manifest_get(vid, CFTYPE_MANIFEST);
109109
if( pManifest==0 ) return;
110110
blob_zero(&filename);
111
- blob_appendf(&filename, "%s/", g.zLocalRoot);
111
+ blob_appendf(&filename, "%s", g.zLocalRoot);
112112
baseLen = blob_size(&filename);
113113
manifest_file_rewind(pManifest);
114114
while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
115115
int isExe;
116116
blob_append(&filename, pFile->zName, -1);
117117
--- src/checkout.c
+++ src/checkout.c
@@ -106,11 +106,11 @@
106 /* Check the EXE permission status of all files
107 */
108 pManifest = manifest_get(vid, CFTYPE_MANIFEST);
109 if( pManifest==0 ) return;
110 blob_zero(&filename);
111 blob_appendf(&filename, "%s/", g.zLocalRoot);
112 baseLen = blob_size(&filename);
113 manifest_file_rewind(pManifest);
114 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
115 int isExe;
116 blob_append(&filename, pFile->zName, -1);
117
--- src/checkout.c
+++ src/checkout.c
@@ -106,11 +106,11 @@
106 /* Check the EXE permission status of all files
107 */
108 pManifest = manifest_get(vid, CFTYPE_MANIFEST);
109 if( pManifest==0 ) return;
110 blob_zero(&filename);
111 blob_appendf(&filename, "%s", g.zLocalRoot);
112 baseLen = blob_size(&filename);
113 manifest_file_rewind(pManifest);
114 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
115 int isExe;
116 blob_append(&filename, pFile->zName, -1);
117
+4 -4
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
9999
** See also: init
100100
*/
101101
void clone_cmd(void){
102102
char *zPassword;
103103
const char *zDefaultUser; /* Optional name of the default user */
104
- const char *zPw; /* The user clone password */
104
+ const char *zPw; /* The user clone password */
105105
int nErr = 0;
106
- int bPrivate; /* Also clone private branches */
106
+ int bPrivate = 0; /* Also clone private branches */
107107
108
- bPrivate = find_option("private",0,0)!=0;
108
+ if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
109109
url_proxy_options();
110110
if( g.argc < 4 ){
111111
usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112112
}
113113
db_open_config(0);
@@ -162,11 +162,11 @@
162162
" VALUES('server-code', lower(hex(randomblob(20))), now());"
163163
);
164164
url_enable_proxy(0);
165165
url_get_password_if_needed();
166166
g.xlinkClusterOnly = 1;
167
- nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
167
+ nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
168168
g.xlinkClusterOnly = 0;
169169
verify_cancel();
170170
db_end_transaction(0);
171171
db_close(1);
172172
if( nErr ){
173173
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
99 ** See also: init
100 */
101 void clone_cmd(void){
102 char *zPassword;
103 const char *zDefaultUser; /* Optional name of the default user */
104 const char *zPw; /* The user clone password */
105 int nErr = 0;
106 int bPrivate; /* Also clone private branches */
107
108 bPrivate = find_option("private",0,0)!=0;
109 url_proxy_options();
110 if( g.argc < 4 ){
111 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112 }
113 db_open_config(0);
@@ -162,11 +162,11 @@
162 " VALUES('server-code', lower(hex(randomblob(20))), now());"
163 );
164 url_enable_proxy(0);
165 url_get_password_if_needed();
166 g.xlinkClusterOnly = 1;
167 nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
168 g.xlinkClusterOnly = 0;
169 verify_cancel();
170 db_end_transaction(0);
171 db_close(1);
172 if( nErr ){
173
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
99 ** See also: init
100 */
101 void clone_cmd(void){
102 char *zPassword;
103 const char *zDefaultUser; /* Optional name of the default user */
104 const char *zPw; /* The user clone password */
105 int nErr = 0;
106 int bPrivate = 0; /* Also clone private branches */
107
108 if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
109 url_proxy_options();
110 if( g.argc < 4 ){
111 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112 }
113 db_open_config(0);
@@ -162,11 +162,11 @@
162 " VALUES('server-code', lower(hex(randomblob(20))), now());"
163 );
164 url_enable_proxy(0);
165 url_get_password_if_needed();
166 g.xlinkClusterOnly = 1;
167 nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
168 g.xlinkClusterOnly = 0;
169 verify_cancel();
170 db_end_transaction(0);
171 db_close(1);
172 if( nErr ){
173
+6 -3
--- src/configure.c
+++ src/configure.c
@@ -86,13 +86,15 @@
8686
{ "background-mimetype", CONFIGSET_SKIN },
8787
{ "background-image", CONFIGSET_SKIN },
8888
{ "index-page", CONFIGSET_SKIN },
8989
{ "timeline-block-markup", CONFIGSET_SKIN },
9090
{ "timeline-max-comment", CONFIGSET_SKIN },
91
+ { "timeline-plaintext", CONFIGSET_SKIN },
9192
{ "adunit", CONFIGSET_SKIN },
9293
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
9394
{ "adunit-omit-if-user", CONFIGSET_SKIN },
95
+ { "th1-setup", CONFIGSET_ALL },
9496
9597
#ifdef FOSSIL_ENABLE_TCL
9698
{ "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
9799
{ "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
98100
#endif
@@ -101,10 +103,11 @@
101103
{ "project-description", CONFIGSET_PROJ },
102104
{ "manifest", CONFIGSET_PROJ },
103105
{ "binary-glob", CONFIGSET_PROJ },
104106
{ "ignore-glob", CONFIGSET_PROJ },
105107
{ "crnl-glob", CONFIGSET_PROJ },
108
+ { "unicode-glob", CONFIGSET_PROJ },
106109
{ "empty-dirs", CONFIGSET_PROJ },
107110
{ "allow-symlinks", CONFIGSET_PROJ },
108111
109112
{ "ticket-table", CONFIGSET_TKT },
110113
{ "ticket-common", CONFIGSET_TKT },
@@ -906,15 +909,15 @@
906909
user_select();
907910
url_enable_proxy("via proxy: ");
908911
if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
909912
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
910913
if( strncmp(zMethod, "push", n)==0 ){
911
- client_sync(0,0,0,0,0,mask);
914
+ client_sync(0,0,(unsigned)mask);
912915
}else if( strncmp(zMethod, "pull", n)==0 ){
913
- client_sync(0,0,0,0,mask,0);
916
+ client_sync(0,(unsigned)mask,0);
914917
}else{
915
- client_sync(0,0,0,0,mask,mask);
918
+ client_sync(0,(unsigned)mask,(unsigned)mask);
916919
}
917920
}else
918921
if( strncmp(zMethod, "reset", n)==0 ){
919922
int mask, i;
920923
char *zBackup;
921924
--- src/configure.c
+++ src/configure.c
@@ -86,13 +86,15 @@
86 { "background-mimetype", CONFIGSET_SKIN },
87 { "background-image", CONFIGSET_SKIN },
88 { "index-page", CONFIGSET_SKIN },
89 { "timeline-block-markup", CONFIGSET_SKIN },
90 { "timeline-max-comment", CONFIGSET_SKIN },
 
91 { "adunit", CONFIGSET_SKIN },
92 { "adunit-omit-if-admin", CONFIGSET_SKIN },
93 { "adunit-omit-if-user", CONFIGSET_SKIN },
 
94
95 #ifdef FOSSIL_ENABLE_TCL
96 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
97 { "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
98 #endif
@@ -101,10 +103,11 @@
101 { "project-description", CONFIGSET_PROJ },
102 { "manifest", CONFIGSET_PROJ },
103 { "binary-glob", CONFIGSET_PROJ },
104 { "ignore-glob", CONFIGSET_PROJ },
105 { "crnl-glob", CONFIGSET_PROJ },
 
106 { "empty-dirs", CONFIGSET_PROJ },
107 { "allow-symlinks", CONFIGSET_PROJ },
108
109 { "ticket-table", CONFIGSET_TKT },
110 { "ticket-common", CONFIGSET_TKT },
@@ -906,15 +909,15 @@
906 user_select();
907 url_enable_proxy("via proxy: ");
908 if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
909 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
910 if( strncmp(zMethod, "push", n)==0 ){
911 client_sync(0,0,0,0,0,mask);
912 }else if( strncmp(zMethod, "pull", n)==0 ){
913 client_sync(0,0,0,0,mask,0);
914 }else{
915 client_sync(0,0,0,0,mask,mask);
916 }
917 }else
918 if( strncmp(zMethod, "reset", n)==0 ){
919 int mask, i;
920 char *zBackup;
921
--- src/configure.c
+++ src/configure.c
@@ -86,13 +86,15 @@
86 { "background-mimetype", CONFIGSET_SKIN },
87 { "background-image", CONFIGSET_SKIN },
88 { "index-page", CONFIGSET_SKIN },
89 { "timeline-block-markup", CONFIGSET_SKIN },
90 { "timeline-max-comment", CONFIGSET_SKIN },
91 { "timeline-plaintext", CONFIGSET_SKIN },
92 { "adunit", CONFIGSET_SKIN },
93 { "adunit-omit-if-admin", CONFIGSET_SKIN },
94 { "adunit-omit-if-user", CONFIGSET_SKIN },
95 { "th1-setup", CONFIGSET_ALL },
96
97 #ifdef FOSSIL_ENABLE_TCL
98 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
99 { "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
100 #endif
@@ -101,10 +103,11 @@
103 { "project-description", CONFIGSET_PROJ },
104 { "manifest", CONFIGSET_PROJ },
105 { "binary-glob", CONFIGSET_PROJ },
106 { "ignore-glob", CONFIGSET_PROJ },
107 { "crnl-glob", CONFIGSET_PROJ },
108 { "unicode-glob", CONFIGSET_PROJ },
109 { "empty-dirs", CONFIGSET_PROJ },
110 { "allow-symlinks", CONFIGSET_PROJ },
111
112 { "ticket-table", CONFIGSET_TKT },
113 { "ticket-common", CONFIGSET_TKT },
@@ -906,15 +909,15 @@
909 user_select();
910 url_enable_proxy("via proxy: ");
911 if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
912 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
913 if( strncmp(zMethod, "push", n)==0 ){
914 client_sync(0,0,(unsigned)mask);
915 }else if( strncmp(zMethod, "pull", n)==0 ){
916 client_sync(0,(unsigned)mask,0);
917 }else{
918 client_sync(0,(unsigned)mask,(unsigned)mask);
919 }
920 }else
921 if( strncmp(zMethod, "reset", n)==0 ){
922 int mask, i;
923 char *zBackup;
924
+4 -1
--- src/content.c
+++ src/content.c
@@ -667,11 +667,14 @@
667667
668668
669669
/*
670670
** COMMAND: test-content-put
671671
**
672
-** Extract a blob from a file and write it into the database
672
+** Usage: %fossil test-content-put FILE
673
+**
674
+** Read the content of FILE and add it to the Blob table as a new
675
+** artifact using a direct call to content_put().
673676
*/
674677
void test_content_put_cmd(void){
675678
int rid;
676679
Blob content;
677680
if( g.argc!=3 ) usage("FILENAME");
678681
--- src/content.c
+++ src/content.c
@@ -667,11 +667,14 @@
667
668
669 /*
670 ** COMMAND: test-content-put
671 **
672 ** Extract a blob from a file and write it into the database
 
 
 
673 */
674 void test_content_put_cmd(void){
675 int rid;
676 Blob content;
677 if( g.argc!=3 ) usage("FILENAME");
678
--- src/content.c
+++ src/content.c
@@ -667,11 +667,14 @@
667
668
669 /*
670 ** COMMAND: test-content-put
671 **
672 ** Usage: %fossil test-content-put FILE
673 **
674 ** Read the content of FILE and add it to the Blob table as a new
675 ** artifact using a direct call to content_put().
676 */
677 void test_content_put_cmd(void){
678 int rid;
679 Blob content;
680 if( g.argc!=3 ) usage("FILENAME");
681
+47 -11
--- src/db.c
+++ src/db.c
@@ -740,17 +740,25 @@
740740
/*
741741
** zDbName is the name of a database file. If no other database
742742
** file is open, then open this one. If another database file is
743743
** already open, then attach zDbName using the name zLabel.
744744
*/
745
-void db_open_or_attach(const char *zDbName, const char *zLabel){
745
+void db_open_or_attach(
746
+ const char *zDbName,
747
+ const char *zLabel,
748
+ int *pWasAttached
749
+){
746750
if( !g.db ){
751
+ assert( g.zMainDbType==0 );
747752
g.db = openDatabase(zDbName);
748753
g.zMainDbType = zLabel;
749754
db_connection_init();
755
+ if ( pWasAttached ) *pWasAttached = 0;
750756
}else{
757
+ assert( g.zMainDbType!=0 );
751758
db_attach(zDbName, zLabel);
759
+ if ( pWasAttached ) *pWasAttached = 1;
752760
}
753761
}
754762
755763
/*
756764
** Open the user database in "~/.fossil". Create the database anew if
@@ -806,16 +814,18 @@
806814
zDbName = mprintf("%s/.fossil", zHome);
807815
#endif
808816
if( file_size(zDbName)<1024*3 ){
809817
db_init_database(zDbName, zConfigSchema, (char*)0);
810818
}
811
- g.useAttach = useAttach;
812819
if( useAttach ){
813
- db_open_or_attach(zDbName, "configdb");
820
+ db_open_or_attach(zDbName, "configdb", &g.useAttach);
814821
g.dbConfig = 0;
822
+ g.zConfigDbType = 0;
815823
}else{
824
+ g.useAttach = 0;
816825
g.dbConfig = openDatabase(zDbName);
826
+ g.zConfigDbType = "configdb";
817827
}
818828
g.configOpen = 1;
819829
free(zDbName);
820830
}
821831
@@ -850,11 +860,11 @@
850860
char *zVFileDef;
851861
852862
if( file_access(zDbName, F_OK) ) return 0;
853863
lsize = file_size(zDbName);
854864
if( lsize%1024!=0 || lsize<4096 ) return 0;
855
- db_open_or_attach(zDbName, "localdb");
865
+ db_open_or_attach(zDbName, "localdb", 0);
856866
zVFileDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
857867
" WHERE name=='vfile'", db_name("localdb"));
858868
859869
/* If the "isexe" column is missing from the vfile table, then
860870
** add it now. This code added on 2010-03-06. After all users have
@@ -986,11 +996,11 @@
986996
g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
987997
#endif
988998
fossil_panic("not a valid repository: %s", zDbName);
989999
}
9901000
}
991
- db_open_or_attach(zDbName, "repository");
1001
+ db_open_or_attach(zDbName, "repository", 0);
9921002
g.repositoryOpen = 1;
9931003
g.zRepositoryName = mprintf("%s", zDbName);
9941004
/* Cache "allow-symlinks" option, because we'll need it on every stat call */
9951005
g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
9961006
}
@@ -1109,11 +1119,11 @@
11091119
file_canonical_name(g.argv[2], &repo, 0);
11101120
zRepo = blob_str(&repo);
11111121
if( file_access(zRepo, 0) ){
11121122
fossil_fatal("no such file: %s", zRepo);
11131123
}
1114
- db_open_or_attach(zRepo, "test_repo");
1124
+ db_open_or_attach(zRepo, "test_repo", 0);
11151125
db_lset("repository", blob_str(&repo));
11161126
db_close(1);
11171127
}
11181128
11191129
@@ -1177,13 +1187,15 @@
11771187
g.localOpen = 0;
11781188
g.configOpen = 0;
11791189
sqlite3_wal_checkpoint(g.db, 0);
11801190
sqlite3_close(g.db);
11811191
g.db = 0;
1192
+ g.zMainDbType = 0;
11821193
if( g.dbConfig ){
11831194
sqlite3_close(g.dbConfig);
11841195
g.dbConfig = 0;
1196
+ g.zConfigDbType = 0;
11851197
}
11861198
}
11871199
11881200
11891201
/*
@@ -1630,19 +1642,32 @@
16301642
/*
16311643
** Swap the g.db and g.dbConfig connections so that the various db_* routines
16321644
** work on the ~/.fossil database instead of on the repository database.
16331645
** Be sure to swap them back after doing the operation.
16341646
**
1635
-** If g.useAttach that means the ~/.fossil database was opened with
1636
-** the useAttach flag set to 1. In that case no connection swap is required
1637
-** so this routine is a no-op.
1647
+** If the ~/.fossil database has already been opened as the main database or
1648
+** is attached to the main database, no connection swaps are required so this
1649
+** routine is a no-op.
16381650
*/
16391651
void db_swap_connections(void){
1640
- if( !g.useAttach ){
1652
+ /*
1653
+ ** When swapping the main database connection with the config database
1654
+ ** connection, the config database connection must be open (not simply
1655
+ ** attached); otherwise, the swap would end up leaving the main database
1656
+ ** connection invalid, defeating the very purpose of this routine. This
1657
+ ** same constraint also holds true when restoring the previously swapped
1658
+ ** database connection; otherwise, it means that no swap was performed
1659
+ ** because the main database connection was already pointing to the config
1660
+ ** database.
1661
+ */
1662
+ if( g.dbConfig ){
16411663
sqlite3 *dbTemp = g.db;
1664
+ const char *zTempDbType = g.zMainDbType;
16421665
g.db = g.dbConfig;
1666
+ g.zMainDbType = g.zConfigDbType;
16431667
g.dbConfig = dbTemp;
1668
+ g.zConfigDbType = zTempDbType;
16441669
}
16451670
}
16461671
16471672
/*
16481673
** Logic for reading potentially versioned settings from
@@ -1672,11 +1697,11 @@
16721697
** and a checkout is open. */
16731698
if( cacheEntry==0 ){
16741699
Blob versionedPathname;
16751700
char *zVersionedPathname;
16761701
blob_zero(&versionedPathname);
1677
- blob_appendf(&versionedPathname, "%s/.fossil-settings/%s",
1702
+ blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
16781703
g.zLocalRoot, zName);
16791704
zVersionedPathname = blob_str(&versionedPathname);
16801705
if( file_size(zVersionedPathname)>=0 ){
16811706
/* File exists, and contains the value for this setting. Load from
16821707
** the file. */
@@ -2055,14 +2080,16 @@
20552080
{ "repo-cksum", 0, 0, 0, "on" },
20562081
{ "self-register", 0, 0, 0, "off" },
20572082
{ "ssl-ca-location",0, 40, 0, "" },
20582083
{ "ssl-identity", 0, 40, 0, "" },
20592084
{ "ssh-command", 0, 40, 0, "" },
2085
+ { "th1-setup", 0, 40, 0, "" },
20602086
#ifdef FOSSIL_ENABLE_TCL
20612087
{ "tcl", 0, 0, 0, "off" },
20622088
{ "tcl-setup", 0, 40, 0, "" },
20632089
#endif
2090
+ { "unicode-glob", 0, 40, 1, "" },
20642091
{ "web-browser", 0, 32, 0, "" },
20652092
{ "white-foreground", 0, 0, 0, "off" },
20662093
{ 0,0,0,0,0 }
20672094
};
20682095
@@ -2233,10 +2260,19 @@
22332260
**
22342261
** tcl-setup This is the setup script to be evaluated after creating
22352262
** and initializing the Tcl interpreter. By default, this
22362263
** is empty and no extra setup is performed.
22372264
**
2265
+** th1-setup This is the setup script to be evaluated after creating
2266
+** and initializing the TH1 interpreter. By default, this
2267
+** is empty and no extra setup is performed.
2268
+**
2269
+** unicode-glob The VALUE is a comma or newline-separated list of GLOB
2270
+** (versionable) patterns specifying files that the "commit" command will
2271
+** ignore when issuing warnings about text files that may
2272
+** contain Unicode. Set to "*" to disable Unicode checking.
2273
+**
22382274
** web-browser A shell command used to launch your preferred
22392275
** web browser when given a URL as an argument.
22402276
** Defaults to "start" on windows, "open" on Mac,
22412277
** and "firefox" on Unix.
22422278
**
22432279
--- src/db.c
+++ src/db.c
@@ -740,17 +740,25 @@
740 /*
741 ** zDbName is the name of a database file. If no other database
742 ** file is open, then open this one. If another database file is
743 ** already open, then attach zDbName using the name zLabel.
744 */
745 void db_open_or_attach(const char *zDbName, const char *zLabel){
 
 
 
 
746 if( !g.db ){
 
747 g.db = openDatabase(zDbName);
748 g.zMainDbType = zLabel;
749 db_connection_init();
 
750 }else{
 
751 db_attach(zDbName, zLabel);
 
752 }
753 }
754
755 /*
756 ** Open the user database in "~/.fossil". Create the database anew if
@@ -806,16 +814,18 @@
806 zDbName = mprintf("%s/.fossil", zHome);
807 #endif
808 if( file_size(zDbName)<1024*3 ){
809 db_init_database(zDbName, zConfigSchema, (char*)0);
810 }
811 g.useAttach = useAttach;
812 if( useAttach ){
813 db_open_or_attach(zDbName, "configdb");
814 g.dbConfig = 0;
 
815 }else{
 
816 g.dbConfig = openDatabase(zDbName);
 
817 }
818 g.configOpen = 1;
819 free(zDbName);
820 }
821
@@ -850,11 +860,11 @@
850 char *zVFileDef;
851
852 if( file_access(zDbName, F_OK) ) return 0;
853 lsize = file_size(zDbName);
854 if( lsize%1024!=0 || lsize<4096 ) return 0;
855 db_open_or_attach(zDbName, "localdb");
856 zVFileDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
857 " WHERE name=='vfile'", db_name("localdb"));
858
859 /* If the "isexe" column is missing from the vfile table, then
860 ** add it now. This code added on 2010-03-06. After all users have
@@ -986,11 +996,11 @@
986 g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
987 #endif
988 fossil_panic("not a valid repository: %s", zDbName);
989 }
990 }
991 db_open_or_attach(zDbName, "repository");
992 g.repositoryOpen = 1;
993 g.zRepositoryName = mprintf("%s", zDbName);
994 /* Cache "allow-symlinks" option, because we'll need it on every stat call */
995 g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
996 }
@@ -1109,11 +1119,11 @@
1109 file_canonical_name(g.argv[2], &repo, 0);
1110 zRepo = blob_str(&repo);
1111 if( file_access(zRepo, 0) ){
1112 fossil_fatal("no such file: %s", zRepo);
1113 }
1114 db_open_or_attach(zRepo, "test_repo");
1115 db_lset("repository", blob_str(&repo));
1116 db_close(1);
1117 }
1118
1119
@@ -1177,13 +1187,15 @@
1177 g.localOpen = 0;
1178 g.configOpen = 0;
1179 sqlite3_wal_checkpoint(g.db, 0);
1180 sqlite3_close(g.db);
1181 g.db = 0;
 
1182 if( g.dbConfig ){
1183 sqlite3_close(g.dbConfig);
1184 g.dbConfig = 0;
 
1185 }
1186 }
1187
1188
1189 /*
@@ -1630,19 +1642,32 @@
1630 /*
1631 ** Swap the g.db and g.dbConfig connections so that the various db_* routines
1632 ** work on the ~/.fossil database instead of on the repository database.
1633 ** Be sure to swap them back after doing the operation.
1634 **
1635 ** If g.useAttach that means the ~/.fossil database was opened with
1636 ** the useAttach flag set to 1. In that case no connection swap is required
1637 ** so this routine is a no-op.
1638 */
1639 void db_swap_connections(void){
1640 if( !g.useAttach ){
 
 
 
 
 
 
 
 
 
 
1641 sqlite3 *dbTemp = g.db;
 
1642 g.db = g.dbConfig;
 
1643 g.dbConfig = dbTemp;
 
1644 }
1645 }
1646
1647 /*
1648 ** Logic for reading potentially versioned settings from
@@ -1672,11 +1697,11 @@
1672 ** and a checkout is open. */
1673 if( cacheEntry==0 ){
1674 Blob versionedPathname;
1675 char *zVersionedPathname;
1676 blob_zero(&versionedPathname);
1677 blob_appendf(&versionedPathname, "%s/.fossil-settings/%s",
1678 g.zLocalRoot, zName);
1679 zVersionedPathname = blob_str(&versionedPathname);
1680 if( file_size(zVersionedPathname)>=0 ){
1681 /* File exists, and contains the value for this setting. Load from
1682 ** the file. */
@@ -2055,14 +2080,16 @@
2055 { "repo-cksum", 0, 0, 0, "on" },
2056 { "self-register", 0, 0, 0, "off" },
2057 { "ssl-ca-location",0, 40, 0, "" },
2058 { "ssl-identity", 0, 40, 0, "" },
2059 { "ssh-command", 0, 40, 0, "" },
 
2060 #ifdef FOSSIL_ENABLE_TCL
2061 { "tcl", 0, 0, 0, "off" },
2062 { "tcl-setup", 0, 40, 0, "" },
2063 #endif
 
2064 { "web-browser", 0, 32, 0, "" },
2065 { "white-foreground", 0, 0, 0, "off" },
2066 { 0,0,0,0,0 }
2067 };
2068
@@ -2233,10 +2260,19 @@
2233 **
2234 ** tcl-setup This is the setup script to be evaluated after creating
2235 ** and initializing the Tcl interpreter. By default, this
2236 ** is empty and no extra setup is performed.
2237 **
 
 
 
 
 
 
 
 
 
2238 ** web-browser A shell command used to launch your preferred
2239 ** web browser when given a URL as an argument.
2240 ** Defaults to "start" on windows, "open" on Mac,
2241 ** and "firefox" on Unix.
2242 **
2243
--- src/db.c
+++ src/db.c
@@ -740,17 +740,25 @@
740 /*
741 ** zDbName is the name of a database file. If no other database
742 ** file is open, then open this one. If another database file is
743 ** already open, then attach zDbName using the name zLabel.
744 */
745 void db_open_or_attach(
746 const char *zDbName,
747 const char *zLabel,
748 int *pWasAttached
749 ){
750 if( !g.db ){
751 assert( g.zMainDbType==0 );
752 g.db = openDatabase(zDbName);
753 g.zMainDbType = zLabel;
754 db_connection_init();
755 if ( pWasAttached ) *pWasAttached = 0;
756 }else{
757 assert( g.zMainDbType!=0 );
758 db_attach(zDbName, zLabel);
759 if ( pWasAttached ) *pWasAttached = 1;
760 }
761 }
762
763 /*
764 ** Open the user database in "~/.fossil". Create the database anew if
@@ -806,16 +814,18 @@
814 zDbName = mprintf("%s/.fossil", zHome);
815 #endif
816 if( file_size(zDbName)<1024*3 ){
817 db_init_database(zDbName, zConfigSchema, (char*)0);
818 }
 
819 if( useAttach ){
820 db_open_or_attach(zDbName, "configdb", &g.useAttach);
821 g.dbConfig = 0;
822 g.zConfigDbType = 0;
823 }else{
824 g.useAttach = 0;
825 g.dbConfig = openDatabase(zDbName);
826 g.zConfigDbType = "configdb";
827 }
828 g.configOpen = 1;
829 free(zDbName);
830 }
831
@@ -850,11 +860,11 @@
860 char *zVFileDef;
861
862 if( file_access(zDbName, F_OK) ) return 0;
863 lsize = file_size(zDbName);
864 if( lsize%1024!=0 || lsize<4096 ) return 0;
865 db_open_or_attach(zDbName, "localdb", 0);
866 zVFileDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
867 " WHERE name=='vfile'", db_name("localdb"));
868
869 /* If the "isexe" column is missing from the vfile table, then
870 ** add it now. This code added on 2010-03-06. After all users have
@@ -986,11 +996,11 @@
996 g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
997 #endif
998 fossil_panic("not a valid repository: %s", zDbName);
999 }
1000 }
1001 db_open_or_attach(zDbName, "repository", 0);
1002 g.repositoryOpen = 1;
1003 g.zRepositoryName = mprintf("%s", zDbName);
1004 /* Cache "allow-symlinks" option, because we'll need it on every stat call */
1005 g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
1006 }
@@ -1109,11 +1119,11 @@
1119 file_canonical_name(g.argv[2], &repo, 0);
1120 zRepo = blob_str(&repo);
1121 if( file_access(zRepo, 0) ){
1122 fossil_fatal("no such file: %s", zRepo);
1123 }
1124 db_open_or_attach(zRepo, "test_repo", 0);
1125 db_lset("repository", blob_str(&repo));
1126 db_close(1);
1127 }
1128
1129
@@ -1177,13 +1187,15 @@
1187 g.localOpen = 0;
1188 g.configOpen = 0;
1189 sqlite3_wal_checkpoint(g.db, 0);
1190 sqlite3_close(g.db);
1191 g.db = 0;
1192 g.zMainDbType = 0;
1193 if( g.dbConfig ){
1194 sqlite3_close(g.dbConfig);
1195 g.dbConfig = 0;
1196 g.zConfigDbType = 0;
1197 }
1198 }
1199
1200
1201 /*
@@ -1630,19 +1642,32 @@
1642 /*
1643 ** Swap the g.db and g.dbConfig connections so that the various db_* routines
1644 ** work on the ~/.fossil database instead of on the repository database.
1645 ** Be sure to swap them back after doing the operation.
1646 **
1647 ** If the ~/.fossil database has already been opened as the main database or
1648 ** is attached to the main database, no connection swaps are required so this
1649 ** routine is a no-op.
1650 */
1651 void db_swap_connections(void){
1652 /*
1653 ** When swapping the main database connection with the config database
1654 ** connection, the config database connection must be open (not simply
1655 ** attached); otherwise, the swap would end up leaving the main database
1656 ** connection invalid, defeating the very purpose of this routine. This
1657 ** same constraint also holds true when restoring the previously swapped
1658 ** database connection; otherwise, it means that no swap was performed
1659 ** because the main database connection was already pointing to the config
1660 ** database.
1661 */
1662 if( g.dbConfig ){
1663 sqlite3 *dbTemp = g.db;
1664 const char *zTempDbType = g.zMainDbType;
1665 g.db = g.dbConfig;
1666 g.zMainDbType = g.zConfigDbType;
1667 g.dbConfig = dbTemp;
1668 g.zConfigDbType = zTempDbType;
1669 }
1670 }
1671
1672 /*
1673 ** Logic for reading potentially versioned settings from
@@ -1672,11 +1697,11 @@
1697 ** and a checkout is open. */
1698 if( cacheEntry==0 ){
1699 Blob versionedPathname;
1700 char *zVersionedPathname;
1701 blob_zero(&versionedPathname);
1702 blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
1703 g.zLocalRoot, zName);
1704 zVersionedPathname = blob_str(&versionedPathname);
1705 if( file_size(zVersionedPathname)>=0 ){
1706 /* File exists, and contains the value for this setting. Load from
1707 ** the file. */
@@ -2055,14 +2080,16 @@
2080 { "repo-cksum", 0, 0, 0, "on" },
2081 { "self-register", 0, 0, 0, "off" },
2082 { "ssl-ca-location",0, 40, 0, "" },
2083 { "ssl-identity", 0, 40, 0, "" },
2084 { "ssh-command", 0, 40, 0, "" },
2085 { "th1-setup", 0, 40, 0, "" },
2086 #ifdef FOSSIL_ENABLE_TCL
2087 { "tcl", 0, 0, 0, "off" },
2088 { "tcl-setup", 0, 40, 0, "" },
2089 #endif
2090 { "unicode-glob", 0, 40, 1, "" },
2091 { "web-browser", 0, 32, 0, "" },
2092 { "white-foreground", 0, 0, 0, "off" },
2093 { 0,0,0,0,0 }
2094 };
2095
@@ -2233,10 +2260,19 @@
2260 **
2261 ** tcl-setup This is the setup script to be evaluated after creating
2262 ** and initializing the Tcl interpreter. By default, this
2263 ** is empty and no extra setup is performed.
2264 **
2265 ** th1-setup This is the setup script to be evaluated after creating
2266 ** and initializing the TH1 interpreter. By default, this
2267 ** is empty and no extra setup is performed.
2268 **
2269 ** unicode-glob The VALUE is a comma or newline-separated list of GLOB
2270 ** (versionable) patterns specifying files that the "commit" command will
2271 ** ignore when issuing warnings about text files that may
2272 ** contain Unicode. Set to "*" to disable Unicode checking.
2273 **
2274 ** web-browser A shell command used to launch your preferred
2275 ** web browser when given a URL as an argument.
2276 ** Defaults to "start" on windows, "open" on Mac,
2277 ** and "firefox" on Unix.
2278 **
2279
--- src/descendants.c
+++ src/descendants.c
@@ -441,5 +441,70 @@
441441
@ function xout(id){
442442
@ }
443443
@ </script>
444444
style_footer();
445445
}
446
+
447
+#if INTERFACE
448
+/* Flag parameters to compute_uses_file() */
449
+#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
450
+
451
+#endif
452
+
453
+
454
+/*
455
+** Add to table zTab the record ID (rid) of every check-in that contains
456
+** the file fid.
457
+*/
458
+void compute_uses_file(const char *zTab, int fid, int usesFlags){
459
+ Bag seen;
460
+ Bag pending;
461
+ Stmt ins;
462
+ Stmt q;
463
+ int rid;
464
+
465
+ bag_init(&seen);
466
+ bag_init(&pending);
467
+ db_prepare(&ins, "INSERT OR IGNORE INTO \"%s\" VALUES(:rid)", zTab);
468
+ db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
469
+ while( db_step(&q)==SQLITE_ROW ){
470
+ int mid = db_column_int(&q, 0);
471
+ bag_insert(&pending, mid);
472
+ bag_insert(&seen, mid);
473
+ db_bind_int(&ins, ":rid", mid);
474
+ db_step(&ins);
475
+ db_reset(&ins);
476
+ }
477
+ db_finalize(&q);
478
+
479
+ db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
480
+ while( db_step(&q)==SQLITE_ROW ){
481
+ int mid = db_column_int(&q, 0);
482
+ bag_insert(&seen, mid);
483
+ if( usesFlags & USESFILE_DELETE ){
484
+ db_bind_int(&ins, ":rid", mid);
485
+ db_step(&ins);
486
+ db_reset(&ins);
487
+ }
488
+ }
489
+ db_finalize(&q);
490
+ db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid");
491
+
492
+ while( (rid = bag_first(&pending))!=0 ){
493
+ bag_remove(&pending, rid);
494
+ db_bind_int(&q, ":rid", rid);
495
+ while( db_step(&q)==SQLITE_ROW ){
496
+ int mid = db_column_int(&q, 0);
497
+ if( bag_find(&seen, mid) ) continue;
498
+ bag_insert(&seen, mid);
499
+ bag_insert(&pending, mid);
500
+ db_bind_int(&ins, ":rid", mid);
501
+ db_step(&ins);
502
+ db_reset(&ins);
503
+ }
504
+ db_reset(&q);
505
+ }
506
+ db_finalize(&q);
507
+ db_finalize(&ins);
508
+ bag_clear(&seen);
509
+ bag_clear(&pending);
510
+}
446511
--- src/descendants.c
+++ src/descendants.c
@@ -441,5 +441,70 @@
441 @ function xout(id){
442 @ }
443 @ </script>
444 style_footer();
445 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
--- src/descendants.c
+++ src/descendants.c
@@ -441,5 +441,70 @@
441 @ function xout(id){
442 @ }
443 @ </script>
444 style_footer();
445 }
446
447 #if INTERFACE
448 /* Flag parameters to compute_uses_file() */
449 #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
450
451 #endif
452
453
454 /*
455 ** Add to table zTab the record ID (rid) of every check-in that contains
456 ** the file fid.
457 */
458 void compute_uses_file(const char *zTab, int fid, int usesFlags){
459 Bag seen;
460 Bag pending;
461 Stmt ins;
462 Stmt q;
463 int rid;
464
465 bag_init(&seen);
466 bag_init(&pending);
467 db_prepare(&ins, "INSERT OR IGNORE INTO \"%s\" VALUES(:rid)", zTab);
468 db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
469 while( db_step(&q)==SQLITE_ROW ){
470 int mid = db_column_int(&q, 0);
471 bag_insert(&pending, mid);
472 bag_insert(&seen, mid);
473 db_bind_int(&ins, ":rid", mid);
474 db_step(&ins);
475 db_reset(&ins);
476 }
477 db_finalize(&q);
478
479 db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
480 while( db_step(&q)==SQLITE_ROW ){
481 int mid = db_column_int(&q, 0);
482 bag_insert(&seen, mid);
483 if( usesFlags & USESFILE_DELETE ){
484 db_bind_int(&ins, ":rid", mid);
485 db_step(&ins);
486 db_reset(&ins);
487 }
488 }
489 db_finalize(&q);
490 db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid");
491
492 while( (rid = bag_first(&pending))!=0 ){
493 bag_remove(&pending, rid);
494 db_bind_int(&q, ":rid", rid);
495 while( db_step(&q)==SQLITE_ROW ){
496 int mid = db_column_int(&q, 0);
497 if( bag_find(&seen, mid) ) continue;
498 bag_insert(&seen, mid);
499 bag_insert(&pending, mid);
500 db_bind_int(&ins, ":rid", mid);
501 db_step(&ins);
502 db_reset(&ins);
503 }
504 db_reset(&q);
505 }
506 db_finalize(&q);
507 db_finalize(&ins);
508 bag_clear(&seen);
509 bag_clear(&pending);
510 }
511
+89 -26
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210210
** to be binary.
211211
**
212212
** (-1) -- The content appears to consist entirely of text, with lines
213213
** delimited by carriage-return, line-feed pairs.
214214
**
215
-** (-3, -5) The same as (1, -3); however, the encoding is not UTF-8 or ASCII.
215
+** (-3, -5) The same as (1, -1); however, the encoding is not UTF-8 or ASCII.
216216
**
217217
** (-4) -- The same as 0, but the determination is based on the fact that
218218
** the blob might be text (any encoding) but it has a line length
219219
** bigger than the diff logic in fossil can handle.
220220
**
@@ -238,18 +238,18 @@
238238
/* Check individual lines.
239239
*/
240240
if( n==0 ) return 1; /* Empty file -> text */
241241
c = *z;
242242
j = (c!='\n');
243
- if( c>=0x80 ){
243
+ if( c&0x80 ){
244244
CHECKUTF8(c)
245245
} else if( c==0 ){
246246
return 0; /* Zero byte in a file -> binary */
247247
}
248248
while( --n>0 ){
249249
c = *++z; ++j;
250
- if( c>=0x80 ){
250
+ if( c&0x80 ){
251251
CHECKUTF8(c)
252252
} else if( c==0 ){
253253
return 0; /* Zero byte in a file -> binary */
254254
} else if( c=='\n' ){
255255
if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359359
if( j>UTF16_LENGTH_MASK ){
360360
return -4; /* Very long line -> binary */
361361
}
362362
return result; /* No problems seen -> not binary */
363363
}
364
+
365
+/*
366
+** This function returns an array of bytes representing the byte-order-mark
367
+** for UTF-8.
368
+*/
369
+const unsigned char *get_utf8_bom(int *pnByte){
370
+ static const unsigned char bom[] = {
371
+ 0xEF, 0xBB, 0xBF, 0x00, 0x00, 0x00
372
+ };
373
+ if( pnByte ) *pnByte = 3;
374
+ return bom;
375
+}
376
+
377
+/*
378
+** This function returns non-zero if the blob starts with a UTF-8
379
+** byte-order-mark (BOM).
380
+*/
381
+int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
382
+ const char *z = blob_buffer(pContent);
383
+ int bomSize = 0;
384
+ const unsigned char *bom = get_utf8_bom(&bomSize);
385
+
386
+ if( pnByte ) *pnByte = bomSize;
387
+ if( blob_size(pContent)<bomSize ) return 0;
388
+ return memcmp(z, bom, bomSize)==0;
389
+}
364390
365391
/*
366392
** This function returns non-zero if the blob starts with a UTF-16le or
367393
** UTF-16be byte-order-mark (BOM).
368394
*/
369
-int starts_with_utf16_bom(const Blob *pContent){
395
+int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
370396
const char *z = blob_buffer(pContent);
371397
int c1, c2;
372398
399
+ if( pnByte ) *pnByte = 2;
373400
if( blob_size(pContent)<2 ) return 0;
374401
c1 = z[0]; c2 = z[1];
375402
if( (c1==(char)0xff) && (c2==(char)0xfe) ){
376403
return 1;
377404
}else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
378405
return 1;
379406
}
380407
return 0;
381408
}
409
+
410
+/*
411
+** This function returns non-zero if the blob starts with a UTF-16le
412
+** byte-order-mark (BOM).
413
+*/
414
+int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
415
+ const char *z = blob_buffer(pContent);
416
+ int c1, c2;
417
+
418
+ if( pnByte ) *pnByte = 2;
419
+ if( blob_size(pContent)<2 ) return 0;
420
+ c1 = z[0]; c2 = z[1];
421
+ if( (c1==(char)0xff) && (c2==(char)0xfe) ){
422
+ return 1;
423
+ }
424
+ return 0;
425
+}
426
+
427
+/*
428
+** This function returns non-zero if the blob starts with a UTF-16be
429
+** byte-order-mark (BOM).
430
+*/
431
+int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
432
+ const char *z = blob_buffer(pContent);
433
+ int c1, c2;
434
+
435
+ if( pnByte ) *pnByte = 2;
436
+ if( blob_size(pContent)<2 ) return 0;
437
+ c1 = z[0]; c2 = z[1];
438
+ if( (c1==(char)0xfe) && (c2==(char)0xff) ){
439
+ return 1;
440
+ }
441
+ return 0;
442
+}
382443
383444
/*
384445
** Return true if two DLine elements are identical.
385446
*/
386447
static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
20192080
Blob toAnnotate; /* Text of the final (mid) version of the file */
20202081
Blob step; /* Text of previous revision */
20212082
int rid; /* Artifact ID of the file being annotated */
20222083
char *zLabel; /* Label to apply to a line */
20232084
Stmt q; /* Query returning all ancestor versions */
2085
+ int cnt = 0; /* Number of versions examined */
20242086
20252087
/* Initialize the annotation */
20262088
rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
20272089
if( rid==0 ){
20282090
fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
20292091
}
20302092
if( !content_get(rid, &toAnnotate) ){
20312093
fossil_panic("unable to retrieve content of artifact #%d", rid);
20322094
}
2033
- db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
20342095
if( iLimit<=0 ) iLimit = 1000000000;
2035
- compute_direct_ancestors(mid, iLimit);
20362096
annotation_start(p, &toAnnotate);
2037
-
2097
+
20382098
db_prepare(&q,
2039
- "SELECT mlink.fid,"
2040
- " (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2041
- " date(event.mtime), "
2042
- " coalesce(event.euser,event.user) "
2043
- " FROM ancestor, mlink, event"
2044
- " WHERE mlink.fnid=%d"
2045
- " AND mlink.mid=ancestor.rid"
2046
- " AND event.objid=ancestor.rid"
2047
- " ORDER BY ancestor.generation ASC"
2048
- " LIMIT %d",
2049
- (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
2050
- fnid,
2051
- iLimit>0 ? iLimit : 10000000
2099
+ "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2100
+ " date(event.mtime),"
2101
+ " coalesce(event.euser,event.user),"
2102
+ " mlink.pid"
2103
+ " FROM mlink, event"
2104
+ " WHERE mlink.fid=:rid"
2105
+ " AND event.objid=mlink.mid"
2106
+ " ORDER BY event.mtime",
2107
+ (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
20522108
);
2053
- while( db_step(&q)==SQLITE_ROW ){
2054
- int pid = db_column_int(&q, 0);
2055
- const char *zUuid = db_column_text(&q, 1);
2056
- const char *zDate = db_column_text(&q, 2);
2057
- const char *zUser = db_column_text(&q, 3);
2109
+
2110
+ db_bind_int(&q, ":rid", rid);
2111
+ if( iLimit==0 ) iLimit = 1000000000;
2112
+ while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2113
+ const char *zUuid = db_column_text(&q, 0);
2114
+ const char *zDate = db_column_text(&q, 1);
2115
+ const char *zUser = db_column_text(&q, 2);
2116
+ int prevId = db_column_int(&q, 3);
20582117
if( webLabel ){
20592118
zLabel = mprintf(
20602119
"<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
20612120
zUuid, zUuid, zDate, zUser
20622121
);
@@ -2064,13 +2123,17 @@
20642123
zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
20652124
}
20662125
p->nVers++;
20672126
p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
20682127
p->azVers[p->nVers-1] = zLabel;
2069
- content_get(pid, &step);
2128
+ content_get(rid, &step);
20702129
annotation_step(p, &step, zLabel);
20712130
blob_reset(&step);
2131
+ db_reset(&q);
2132
+ rid = prevId;
2133
+ db_bind_int(&q, ":rid", prevId);
2134
+ cnt++;
20722135
}
20732136
db_finalize(&q);
20742137
}
20752138
20762139
/*
20772140
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210 ** to be binary.
211 **
212 ** (-1) -- The content appears to consist entirely of text, with lines
213 ** delimited by carriage-return, line-feed pairs.
214 **
215 ** (-3, -5) The same as (1, -3); however, the encoding is not UTF-8 or ASCII.
216 **
217 ** (-4) -- The same as 0, but the determination is based on the fact that
218 ** the blob might be text (any encoding) but it has a line length
219 ** bigger than the diff logic in fossil can handle.
220 **
@@ -238,18 +238,18 @@
238 /* Check individual lines.
239 */
240 if( n==0 ) return 1; /* Empty file -> text */
241 c = *z;
242 j = (c!='\n');
243 if( c>=0x80 ){
244 CHECKUTF8(c)
245 } else if( c==0 ){
246 return 0; /* Zero byte in a file -> binary */
247 }
248 while( --n>0 ){
249 c = *++z; ++j;
250 if( c>=0x80 ){
251 CHECKUTF8(c)
252 } else if( c==0 ){
253 return 0; /* Zero byte in a file -> binary */
254 } else if( c=='\n' ){
255 if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359 if( j>UTF16_LENGTH_MASK ){
360 return -4; /* Very long line -> binary */
361 }
362 return result; /* No problems seen -> not binary */
363 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
365 /*
366 ** This function returns non-zero if the blob starts with a UTF-16le or
367 ** UTF-16be byte-order-mark (BOM).
368 */
369 int starts_with_utf16_bom(const Blob *pContent){
370 const char *z = blob_buffer(pContent);
371 int c1, c2;
372
 
373 if( blob_size(pContent)<2 ) return 0;
374 c1 = z[0]; c2 = z[1];
375 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
376 return 1;
377 }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
378 return 1;
379 }
380 return 0;
381 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
383 /*
384 ** Return true if two DLine elements are identical.
385 */
386 static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
2019 Blob toAnnotate; /* Text of the final (mid) version of the file */
2020 Blob step; /* Text of previous revision */
2021 int rid; /* Artifact ID of the file being annotated */
2022 char *zLabel; /* Label to apply to a line */
2023 Stmt q; /* Query returning all ancestor versions */
 
2024
2025 /* Initialize the annotation */
2026 rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
2027 if( rid==0 ){
2028 fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
2029 }
2030 if( !content_get(rid, &toAnnotate) ){
2031 fossil_panic("unable to retrieve content of artifact #%d", rid);
2032 }
2033 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
2034 if( iLimit<=0 ) iLimit = 1000000000;
2035 compute_direct_ancestors(mid, iLimit);
2036 annotation_start(p, &toAnnotate);
2037
2038 db_prepare(&q,
2039 "SELECT mlink.fid,"
2040 " (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2041 " date(event.mtime), "
2042 " coalesce(event.euser,event.user) "
2043 " FROM ancestor, mlink, event"
2044 " WHERE mlink.fnid=%d"
2045 " AND mlink.mid=ancestor.rid"
2046 " AND event.objid=ancestor.rid"
2047 " ORDER BY ancestor.generation ASC"
2048 " LIMIT %d",
2049 (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
2050 fnid,
2051 iLimit>0 ? iLimit : 10000000
2052 );
2053 while( db_step(&q)==SQLITE_ROW ){
2054 int pid = db_column_int(&q, 0);
2055 const char *zUuid = db_column_text(&q, 1);
2056 const char *zDate = db_column_text(&q, 2);
2057 const char *zUser = db_column_text(&q, 3);
 
 
 
2058 if( webLabel ){
2059 zLabel = mprintf(
2060 "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2061 zUuid, zUuid, zDate, zUser
2062 );
@@ -2064,13 +2123,17 @@
2064 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2065 }
2066 p->nVers++;
2067 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2068 p->azVers[p->nVers-1] = zLabel;
2069 content_get(pid, &step);
2070 annotation_step(p, &step, zLabel);
2071 blob_reset(&step);
 
 
 
 
2072 }
2073 db_finalize(&q);
2074 }
2075
2076 /*
2077
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210 ** to be binary.
211 **
212 ** (-1) -- The content appears to consist entirely of text, with lines
213 ** delimited by carriage-return, line-feed pairs.
214 **
215 ** (-3, -5) The same as (1, -1); however, the encoding is not UTF-8 or ASCII.
216 **
217 ** (-4) -- The same as 0, but the determination is based on the fact that
218 ** the blob might be text (any encoding) but it has a line length
219 ** bigger than the diff logic in fossil can handle.
220 **
@@ -238,18 +238,18 @@
238 /* Check individual lines.
239 */
240 if( n==0 ) return 1; /* Empty file -> text */
241 c = *z;
242 j = (c!='\n');
243 if( c&0x80 ){
244 CHECKUTF8(c)
245 } else if( c==0 ){
246 return 0; /* Zero byte in a file -> binary */
247 }
248 while( --n>0 ){
249 c = *++z; ++j;
250 if( c&0x80 ){
251 CHECKUTF8(c)
252 } else if( c==0 ){
253 return 0; /* Zero byte in a file -> binary */
254 } else if( c=='\n' ){
255 if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359 if( j>UTF16_LENGTH_MASK ){
360 return -4; /* Very long line -> binary */
361 }
362 return result; /* No problems seen -> not binary */
363 }
364
365 /*
366 ** This function returns an array of bytes representing the byte-order-mark
367 ** for UTF-8.
368 */
369 const unsigned char *get_utf8_bom(int *pnByte){
370 static const unsigned char bom[] = {
371 0xEF, 0xBB, 0xBF, 0x00, 0x00, 0x00
372 };
373 if( pnByte ) *pnByte = 3;
374 return bom;
375 }
376
377 /*
378 ** This function returns non-zero if the blob starts with a UTF-8
379 ** byte-order-mark (BOM).
380 */
381 int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
382 const char *z = blob_buffer(pContent);
383 int bomSize = 0;
384 const unsigned char *bom = get_utf8_bom(&bomSize);
385
386 if( pnByte ) *pnByte = bomSize;
387 if( blob_size(pContent)<bomSize ) return 0;
388 return memcmp(z, bom, bomSize)==0;
389 }
390
391 /*
392 ** This function returns non-zero if the blob starts with a UTF-16le or
393 ** UTF-16be byte-order-mark (BOM).
394 */
395 int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
396 const char *z = blob_buffer(pContent);
397 int c1, c2;
398
399 if( pnByte ) *pnByte = 2;
400 if( blob_size(pContent)<2 ) return 0;
401 c1 = z[0]; c2 = z[1];
402 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
403 return 1;
404 }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
405 return 1;
406 }
407 return 0;
408 }
409
410 /*
411 ** This function returns non-zero if the blob starts with a UTF-16le
412 ** byte-order-mark (BOM).
413 */
414 int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
415 const char *z = blob_buffer(pContent);
416 int c1, c2;
417
418 if( pnByte ) *pnByte = 2;
419 if( blob_size(pContent)<2 ) return 0;
420 c1 = z[0]; c2 = z[1];
421 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
422 return 1;
423 }
424 return 0;
425 }
426
427 /*
428 ** This function returns non-zero if the blob starts with a UTF-16be
429 ** byte-order-mark (BOM).
430 */
431 int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
432 const char *z = blob_buffer(pContent);
433 int c1, c2;
434
435 if( pnByte ) *pnByte = 2;
436 if( blob_size(pContent)<2 ) return 0;
437 c1 = z[0]; c2 = z[1];
438 if( (c1==(char)0xfe) && (c2==(char)0xff) ){
439 return 1;
440 }
441 return 0;
442 }
443
444 /*
445 ** Return true if two DLine elements are identical.
446 */
447 static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
2080 Blob toAnnotate; /* Text of the final (mid) version of the file */
2081 Blob step; /* Text of previous revision */
2082 int rid; /* Artifact ID of the file being annotated */
2083 char *zLabel; /* Label to apply to a line */
2084 Stmt q; /* Query returning all ancestor versions */
2085 int cnt = 0; /* Number of versions examined */
2086
2087 /* Initialize the annotation */
2088 rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
2089 if( rid==0 ){
2090 fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
2091 }
2092 if( !content_get(rid, &toAnnotate) ){
2093 fossil_panic("unable to retrieve content of artifact #%d", rid);
2094 }
 
2095 if( iLimit<=0 ) iLimit = 1000000000;
 
2096 annotation_start(p, &toAnnotate);
2097
2098 db_prepare(&q,
2099 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2100 " date(event.mtime),"
2101 " coalesce(event.euser,event.user),"
2102 " mlink.pid"
2103 " FROM mlink, event"
2104 " WHERE mlink.fid=:rid"
2105 " AND event.objid=mlink.mid"
2106 " ORDER BY event.mtime",
2107 (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
 
 
 
 
2108 );
2109
2110 db_bind_int(&q, ":rid", rid);
2111 if( iLimit==0 ) iLimit = 1000000000;
2112 while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2113 const char *zUuid = db_column_text(&q, 0);
2114 const char *zDate = db_column_text(&q, 1);
2115 const char *zUser = db_column_text(&q, 2);
2116 int prevId = db_column_int(&q, 3);
2117 if( webLabel ){
2118 zLabel = mprintf(
2119 "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2120 zUuid, zUuid, zDate, zUser
2121 );
@@ -2064,13 +2123,17 @@
2123 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2124 }
2125 p->nVers++;
2126 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2127 p->azVers[p->nVers-1] = zLabel;
2128 content_get(rid, &step);
2129 annotation_step(p, &step, zLabel);
2130 blob_reset(&step);
2131 db_reset(&q);
2132 rid = prevId;
2133 db_bind_int(&q, ":rid", prevId);
2134 cnt++;
2135 }
2136 db_finalize(&q);
2137 }
2138
2139 /*
2140
+89 -26
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210210
** to be binary.
211211
**
212212
** (-1) -- The content appears to consist entirely of text, with lines
213213
** delimited by carriage-return, line-feed pairs.
214214
**
215
-** (-3, -5) The same as (1, -3); however, the encoding is not UTF-8 or ASCII.
215
+** (-3, -5) The same as (1, -1); however, the encoding is not UTF-8 or ASCII.
216216
**
217217
** (-4) -- The same as 0, but the determination is based on the fact that
218218
** the blob might be text (any encoding) but it has a line length
219219
** bigger than the diff logic in fossil can handle.
220220
**
@@ -238,18 +238,18 @@
238238
/* Check individual lines.
239239
*/
240240
if( n==0 ) return 1; /* Empty file -> text */
241241
c = *z;
242242
j = (c!='\n');
243
- if( c>=0x80 ){
243
+ if( c&0x80 ){
244244
CHECKUTF8(c)
245245
} else if( c==0 ){
246246
return 0; /* Zero byte in a file -> binary */
247247
}
248248
while( --n>0 ){
249249
c = *++z; ++j;
250
- if( c>=0x80 ){
250
+ if( c&0x80 ){
251251
CHECKUTF8(c)
252252
} else if( c==0 ){
253253
return 0; /* Zero byte in a file -> binary */
254254
} else if( c=='\n' ){
255255
if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359359
if( j>UTF16_LENGTH_MASK ){
360360
return -4; /* Very long line -> binary */
361361
}
362362
return result; /* No problems seen -> not binary */
363363
}
364
+
365
+/*
366
+** This function returns an array of bytes representing the byte-order-mark
367
+** for UTF-8.
368
+*/
369
+const unsigned char *get_utf8_bom(int *pnByte){
370
+ static const unsigned char bom[] = {
371
+ 0xEF, 0xBB, 0xBF, 0x00, 0x00, 0x00
372
+ };
373
+ if( pnByte ) *pnByte = 3;
374
+ return bom;
375
+}
376
+
377
+/*
378
+** This function returns non-zero if the blob starts with a UTF-8
379
+** byte-order-mark (BOM).
380
+*/
381
+int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
382
+ const char *z = blob_buffer(pContent);
383
+ int bomSize = 0;
384
+ const unsigned char *bom = get_utf8_bom(&bomSize);
385
+
386
+ if( pnByte ) *pnByte = bomSize;
387
+ if( blob_size(pContent)<bomSize ) return 0;
388
+ return memcmp(z, bom, bomSize)==0;
389
+}
364390
365391
/*
366392
** This function returns non-zero if the blob starts with a UTF-16le or
367393
** UTF-16be byte-order-mark (BOM).
368394
*/
369
-int starts_with_utf16_bom(const Blob *pContent){
395
+int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
370396
const char *z = blob_buffer(pContent);
371397
int c1, c2;
372398
399
+ if( pnByte ) *pnByte = 2;
373400
if( blob_size(pContent)<2 ) return 0;
374401
c1 = z[0]; c2 = z[1];
375402
if( (c1==(char)0xff) && (c2==(char)0xfe) ){
376403
return 1;
377404
}else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
378405
return 1;
379406
}
380407
return 0;
381408
}
409
+
410
+/*
411
+** This function returns non-zero if the blob starts with a UTF-16le
412
+** byte-order-mark (BOM).
413
+*/
414
+int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
415
+ const char *z = blob_buffer(pContent);
416
+ int c1, c2;
417
+
418
+ if( pnByte ) *pnByte = 2;
419
+ if( blob_size(pContent)<2 ) return 0;
420
+ c1 = z[0]; c2 = z[1];
421
+ if( (c1==(char)0xff) && (c2==(char)0xfe) ){
422
+ return 1;
423
+ }
424
+ return 0;
425
+}
426
+
427
+/*
428
+** This function returns non-zero if the blob starts with a UTF-16be
429
+** byte-order-mark (BOM).
430
+*/
431
+int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
432
+ const char *z = blob_buffer(pContent);
433
+ int c1, c2;
434
+
435
+ if( pnByte ) *pnByte = 2;
436
+ if( blob_size(pContent)<2 ) return 0;
437
+ c1 = z[0]; c2 = z[1];
438
+ if( (c1==(char)0xfe) && (c2==(char)0xff) ){
439
+ return 1;
440
+ }
441
+ return 0;
442
+}
382443
383444
/*
384445
** Return true if two DLine elements are identical.
385446
*/
386447
static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
20192080
Blob toAnnotate; /* Text of the final (mid) version of the file */
20202081
Blob step; /* Text of previous revision */
20212082
int rid; /* Artifact ID of the file being annotated */
20222083
char *zLabel; /* Label to apply to a line */
20232084
Stmt q; /* Query returning all ancestor versions */
2085
+ int cnt = 0; /* Number of versions examined */
20242086
20252087
/* Initialize the annotation */
20262088
rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
20272089
if( rid==0 ){
20282090
fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
20292091
}
20302092
if( !content_get(rid, &toAnnotate) ){
20312093
fossil_panic("unable to retrieve content of artifact #%d", rid);
20322094
}
2033
- db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
20342095
if( iLimit<=0 ) iLimit = 1000000000;
2035
- compute_direct_ancestors(mid, iLimit);
20362096
annotation_start(p, &toAnnotate);
2037
-
2097
+
20382098
db_prepare(&q,
2039
- "SELECT mlink.fid,"
2040
- " (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2041
- " date(event.mtime), "
2042
- " coalesce(event.euser,event.user) "
2043
- " FROM ancestor, mlink, event"
2044
- " WHERE mlink.fnid=%d"
2045
- " AND mlink.mid=ancestor.rid"
2046
- " AND event.objid=ancestor.rid"
2047
- " ORDER BY ancestor.generation ASC"
2048
- " LIMIT %d",
2049
- (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
2050
- fnid,
2051
- iLimit>0 ? iLimit : 10000000
2099
+ "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2100
+ " date(event.mtime),"
2101
+ " coalesce(event.euser,event.user),"
2102
+ " mlink.pid"
2103
+ " FROM mlink, event"
2104
+ " WHERE mlink.fid=:rid"
2105
+ " AND event.objid=mlink.mid"
2106
+ " ORDER BY event.mtime",
2107
+ (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
20522108
);
2053
- while( db_step(&q)==SQLITE_ROW ){
2054
- int pid = db_column_int(&q, 0);
2055
- const char *zUuid = db_column_text(&q, 1);
2056
- const char *zDate = db_column_text(&q, 2);
2057
- const char *zUser = db_column_text(&q, 3);
2109
+
2110
+ db_bind_int(&q, ":rid", rid);
2111
+ if( iLimit==0 ) iLimit = 1000000000;
2112
+ while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2113
+ const char *zUuid = db_column_text(&q, 0);
2114
+ const char *zDate = db_column_text(&q, 1);
2115
+ const char *zUser = db_column_text(&q, 2);
2116
+ int prevId = db_column_int(&q, 3);
20582117
if( webLabel ){
20592118
zLabel = mprintf(
20602119
"<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
20612120
zUuid, zUuid, zDate, zUser
20622121
);
@@ -2064,13 +2123,17 @@
20642123
zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
20652124
}
20662125
p->nVers++;
20672126
p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
20682127
p->azVers[p->nVers-1] = zLabel;
2069
- content_get(pid, &step);
2128
+ content_get(rid, &step);
20702129
annotation_step(p, &step, zLabel);
20712130
blob_reset(&step);
2131
+ db_reset(&q);
2132
+ rid = prevId;
2133
+ db_bind_int(&q, ":rid", prevId);
2134
+ cnt++;
20722135
}
20732136
db_finalize(&q);
20742137
}
20752138
20762139
/*
20772140
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210 ** to be binary.
211 **
212 ** (-1) -- The content appears to consist entirely of text, with lines
213 ** delimited by carriage-return, line-feed pairs.
214 **
215 ** (-3, -5) The same as (1, -3); however, the encoding is not UTF-8 or ASCII.
216 **
217 ** (-4) -- The same as 0, but the determination is based on the fact that
218 ** the blob might be text (any encoding) but it has a line length
219 ** bigger than the diff logic in fossil can handle.
220 **
@@ -238,18 +238,18 @@
238 /* Check individual lines.
239 */
240 if( n==0 ) return 1; /* Empty file -> text */
241 c = *z;
242 j = (c!='\n');
243 if( c>=0x80 ){
244 CHECKUTF8(c)
245 } else if( c==0 ){
246 return 0; /* Zero byte in a file -> binary */
247 }
248 while( --n>0 ){
249 c = *++z; ++j;
250 if( c>=0x80 ){
251 CHECKUTF8(c)
252 } else if( c==0 ){
253 return 0; /* Zero byte in a file -> binary */
254 } else if( c=='\n' ){
255 if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359 if( j>UTF16_LENGTH_MASK ){
360 return -4; /* Very long line -> binary */
361 }
362 return result; /* No problems seen -> not binary */
363 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
365 /*
366 ** This function returns non-zero if the blob starts with a UTF-16le or
367 ** UTF-16be byte-order-mark (BOM).
368 */
369 int starts_with_utf16_bom(const Blob *pContent){
370 const char *z = blob_buffer(pContent);
371 int c1, c2;
372
 
373 if( blob_size(pContent)<2 ) return 0;
374 c1 = z[0]; c2 = z[1];
375 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
376 return 1;
377 }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
378 return 1;
379 }
380 return 0;
381 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
383 /*
384 ** Return true if two DLine elements are identical.
385 */
386 static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
2019 Blob toAnnotate; /* Text of the final (mid) version of the file */
2020 Blob step; /* Text of previous revision */
2021 int rid; /* Artifact ID of the file being annotated */
2022 char *zLabel; /* Label to apply to a line */
2023 Stmt q; /* Query returning all ancestor versions */
 
2024
2025 /* Initialize the annotation */
2026 rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
2027 if( rid==0 ){
2028 fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
2029 }
2030 if( !content_get(rid, &toAnnotate) ){
2031 fossil_panic("unable to retrieve content of artifact #%d", rid);
2032 }
2033 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
2034 if( iLimit<=0 ) iLimit = 1000000000;
2035 compute_direct_ancestors(mid, iLimit);
2036 annotation_start(p, &toAnnotate);
2037
2038 db_prepare(&q,
2039 "SELECT mlink.fid,"
2040 " (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2041 " date(event.mtime), "
2042 " coalesce(event.euser,event.user) "
2043 " FROM ancestor, mlink, event"
2044 " WHERE mlink.fnid=%d"
2045 " AND mlink.mid=ancestor.rid"
2046 " AND event.objid=ancestor.rid"
2047 " ORDER BY ancestor.generation ASC"
2048 " LIMIT %d",
2049 (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
2050 fnid,
2051 iLimit>0 ? iLimit : 10000000
2052 );
2053 while( db_step(&q)==SQLITE_ROW ){
2054 int pid = db_column_int(&q, 0);
2055 const char *zUuid = db_column_text(&q, 1);
2056 const char *zDate = db_column_text(&q, 2);
2057 const char *zUser = db_column_text(&q, 3);
 
 
 
2058 if( webLabel ){
2059 zLabel = mprintf(
2060 "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2061 zUuid, zUuid, zDate, zUser
2062 );
@@ -2064,13 +2123,17 @@
2064 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2065 }
2066 p->nVers++;
2067 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2068 p->azVers[p->nVers-1] = zLabel;
2069 content_get(pid, &step);
2070 annotation_step(p, &step, zLabel);
2071 blob_reset(&step);
 
 
 
 
2072 }
2073 db_finalize(&q);
2074 }
2075
2076 /*
2077
--- src/diff.c
+++ src/diff.c
@@ -210,11 +210,11 @@
210 ** to be binary.
211 **
212 ** (-1) -- The content appears to consist entirely of text, with lines
213 ** delimited by carriage-return, line-feed pairs.
214 **
215 ** (-3, -5) The same as (1, -1); however, the encoding is not UTF-8 or ASCII.
216 **
217 ** (-4) -- The same as 0, but the determination is based on the fact that
218 ** the blob might be text (any encoding) but it has a line length
219 ** bigger than the diff logic in fossil can handle.
220 **
@@ -238,18 +238,18 @@
238 /* Check individual lines.
239 */
240 if( n==0 ) return 1; /* Empty file -> text */
241 c = *z;
242 j = (c!='\n');
243 if( c&0x80 ){
244 CHECKUTF8(c)
245 } else if( c==0 ){
246 return 0; /* Zero byte in a file -> binary */
247 }
248 while( --n>0 ){
249 c = *++z; ++j;
250 if( c&0x80 ){
251 CHECKUTF8(c)
252 } else if( c==0 ){
253 return 0; /* Zero byte in a file -> binary */
254 } else if( c=='\n' ){
255 if( z[-1]=='\r' ){
@@ -359,28 +359,89 @@
359 if( j>UTF16_LENGTH_MASK ){
360 return -4; /* Very long line -> binary */
361 }
362 return result; /* No problems seen -> not binary */
363 }
364
365 /*
366 ** This function returns an array of bytes representing the byte-order-mark
367 ** for UTF-8.
368 */
369 const unsigned char *get_utf8_bom(int *pnByte){
370 static const unsigned char bom[] = {
371 0xEF, 0xBB, 0xBF, 0x00, 0x00, 0x00
372 };
373 if( pnByte ) *pnByte = 3;
374 return bom;
375 }
376
377 /*
378 ** This function returns non-zero if the blob starts with a UTF-8
379 ** byte-order-mark (BOM).
380 */
381 int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
382 const char *z = blob_buffer(pContent);
383 int bomSize = 0;
384 const unsigned char *bom = get_utf8_bom(&bomSize);
385
386 if( pnByte ) *pnByte = bomSize;
387 if( blob_size(pContent)<bomSize ) return 0;
388 return memcmp(z, bom, bomSize)==0;
389 }
390
391 /*
392 ** This function returns non-zero if the blob starts with a UTF-16le or
393 ** UTF-16be byte-order-mark (BOM).
394 */
395 int starts_with_utf16_bom(const Blob *pContent, int *pnByte){
396 const char *z = blob_buffer(pContent);
397 int c1, c2;
398
399 if( pnByte ) *pnByte = 2;
400 if( blob_size(pContent)<2 ) return 0;
401 c1 = z[0]; c2 = z[1];
402 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
403 return 1;
404 }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){
405 return 1;
406 }
407 return 0;
408 }
409
410 /*
411 ** This function returns non-zero if the blob starts with a UTF-16le
412 ** byte-order-mark (BOM).
413 */
414 int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){
415 const char *z = blob_buffer(pContent);
416 int c1, c2;
417
418 if( pnByte ) *pnByte = 2;
419 if( blob_size(pContent)<2 ) return 0;
420 c1 = z[0]; c2 = z[1];
421 if( (c1==(char)0xff) && (c2==(char)0xfe) ){
422 return 1;
423 }
424 return 0;
425 }
426
427 /*
428 ** This function returns non-zero if the blob starts with a UTF-16be
429 ** byte-order-mark (BOM).
430 */
431 int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){
432 const char *z = blob_buffer(pContent);
433 int c1, c2;
434
435 if( pnByte ) *pnByte = 2;
436 if( blob_size(pContent)<2 ) return 0;
437 c1 = z[0]; c2 = z[1];
438 if( (c1==(char)0xfe) && (c2==(char)0xff) ){
439 return 1;
440 }
441 return 0;
442 }
443
444 /*
445 ** Return true if two DLine elements are identical.
446 */
447 static int same_dline(DLine *pA, DLine *pB){
@@ -2019,44 +2080,42 @@
2080 Blob toAnnotate; /* Text of the final (mid) version of the file */
2081 Blob step; /* Text of previous revision */
2082 int rid; /* Artifact ID of the file being annotated */
2083 char *zLabel; /* Label to apply to a line */
2084 Stmt q; /* Query returning all ancestor versions */
2085 int cnt = 0; /* Number of versions examined */
2086
2087 /* Initialize the annotation */
2088 rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
2089 if( rid==0 ){
2090 fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
2091 }
2092 if( !content_get(rid, &toAnnotate) ){
2093 fossil_panic("unable to retrieve content of artifact #%d", rid);
2094 }
 
2095 if( iLimit<=0 ) iLimit = 1000000000;
 
2096 annotation_start(p, &toAnnotate);
2097
2098 db_prepare(&q,
2099 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2100 " date(event.mtime),"
2101 " coalesce(event.euser,event.user),"
2102 " mlink.pid"
2103 " FROM mlink, event"
2104 " WHERE mlink.fid=:rid"
2105 " AND event.objid=mlink.mid"
2106 " ORDER BY event.mtime",
2107 (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
 
 
 
 
2108 );
2109
2110 db_bind_int(&q, ":rid", rid);
2111 if( iLimit==0 ) iLimit = 1000000000;
2112 while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2113 const char *zUuid = db_column_text(&q, 0);
2114 const char *zDate = db_column_text(&q, 1);
2115 const char *zUser = db_column_text(&q, 2);
2116 int prevId = db_column_int(&q, 3);
2117 if( webLabel ){
2118 zLabel = mprintf(
2119 "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2120 zUuid, zUuid, zDate, zUser
2121 );
@@ -2064,13 +2123,17 @@
2123 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2124 }
2125 p->nVers++;
2126 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2127 p->azVers[p->nVers-1] = zLabel;
2128 content_get(rid, &step);
2129 annotation_step(p, &step, zLabel);
2130 blob_reset(&step);
2131 db_reset(&q);
2132 rid = prevId;
2133 db_bind_int(&q, ":rid", prevId);
2134 cnt++;
2135 }
2136 db_finalize(&q);
2137 }
2138
2139 /*
2140
+34
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
8383
zIn++;
8484
}
8585
zOut[i] = 0;
8686
return zOut;
8787
}
88
+
89
+/*
90
+** Append HTML-escaped text to a Blob.
91
+*/
92
+void htmlize_to_blob(Blob *p, const char *zIn, int n){
93
+ int c, i, j;
94
+ if( n<0 ) n = strlen(zIn);
95
+ for(i=j=0; i<n; i++){
96
+ c = zIn[i];
97
+ switch( c ){
98
+ case '<':
99
+ if( j<i ) blob_append(p, zIn+j, i-j);
100
+ blob_append(p, "&lt;", 4);
101
+ j = i+1;
102
+ break;
103
+ case '>':
104
+ if( j<i ) blob_append(p, zIn+j, i-j);
105
+ blob_append(p, "&gt;", 4);
106
+ j = i+1;
107
+ break;
108
+ case '&':
109
+ if( j<i ) blob_append(p, zIn+j, i-j);
110
+ blob_append(p, "&amp;", 5);
111
+ j = i+1;
112
+ break;
113
+ case '"':
114
+ if( j<i ) blob_append(p, zIn+j, i-j);
115
+ blob_append(p, "&quot;", 6);
116
+ j = i+1;
117
+ break;
118
+ }
119
+ }
120
+ if( j<i ) blob_append(p, zIn+j, i-j);
121
+}
88122
89123
90124
/*
91125
** Encode a string for HTTP. This means converting lots of
92126
** characters into the "%HH" where H is a hex digit. It also
93127
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
83 zIn++;
84 }
85 zOut[i] = 0;
86 return zOut;
87 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
89
90 /*
91 ** Encode a string for HTTP. This means converting lots of
92 ** characters into the "%HH" where H is a hex digit. It also
93
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
83 zIn++;
84 }
85 zOut[i] = 0;
86 return zOut;
87 }
88
89 /*
90 ** Append HTML-escaped text to a Blob.
91 */
92 void htmlize_to_blob(Blob *p, const char *zIn, int n){
93 int c, i, j;
94 if( n<0 ) n = strlen(zIn);
95 for(i=j=0; i<n; i++){
96 c = zIn[i];
97 switch( c ){
98 case '<':
99 if( j<i ) blob_append(p, zIn+j, i-j);
100 blob_append(p, "&lt;", 4);
101 j = i+1;
102 break;
103 case '>':
104 if( j<i ) blob_append(p, zIn+j, i-j);
105 blob_append(p, "&gt;", 4);
106 j = i+1;
107 break;
108 case '&':
109 if( j<i ) blob_append(p, zIn+j, i-j);
110 blob_append(p, "&amp;", 5);
111 j = i+1;
112 break;
113 case '"':
114 if( j<i ) blob_append(p, zIn+j, i-j);
115 blob_append(p, "&quot;", 6);
116 j = i+1;
117 break;
118 }
119 }
120 if( j<i ) blob_append(p, zIn+j, i-j);
121 }
122
123
124 /*
125 ** Encode a string for HTTP. This means converting lots of
126 ** characters into the "%HH" where H is a hex digit. It also
127
+1 -1
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365365
}else{
366366
@ <tr><td>
367367
}
368368
blob_zero(&com);
369369
blob_append(&com, zComment, -1);
370
- wiki_convert(&com, 0, WIKI_INLINE);
370
+ wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
371371
@ </td></tr></table>
372372
@ </blockquote>
373373
@ <p><b>Page content preview:</b><p>
374374
@ <blockquote>
375375
blob_zero(&event);
376376
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365 }else{
366 @ <tr><td>
367 }
368 blob_zero(&com);
369 blob_append(&com, zComment, -1);
370 wiki_convert(&com, 0, WIKI_INLINE);
371 @ </td></tr></table>
372 @ </blockquote>
373 @ <p><b>Page content preview:</b><p>
374 @ <blockquote>
375 blob_zero(&event);
376
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365 }else{
366 @ <tr><td>
367 }
368 blob_zero(&com);
369 blob_append(&com, zComment, -1);
370 wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
371 @ </td></tr></table>
372 @ </blockquote>
373 @ <p><b>Page content preview:</b><p>
374 @ <blockquote>
375 blob_zero(&event);
376
+11 -3
--- src/export.c
+++ src/export.c
@@ -166,16 +166,19 @@
166166
167167
/* Step 1: Generate "blob" records for every artifact that is part
168168
** of a check-in
169169
*/
170170
fossil_binary_mode(stdout);
171
- db_multi_exec("CREATE TEMPORARY TABLE newblob(rid INTEGER KEY, srcid INTEGER)");
171
+ db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)");
172172
db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)");
173173
db_multi_exec(
174174
"INSERT INTO newblob"
175175
" SELECT DISTINCT fid,"
176
- " CASE WHEN EXISTS(SELECT 1 FROM delta WHERE rid=fid AND NOT EXISTS(SELECT 1 FROM oldblob WHERE srcid=fid))"
176
+ " CASE WHEN EXISTS(SELECT 1 FROM delta"
177
+ " WHERE rid=fid"
178
+ " AND NOT EXISTS(SELECT 1 FROM oldblob"
179
+ " WHERE srcid=fid))"
177180
" THEN (SELECT srcid FROM delta WHERE rid=fid)"
178181
" ELSE 0"
179182
" END"
180183
" FROM mlink"
181184
" WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)");
@@ -247,11 +250,16 @@
247250
printf("committer");
248251
print_person(zUser);
249252
printf(" %s +0000\n", zSecondsSince1970);
250253
if( zComment==0 ) zComment = "null comment";
251254
printf("data %d\n%s\n", (int)strlen(zComment), zComment);
252
- db_prepare(&q3, "SELECT pid FROM plink WHERE cid=%d AND isprim", ckinId);
255
+ db_prepare(&q3,
256
+ "SELECT pid FROM plink"
257
+ " WHERE cid=%d AND isprim"
258
+ " AND pid IN (SELECT objid FROM event)",
259
+ ckinId
260
+ );
253261
if( db_step(&q3) == SQLITE_ROW ){
254262
printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0)));
255263
db_prepare(&q4,
256264
"SELECT pid FROM plink"
257265
" WHERE cid=%d AND NOT isprim"
258266
--- src/export.c
+++ src/export.c
@@ -166,16 +166,19 @@
166
167 /* Step 1: Generate "blob" records for every artifact that is part
168 ** of a check-in
169 */
170 fossil_binary_mode(stdout);
171 db_multi_exec("CREATE TEMPORARY TABLE newblob(rid INTEGER KEY, srcid INTEGER)");
172 db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)");
173 db_multi_exec(
174 "INSERT INTO newblob"
175 " SELECT DISTINCT fid,"
176 " CASE WHEN EXISTS(SELECT 1 FROM delta WHERE rid=fid AND NOT EXISTS(SELECT 1 FROM oldblob WHERE srcid=fid))"
 
 
 
177 " THEN (SELECT srcid FROM delta WHERE rid=fid)"
178 " ELSE 0"
179 " END"
180 " FROM mlink"
181 " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)");
@@ -247,11 +250,16 @@
247 printf("committer");
248 print_person(zUser);
249 printf(" %s +0000\n", zSecondsSince1970);
250 if( zComment==0 ) zComment = "null comment";
251 printf("data %d\n%s\n", (int)strlen(zComment), zComment);
252 db_prepare(&q3, "SELECT pid FROM plink WHERE cid=%d AND isprim", ckinId);
 
 
 
 
 
253 if( db_step(&q3) == SQLITE_ROW ){
254 printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0)));
255 db_prepare(&q4,
256 "SELECT pid FROM plink"
257 " WHERE cid=%d AND NOT isprim"
258
--- src/export.c
+++ src/export.c
@@ -166,16 +166,19 @@
166
167 /* Step 1: Generate "blob" records for every artifact that is part
168 ** of a check-in
169 */
170 fossil_binary_mode(stdout);
171 db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)");
172 db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)");
173 db_multi_exec(
174 "INSERT INTO newblob"
175 " SELECT DISTINCT fid,"
176 " CASE WHEN EXISTS(SELECT 1 FROM delta"
177 " WHERE rid=fid"
178 " AND NOT EXISTS(SELECT 1 FROM oldblob"
179 " WHERE srcid=fid))"
180 " THEN (SELECT srcid FROM delta WHERE rid=fid)"
181 " ELSE 0"
182 " END"
183 " FROM mlink"
184 " WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)");
@@ -247,11 +250,16 @@
250 printf("committer");
251 print_person(zUser);
252 printf(" %s +0000\n", zSecondsSince1970);
253 if( zComment==0 ) zComment = "null comment";
254 printf("data %d\n%s\n", (int)strlen(zComment), zComment);
255 db_prepare(&q3,
256 "SELECT pid FROM plink"
257 " WHERE cid=%d AND isprim"
258 " AND pid IN (SELECT objid FROM event)",
259 ckinId
260 );
261 if( db_step(&q3) == SQLITE_ROW ){
262 printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0)));
263 db_prepare(&q4,
264 "SELECT pid FROM plink"
265 " WHERE cid=%d AND NOT isprim"
266
+26 -1
--- src/file.c
+++ src/file.c
@@ -425,11 +425,11 @@
425425
char *zDate;
426426
i64 iMTime;
427427
if( g.argc!=4 ){
428428
usage("test-set-mtime FILENAME DATE/TIME");
429429
}
430
- db_open_or_attach(":memory:", "mem");
430
+ db_open_or_attach(":memory:", "mem", 0);
431431
iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
432432
zFile = g.argv[2];
433433
file_set_mtime(zFile, iMTime);
434434
iMTime = file_wd_mtime(zFile);
435435
zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
@@ -495,10 +495,35 @@
495495
if( c=='.' ){
496496
if( z[1]=='/' || z[1]==0 ) return 0;
497497
if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
498498
}
499499
for(i=0; (c=z[i])!=0; i++){
500
+ if( (c & 0xf0) == 0xf0 ) {
501
+ /* Unicode characters > U+FFFF are not supported.
502
+ * Windows XP and earlier cannot handle them.
503
+ */
504
+ return 0;
505
+ }
506
+ if( (c & 0xf0) == 0xe0 ) {
507
+ /* This is a 3-byte UTF-8 character */
508
+ if ( (c & 0xfe) == 0xee ){
509
+ /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
510
+ if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){
511
+ /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4),
512
+ * which contain valid characters. */
513
+ continue;
514
+ }
515
+ /* Unicode character in the range U+E000 - U+F8FF are for
516
+ * private use, they shouldn't occur in filenames. */
517
+ return 0;
518
+ }
519
+ if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
520
+ /* Unicode character in the range U+D800 - U+DFFF are for
521
+ * surrogate pairs, they shouldn't occur in filenames. */
522
+ return 0;
523
+ }
524
+ }
500525
if( c=='\\' || c=='*' || c=='[' || c==']' || c=='?' ){
501526
return 0;
502527
}
503528
if( c=='/' ){
504529
if( z[i+1]=='/' ) return 0;
505530
--- src/file.c
+++ src/file.c
@@ -425,11 +425,11 @@
425 char *zDate;
426 i64 iMTime;
427 if( g.argc!=4 ){
428 usage("test-set-mtime FILENAME DATE/TIME");
429 }
430 db_open_or_attach(":memory:", "mem");
431 iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
432 zFile = g.argv[2];
433 file_set_mtime(zFile, iMTime);
434 iMTime = file_wd_mtime(zFile);
435 zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
@@ -495,10 +495,35 @@
495 if( c=='.' ){
496 if( z[1]=='/' || z[1]==0 ) return 0;
497 if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
498 }
499 for(i=0; (c=z[i])!=0; i++){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500 if( c=='\\' || c=='*' || c=='[' || c==']' || c=='?' ){
501 return 0;
502 }
503 if( c=='/' ){
504 if( z[i+1]=='/' ) return 0;
505
--- src/file.c
+++ src/file.c
@@ -425,11 +425,11 @@
425 char *zDate;
426 i64 iMTime;
427 if( g.argc!=4 ){
428 usage("test-set-mtime FILENAME DATE/TIME");
429 }
430 db_open_or_attach(":memory:", "mem", 0);
431 iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
432 zFile = g.argv[2];
433 file_set_mtime(zFile, iMTime);
434 iMTime = file_wd_mtime(zFile);
435 zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
@@ -495,10 +495,35 @@
495 if( c=='.' ){
496 if( z[1]=='/' || z[1]==0 ) return 0;
497 if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
498 }
499 for(i=0; (c=z[i])!=0; i++){
500 if( (c & 0xf0) == 0xf0 ) {
501 /* Unicode characters > U+FFFF are not supported.
502 * Windows XP and earlier cannot handle them.
503 */
504 return 0;
505 }
506 if( (c & 0xf0) == 0xe0 ) {
507 /* This is a 3-byte UTF-8 character */
508 if ( (c & 0xfe) == 0xee ){
509 /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
510 if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){
511 /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4),
512 * which contain valid characters. */
513 continue;
514 }
515 /* Unicode character in the range U+E000 - U+F8FF are for
516 * private use, they shouldn't occur in filenames. */
517 return 0;
518 }
519 if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
520 /* Unicode character in the range U+D800 - U+DFFF are for
521 * surrogate pairs, they shouldn't occur in filenames. */
522 return 0;
523 }
524 }
525 if( c=='\\' || c=='*' || c=='[' || c==']' || c=='?' ){
526 return 0;
527 }
528 if( c=='/' ){
529 if( z[i+1]=='/' ) return 0;
530
+35 -2
--- src/finfo.c
+++ src/finfo.c
@@ -204,10 +204,12 @@
204204
db_finalize(&q);
205205
blob_reset(&fname);
206206
}
207207
}
208208
209
+/* Values for the debug= query parameter to finfo */
210
+#define FINFO_DEBUG_MLINK 0x01
209211
210212
/*
211213
** WEBPAGE: finfo
212214
** URL: /finfo?name=FILENAME
213215
**
@@ -227,17 +229,19 @@
227229
const char *zFilename;
228230
char zPrevDate[20];
229231
const char *zA;
230232
const char *zB;
231233
int n;
234
+
232235
Blob title;
233236
Blob sql;
234237
HQuery url;
235238
GraphContext *pGraph;
236239
int brBg = P("brbg")!=0;
237240
int uBg = P("ubg")!=0;
238241
int firstChngOnly = atoi(PD("fco","1"))!=0;
242
+ int fDebug = atoi(PD("debug","0"));
239243
240244
login_check_credentials();
241245
if( !g.perm.Read ){ login_needed(); return; }
242246
style_header("File History");
243247
login_anonymous_available();
@@ -260,11 +264,13 @@
260264
" (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
261265
" (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */
262266
" (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */
263267
" event.bgcolor," /* Background color */
264268
" (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
265
- " AND tagxref.rid=mlink.mid)", /* Tags */
269
+ " AND tagxref.rid=mlink.mid)," /* Tags */
270
+ " mlink.mid," /* check-in ID */
271
+ " mlink.pfnid", /* Previous filename */
266272
TAG_BRANCH
267273
);
268274
if( firstChngOnly ){
269275
blob_appendf(&sql, ", min(event.mtime)");
270276
}
@@ -316,10 +322,12 @@
316322
const char *zPUuid = db_column_text(&q, 5);
317323
const char *zUuid = db_column_text(&q, 6);
318324
const char *zCkin = db_column_text(&q,7);
319325
const char *zBgClr = db_column_text(&q, 8);
320326
const char *zBr = db_column_text(&q, 9);
327
+ int fmid = db_column_int(&q, 10);
328
+ int pfnid = db_column_int(&q, 11);
321329
int gidx;
322330
char zTime[10];
323331
char zShort[20];
324332
char zShortCkin[20];
325333
if( zBr==0 ) zBr = "trunk";
@@ -346,13 +354,34 @@
346354
@ <td class="timelineTableCell">
347355
}
348356
sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
349357
sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
350358
if( zUuid ){
359
+ if( fpid==0 ){
360
+ @ <b>Added</b>
361
+ }else if( pfnid ){
362
+ char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d",
363
+ pfnid);
364
+ @ <b>Renamed</b> from
365
+ @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
366
+ }
351367
@ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in
352368
}else{
353
- @ <b>Deleted</b> by check-in
369
+ char *zNewName;
370
+ zNewName = db_text(0,
371
+ "SELECT name FROM filename WHERE fnid = "
372
+ " (SELECT fnid FROM mlink"
373
+ " WHERE mid=%d"
374
+ " AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q %s))",
375
+ fmid, zFilename, filename_collation());
376
+ if( zNewName ){
377
+ @ <b>Renamed</b> to
378
+ @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
379
+ fossil_free(zNewName);
380
+ }else{
381
+ @ <b>Deleted</b> by check-in
382
+ }
354383
}
355384
hyperlink_to_uuid(zShortCkin);
356385
@ %h(zCom) (user:
357386
hyperlink_to_user(zUser, zDate, "");
358387
@ branch: %h(zBr))
@@ -361,10 +390,14 @@
361390
if( fpid ){
362391
@ %z(href("%R/fdiff?v1=%S&v2=%S",zPUuid,zUuid))[diff]</a>
363392
}
364393
@ %z(href("%R/annotate?checkin=%S&filename=%h",zCkin,z))
365394
@ [annotate]</a>
395
+ @ %z(href("%R/timeline?n=200&uf=%S",zUuid))[checkins&nbsp;using]</a>
396
+ }
397
+ if( fDebug & FINFO_DEBUG_MLINK ){
398
+ @ fid=%d(frid), pid=%d(fpid), mid=%d(fmid)
366399
}
367400
@ </td></tr>
368401
}
369402
db_finalize(&q);
370403
if( pGraph ){
371404
--- src/finfo.c
+++ src/finfo.c
@@ -204,10 +204,12 @@
204 db_finalize(&q);
205 blob_reset(&fname);
206 }
207 }
208
 
 
209
210 /*
211 ** WEBPAGE: finfo
212 ** URL: /finfo?name=FILENAME
213 **
@@ -227,17 +229,19 @@
227 const char *zFilename;
228 char zPrevDate[20];
229 const char *zA;
230 const char *zB;
231 int n;
 
232 Blob title;
233 Blob sql;
234 HQuery url;
235 GraphContext *pGraph;
236 int brBg = P("brbg")!=0;
237 int uBg = P("ubg")!=0;
238 int firstChngOnly = atoi(PD("fco","1"))!=0;
 
239
240 login_check_credentials();
241 if( !g.perm.Read ){ login_needed(); return; }
242 style_header("File History");
243 login_anonymous_available();
@@ -260,11 +264,13 @@
260 " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
261 " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */
262 " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */
263 " event.bgcolor," /* Background color */
264 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
265 " AND tagxref.rid=mlink.mid)", /* Tags */
 
 
266 TAG_BRANCH
267 );
268 if( firstChngOnly ){
269 blob_appendf(&sql, ", min(event.mtime)");
270 }
@@ -316,10 +322,12 @@
316 const char *zPUuid = db_column_text(&q, 5);
317 const char *zUuid = db_column_text(&q, 6);
318 const char *zCkin = db_column_text(&q,7);
319 const char *zBgClr = db_column_text(&q, 8);
320 const char *zBr = db_column_text(&q, 9);
 
 
321 int gidx;
322 char zTime[10];
323 char zShort[20];
324 char zShortCkin[20];
325 if( zBr==0 ) zBr = "trunk";
@@ -346,13 +354,34 @@
346 @ <td class="timelineTableCell">
347 }
348 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
349 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
350 if( zUuid ){
 
 
 
 
 
 
 
 
351 @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in
352 }else{
353 @ <b>Deleted</b> by check-in
 
 
 
 
 
 
 
 
 
 
 
 
 
354 }
355 hyperlink_to_uuid(zShortCkin);
356 @ %h(zCom) (user:
357 hyperlink_to_user(zUser, zDate, "");
358 @ branch: %h(zBr))
@@ -361,10 +390,14 @@
361 if( fpid ){
362 @ %z(href("%R/fdiff?v1=%S&v2=%S",zPUuid,zUuid))[diff]</a>
363 }
364 @ %z(href("%R/annotate?checkin=%S&filename=%h",zCkin,z))
365 @ [annotate]</a>
 
 
 
 
366 }
367 @ </td></tr>
368 }
369 db_finalize(&q);
370 if( pGraph ){
371
--- src/finfo.c
+++ src/finfo.c
@@ -204,10 +204,12 @@
204 db_finalize(&q);
205 blob_reset(&fname);
206 }
207 }
208
209 /* Values for the debug= query parameter to finfo */
210 #define FINFO_DEBUG_MLINK 0x01
211
212 /*
213 ** WEBPAGE: finfo
214 ** URL: /finfo?name=FILENAME
215 **
@@ -227,17 +229,19 @@
229 const char *zFilename;
230 char zPrevDate[20];
231 const char *zA;
232 const char *zB;
233 int n;
234
235 Blob title;
236 Blob sql;
237 HQuery url;
238 GraphContext *pGraph;
239 int brBg = P("brbg")!=0;
240 int uBg = P("ubg")!=0;
241 int firstChngOnly = atoi(PD("fco","1"))!=0;
242 int fDebug = atoi(PD("debug","0"));
243
244 login_check_credentials();
245 if( !g.perm.Read ){ login_needed(); return; }
246 style_header("File History");
247 login_anonymous_available();
@@ -260,11 +264,13 @@
264 " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
265 " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */
266 " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */
267 " event.bgcolor," /* Background color */
268 " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
269 " AND tagxref.rid=mlink.mid)," /* Tags */
270 " mlink.mid," /* check-in ID */
271 " mlink.pfnid", /* Previous filename */
272 TAG_BRANCH
273 );
274 if( firstChngOnly ){
275 blob_appendf(&sql, ", min(event.mtime)");
276 }
@@ -316,10 +322,12 @@
322 const char *zPUuid = db_column_text(&q, 5);
323 const char *zUuid = db_column_text(&q, 6);
324 const char *zCkin = db_column_text(&q,7);
325 const char *zBgClr = db_column_text(&q, 8);
326 const char *zBr = db_column_text(&q, 9);
327 int fmid = db_column_int(&q, 10);
328 int pfnid = db_column_int(&q, 11);
329 int gidx;
330 char zTime[10];
331 char zShort[20];
332 char zShortCkin[20];
333 if( zBr==0 ) zBr = "trunk";
@@ -346,13 +354,34 @@
354 @ <td class="timelineTableCell">
355 }
356 sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
357 sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
358 if( zUuid ){
359 if( fpid==0 ){
360 @ <b>Added</b>
361 }else if( pfnid ){
362 char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d",
363 pfnid);
364 @ <b>Renamed</b> from
365 @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a>
366 }
367 @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in
368 }else{
369 char *zNewName;
370 zNewName = db_text(0,
371 "SELECT name FROM filename WHERE fnid = "
372 " (SELECT fnid FROM mlink"
373 " WHERE mid=%d"
374 " AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q %s))",
375 fmid, zFilename, filename_collation());
376 if( zNewName ){
377 @ <b>Renamed</b> to
378 @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
379 fossil_free(zNewName);
380 }else{
381 @ <b>Deleted</b> by check-in
382 }
383 }
384 hyperlink_to_uuid(zShortCkin);
385 @ %h(zCom) (user:
386 hyperlink_to_user(zUser, zDate, "");
387 @ branch: %h(zBr))
@@ -361,10 +390,14 @@
390 if( fpid ){
391 @ %z(href("%R/fdiff?v1=%S&v2=%S",zPUuid,zUuid))[diff]</a>
392 }
393 @ %z(href("%R/annotate?checkin=%S&filename=%h",zCkin,z))
394 @ [annotate]</a>
395 @ %z(href("%R/timeline?n=200&uf=%S",zUuid))[checkins&nbsp;using]</a>
396 }
397 if( fDebug & FINFO_DEBUG_MLINK ){
398 @ fid=%d(frid), pid=%d(fpid), mid=%d(fmid)
399 }
400 @ </td></tr>
401 }
402 db_finalize(&q);
403 if( pGraph ){
404
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,10 +97,84 @@
9797
#ifdef __MINGW32__
9898
static char zDefaultSshCmd[] = "ssh -T";
9999
#else
100100
static char zDefaultSshCmd[] = "ssh -e none -T";
101101
#endif
102
+
103
+/*
104
+** Generate a random SSH link problem keyword
105
+*/
106
+static int random_probe(char *zProbe, int nProbe){
107
+ unsigned r[4];
108
+ sqlite3_randomness(sizeof(r), r);
109
+ sqlite3_snprintf(nProbe, zProbe, "probe-%08x%08x%08x%08x",
110
+ r[0], r[1], r[2], r[3]);
111
+ return (int)strlen(zProbe);
112
+}
113
+
114
+/*
115
+** Bring up an SSH link. This involves sending some "echo" commands and
116
+** get back appropriate responses. The point is to move past the MOTD and
117
+** verify that the link is working.
118
+*/
119
+static void transport_ssh_startup(void){
120
+ char *zIn; /* An input line received back from remote */
121
+ int nWait; /* Number of times waiting for the MOTD */
122
+ char zProbe[40]; /* Text of the random probe */
123
+ int nProbe; /* Size of probe message */
124
+ int nIn; /* Size of input */
125
+ static const int nBuf = 10000; /* Size of input buffer */
126
+
127
+ zIn = fossil_malloc(nBuf);
128
+ nProbe = random_probe(zProbe, sizeof(zProbe));
129
+ fprintf(sshOut, "echo %s\n", zProbe);
130
+ fflush(sshOut);
131
+ if( g.fSshTrace ){
132
+ printf("Sent: [echo %s]\n", zProbe);
133
+ fflush(stdout);
134
+ }
135
+ memset(zIn, '*', nProbe);
136
+ for(nWait=1; nWait<=10; nWait++){
137
+ sshin_read(zIn+nProbe, nBuf-nProbe);
138
+ if( g.fSshTrace ){
139
+ printf("Got back-----------------------------------------------\n"
140
+ "%s\n"
141
+ "-------------------------------------------------------\n",
142
+ zIn+nProbe);
143
+ }
144
+ if( strstr(zIn, zProbe) ) break;
145
+ sqlite3_sleep(100*nWait);
146
+ nIn = (int)strlen(zIn);
147
+ memcpy(zIn, zIn+(nIn-nProbe), nProbe);
148
+ if( g.fSshTrace ){
149
+ printf("Fetching more text. Looking for [%s]...\n", zProbe);
150
+ fflush(stdout);
151
+ }
152
+ }
153
+ nProbe = random_probe(zProbe, sizeof(zProbe));
154
+ fprintf(sshOut, "echo %s\n", zProbe);
155
+ fflush(sshOut);
156
+ if( g.fSshTrace ){
157
+ printf("Sent: [echo %s]\n", zProbe);
158
+ fflush(stdout);
159
+ }
160
+ sshin_read(zIn, nBuf);
161
+ if( zIn[0]==0 ){
162
+ sqlite3_sleep(250);
163
+ sshin_read(zIn, nBuf);
164
+ }
165
+ if( g.fSshTrace ){
166
+ printf("Got back-----------------------------------------------\n"
167
+ "%s\n"
168
+ "-------------------------------------------------------\n", zIn);
169
+ }
170
+ if( memcmp(zIn, zProbe, nProbe)!=0 ){
171
+ pclose2(sshIn, sshOut, sshPid);
172
+ fossil_fatal("ssh connection failed: [%s]", zIn);
173
+ }
174
+ fossil_free(zIn);
175
+}
102176
103177
/*
104178
** Global initialization of the transport layer
105179
*/
106180
void transport_global_startup(void){
@@ -109,13 +183,10 @@
109183
** and run an SSH command to talk to the remote machine.
110184
*/
111185
const char *zSsh; /* The base SSH command */
112186
Blob zCmd; /* The SSH command */
113187
char *zHost; /* The host name to contact */
114
- char *zIn; /* An input line received back from remote */
115
- unsigned iRandom;
116
- char zProbe[30];
117188
118189
zSsh = db_get("ssh-command", zDefaultSshCmd);
119190
blob_init(&zCmd, zSsh, -1);
120191
if( g.urlPort!=g.urlDfltPort ){
121192
#ifdef __MINGW32__
@@ -156,28 +227,11 @@
156227
popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
157228
if( sshPid==0 ){
158229
fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
159230
}
160231
blob_reset(&zCmd);
161
-
162
- /* Send a couple of "echo" command to the other side to make sure that the
163
- ** connection is up and working.
164
- */
165
- fprintf(sshOut, "echo test1\n");
166
- fflush(sshOut);
167
- zIn = fossil_malloc(50000);
168
- sshin_read(zIn, 50000);
169
- sqlite3_randomness(sizeof(iRandom), &iRandom);
170
- sqlite3_snprintf(sizeof(zProbe), zProbe, "probe-%08x", iRandom);
171
- fprintf(sshOut, "echo %s\n", zProbe);
172
- fflush(sshOut);
173
- sshin_read(zIn, 500);
174
- if( memcmp(zIn, zProbe, 14)!=0 ){
175
- pclose2(sshIn, sshOut, sshPid);
176
- fossil_fatal("ssh connection failed: [%s]", zIn);
177
- }
178
- fossil_free(zIn);
232
+ transport_ssh_startup();
179233
}
180234
}
181235
182236
/*
183237
** Open a connection to the server. The server is defined by the following
184238
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,10 +97,84 @@
97 #ifdef __MINGW32__
98 static char zDefaultSshCmd[] = "ssh -T";
99 #else
100 static char zDefaultSshCmd[] = "ssh -e none -T";
101 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
103 /*
104 ** Global initialization of the transport layer
105 */
106 void transport_global_startup(void){
@@ -109,13 +183,10 @@
109 ** and run an SSH command to talk to the remote machine.
110 */
111 const char *zSsh; /* The base SSH command */
112 Blob zCmd; /* The SSH command */
113 char *zHost; /* The host name to contact */
114 char *zIn; /* An input line received back from remote */
115 unsigned iRandom;
116 char zProbe[30];
117
118 zSsh = db_get("ssh-command", zDefaultSshCmd);
119 blob_init(&zCmd, zSsh, -1);
120 if( g.urlPort!=g.urlDfltPort ){
121 #ifdef __MINGW32__
@@ -156,28 +227,11 @@
156 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
157 if( sshPid==0 ){
158 fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
159 }
160 blob_reset(&zCmd);
161
162 /* Send a couple of "echo" command to the other side to make sure that the
163 ** connection is up and working.
164 */
165 fprintf(sshOut, "echo test1\n");
166 fflush(sshOut);
167 zIn = fossil_malloc(50000);
168 sshin_read(zIn, 50000);
169 sqlite3_randomness(sizeof(iRandom), &iRandom);
170 sqlite3_snprintf(sizeof(zProbe), zProbe, "probe-%08x", iRandom);
171 fprintf(sshOut, "echo %s\n", zProbe);
172 fflush(sshOut);
173 sshin_read(zIn, 500);
174 if( memcmp(zIn, zProbe, 14)!=0 ){
175 pclose2(sshIn, sshOut, sshPid);
176 fossil_fatal("ssh connection failed: [%s]", zIn);
177 }
178 fossil_free(zIn);
179 }
180 }
181
182 /*
183 ** Open a connection to the server. The server is defined by the following
184
--- src/http_transport.c
+++ src/http_transport.c
@@ -97,10 +97,84 @@
97 #ifdef __MINGW32__
98 static char zDefaultSshCmd[] = "ssh -T";
99 #else
100 static char zDefaultSshCmd[] = "ssh -e none -T";
101 #endif
102
103 /*
104 ** Generate a random SSH link problem keyword
105 */
106 static int random_probe(char *zProbe, int nProbe){
107 unsigned r[4];
108 sqlite3_randomness(sizeof(r), r);
109 sqlite3_snprintf(nProbe, zProbe, "probe-%08x%08x%08x%08x",
110 r[0], r[1], r[2], r[3]);
111 return (int)strlen(zProbe);
112 }
113
114 /*
115 ** Bring up an SSH link. This involves sending some "echo" commands and
116 ** get back appropriate responses. The point is to move past the MOTD and
117 ** verify that the link is working.
118 */
119 static void transport_ssh_startup(void){
120 char *zIn; /* An input line received back from remote */
121 int nWait; /* Number of times waiting for the MOTD */
122 char zProbe[40]; /* Text of the random probe */
123 int nProbe; /* Size of probe message */
124 int nIn; /* Size of input */
125 static const int nBuf = 10000; /* Size of input buffer */
126
127 zIn = fossil_malloc(nBuf);
128 nProbe = random_probe(zProbe, sizeof(zProbe));
129 fprintf(sshOut, "echo %s\n", zProbe);
130 fflush(sshOut);
131 if( g.fSshTrace ){
132 printf("Sent: [echo %s]\n", zProbe);
133 fflush(stdout);
134 }
135 memset(zIn, '*', nProbe);
136 for(nWait=1; nWait<=10; nWait++){
137 sshin_read(zIn+nProbe, nBuf-nProbe);
138 if( g.fSshTrace ){
139 printf("Got back-----------------------------------------------\n"
140 "%s\n"
141 "-------------------------------------------------------\n",
142 zIn+nProbe);
143 }
144 if( strstr(zIn, zProbe) ) break;
145 sqlite3_sleep(100*nWait);
146 nIn = (int)strlen(zIn);
147 memcpy(zIn, zIn+(nIn-nProbe), nProbe);
148 if( g.fSshTrace ){
149 printf("Fetching more text. Looking for [%s]...\n", zProbe);
150 fflush(stdout);
151 }
152 }
153 nProbe = random_probe(zProbe, sizeof(zProbe));
154 fprintf(sshOut, "echo %s\n", zProbe);
155 fflush(sshOut);
156 if( g.fSshTrace ){
157 printf("Sent: [echo %s]\n", zProbe);
158 fflush(stdout);
159 }
160 sshin_read(zIn, nBuf);
161 if( zIn[0]==0 ){
162 sqlite3_sleep(250);
163 sshin_read(zIn, nBuf);
164 }
165 if( g.fSshTrace ){
166 printf("Got back-----------------------------------------------\n"
167 "%s\n"
168 "-------------------------------------------------------\n", zIn);
169 }
170 if( memcmp(zIn, zProbe, nProbe)!=0 ){
171 pclose2(sshIn, sshOut, sshPid);
172 fossil_fatal("ssh connection failed: [%s]", zIn);
173 }
174 fossil_free(zIn);
175 }
176
177 /*
178 ** Global initialization of the transport layer
179 */
180 void transport_global_startup(void){
@@ -109,13 +183,10 @@
183 ** and run an SSH command to talk to the remote machine.
184 */
185 const char *zSsh; /* The base SSH command */
186 Blob zCmd; /* The SSH command */
187 char *zHost; /* The host name to contact */
 
 
 
188
189 zSsh = db_get("ssh-command", zDefaultSshCmd);
190 blob_init(&zCmd, zSsh, -1);
191 if( g.urlPort!=g.urlDfltPort ){
192 #ifdef __MINGW32__
@@ -156,28 +227,11 @@
227 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
228 if( sshPid==0 ){
229 fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
230 }
231 blob_reset(&zCmd);
232 transport_ssh_startup();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233 }
234 }
235
236 /*
237 ** Open a connection to the server. The server is defined by the following
238
+11 -7
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865865
const char *zDate = db_column_text(&q, 0);
866866
const char *zUser = db_column_text(&q, 1);
867867
const char *zUuid = db_column_text(&q, 3);
868868
const char *zTagList = db_column_text(&q, 4);
869869
Blob comment;
870
- int wikiFlags = WIKI_INLINE;
870
+ int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
871871
if( db_get_boolean("timeline-block-markup", 0)==0 ){
872872
wikiFlags |= WIKI_NOBLOCK;
873873
}
874874
hyperlink_to_uuid(zUuid);
875875
blob_zero(&comment);
@@ -1597,11 +1597,15 @@
15971597
@ <h2>Artifact %s(zUuid):</h2>
15981598
}
15991599
blob_zero(&downloadName);
16001600
objType = object_description(rid, 0, &downloadName);
16011601
style_submenu_element("Download", "Download",
1602
- "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
1602
+ "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid);
1603
+ if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
1604
+ style_submenu_element("Checkins Using", "Checkins Using",
1605
+ "%R/timeline?uf=%s&n=200",zUuid);
1606
+ }
16031607
asText = P("txt")!=0;
16041608
zMime = mimetype_from_name(blob_str(&downloadName));
16051609
if( zMime ){
16061610
if( fossil_strcmp(zMime, "text/html")==0 ){
16071611
if( asText ){
@@ -1630,21 +1634,21 @@
16301634
content_get(rid, &content);
16311635
if( renderAsWiki ){
16321636
wiki_convert(&content, 0, 0);
16331637
}else if( renderAsHtml ){
16341638
@ <div>
1635
- blob_strip_bom(&content, 0);
1639
+ blob_to_utf8_no_bom(&content, 0);
16361640
cgi_append_content(blob_buffer(&content), blob_size(&content));
16371641
@ </div>
16381642
}else{
16391643
style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
16401644
zMime = mimetype_from_content(&content);
16411645
@ <blockquote>
16421646
if( zMime==0 ){
16431647
const char *zLn = P("ln");
16441648
const char *z;
1645
- blob_strip_bom(&content, 0);
1649
+ blob_to_utf8_no_bom(&content, 0);
16461650
z = blob_str(&content);
16471651
if( zLn ){
16481652
output_text_with_line_numbers(z, zLn);
16491653
}else{
16501654
@ <pre>
@@ -2149,11 +2153,11 @@
21492153
if( zNewColor && zNewColor[0] ){
21502154
@ <tr><td style="background-color: %h(zNewColor);">
21512155
}else{
21522156
@ <tr><td>
21532157
}
2154
- wiki_convert(&comment, 0, WIKI_INLINE);
2158
+ @ %w(blob_str(&comment))
21552159
blob_zero(&suffix);
21562160
blob_appendf(&suffix, "(user: %h", zNewUser);
21572161
db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
21582162
" WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
21592163
" AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2179,13 +2183,13 @@
21792183
@ <hr />
21802184
blob_reset(&suffix);
21812185
}
21822186
@ <p>Make changes to attributes of check-in
21832187
@ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2184
- @ <form action="%s(g.zTop)/ci_edit" method="post"><div>
2188
+ form_begin(0, "%R/ci_edit");
21852189
login_insert_csrf_secret();
2186
- @ <input type="hidden" name="r" value="%S(zUuid)" />
2190
+ @ <div><input type="hidden" name="r" value="%S(zUuid)" />
21872191
@ <table border="0" cellspacing="10">
21882192
21892193
@ <tr><td align="right" valign="top"><b>User:</b></td>
21902194
@ <td valign="top">
21912195
@ <input type="text" name="u" size="20" value="%h(zNewUser)" />
21922196
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865 const char *zDate = db_column_text(&q, 0);
866 const char *zUser = db_column_text(&q, 1);
867 const char *zUuid = db_column_text(&q, 3);
868 const char *zTagList = db_column_text(&q, 4);
869 Blob comment;
870 int wikiFlags = WIKI_INLINE;
871 if( db_get_boolean("timeline-block-markup", 0)==0 ){
872 wikiFlags |= WIKI_NOBLOCK;
873 }
874 hyperlink_to_uuid(zUuid);
875 blob_zero(&comment);
@@ -1597,11 +1597,15 @@
1597 @ <h2>Artifact %s(zUuid):</h2>
1598 }
1599 blob_zero(&downloadName);
1600 objType = object_description(rid, 0, &downloadName);
1601 style_submenu_element("Download", "Download",
1602 "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
 
 
 
 
1603 asText = P("txt")!=0;
1604 zMime = mimetype_from_name(blob_str(&downloadName));
1605 if( zMime ){
1606 if( fossil_strcmp(zMime, "text/html")==0 ){
1607 if( asText ){
@@ -1630,21 +1634,21 @@
1630 content_get(rid, &content);
1631 if( renderAsWiki ){
1632 wiki_convert(&content, 0, 0);
1633 }else if( renderAsHtml ){
1634 @ <div>
1635 blob_strip_bom(&content, 0);
1636 cgi_append_content(blob_buffer(&content), blob_size(&content));
1637 @ </div>
1638 }else{
1639 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1640 zMime = mimetype_from_content(&content);
1641 @ <blockquote>
1642 if( zMime==0 ){
1643 const char *zLn = P("ln");
1644 const char *z;
1645 blob_strip_bom(&content, 0);
1646 z = blob_str(&content);
1647 if( zLn ){
1648 output_text_with_line_numbers(z, zLn);
1649 }else{
1650 @ <pre>
@@ -2149,11 +2153,11 @@
2149 if( zNewColor && zNewColor[0] ){
2150 @ <tr><td style="background-color: %h(zNewColor);">
2151 }else{
2152 @ <tr><td>
2153 }
2154 wiki_convert(&comment, 0, WIKI_INLINE);
2155 blob_zero(&suffix);
2156 blob_appendf(&suffix, "(user: %h", zNewUser);
2157 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
2158 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
2159 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2179,13 +2183,13 @@
2179 @ <hr />
2180 blob_reset(&suffix);
2181 }
2182 @ <p>Make changes to attributes of check-in
2183 @ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2184 @ <form action="%s(g.zTop)/ci_edit" method="post"><div>
2185 login_insert_csrf_secret();
2186 @ <input type="hidden" name="r" value="%S(zUuid)" />
2187 @ <table border="0" cellspacing="10">
2188
2189 @ <tr><td align="right" valign="top"><b>User:</b></td>
2190 @ <td valign="top">
2191 @ <input type="text" name="u" size="20" value="%h(zNewUser)" />
2192
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865 const char *zDate = db_column_text(&q, 0);
866 const char *zUser = db_column_text(&q, 1);
867 const char *zUuid = db_column_text(&q, 3);
868 const char *zTagList = db_column_text(&q, 4);
869 Blob comment;
870 int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
871 if( db_get_boolean("timeline-block-markup", 0)==0 ){
872 wikiFlags |= WIKI_NOBLOCK;
873 }
874 hyperlink_to_uuid(zUuid);
875 blob_zero(&comment);
@@ -1597,11 +1597,15 @@
1597 @ <h2>Artifact %s(zUuid):</h2>
1598 }
1599 blob_zero(&downloadName);
1600 objType = object_description(rid, 0, &downloadName);
1601 style_submenu_element("Download", "Download",
1602 "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid);
1603 if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
1604 style_submenu_element("Checkins Using", "Checkins Using",
1605 "%R/timeline?uf=%s&n=200",zUuid);
1606 }
1607 asText = P("txt")!=0;
1608 zMime = mimetype_from_name(blob_str(&downloadName));
1609 if( zMime ){
1610 if( fossil_strcmp(zMime, "text/html")==0 ){
1611 if( asText ){
@@ -1630,21 +1634,21 @@
1634 content_get(rid, &content);
1635 if( renderAsWiki ){
1636 wiki_convert(&content, 0, 0);
1637 }else if( renderAsHtml ){
1638 @ <div>
1639 blob_to_utf8_no_bom(&content, 0);
1640 cgi_append_content(blob_buffer(&content), blob_size(&content));
1641 @ </div>
1642 }else{
1643 style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
1644 zMime = mimetype_from_content(&content);
1645 @ <blockquote>
1646 if( zMime==0 ){
1647 const char *zLn = P("ln");
1648 const char *z;
1649 blob_to_utf8_no_bom(&content, 0);
1650 z = blob_str(&content);
1651 if( zLn ){
1652 output_text_with_line_numbers(z, zLn);
1653 }else{
1654 @ <pre>
@@ -2149,11 +2153,11 @@
2153 if( zNewColor && zNewColor[0] ){
2154 @ <tr><td style="background-color: %h(zNewColor);">
2155 }else{
2156 @ <tr><td>
2157 }
2158 @ %w(blob_str(&comment))
2159 blob_zero(&suffix);
2160 blob_appendf(&suffix, "(user: %h", zNewUser);
2161 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
2162 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
2163 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2179,13 +2183,13 @@
2183 @ <hr />
2184 blob_reset(&suffix);
2185 }
2186 @ <p>Make changes to attributes of check-in
2187 @ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2188 form_begin(0, "%R/ci_edit");
2189 login_insert_csrf_secret();
2190 @ <div><input type="hidden" name="r" value="%S(zUuid)" />
2191 @ <table border="0" cellspacing="10">
2192
2193 @ <tr><td align="right" valign="top"><b>User:</b></td>
2194 @ <td valign="top">
2195 @ <input type="text" name="u" size="20" value="%h(zNewUser)" />
2196
+1
--- src/json.c
+++ src/json.c
@@ -1409,10 +1409,11 @@
14091409
INT(g, isHome);
14101410
INT(g, nAux);
14111411
INT(g, allowSymlinks);
14121412
14131413
CSTR(g, zMainDbType);
1414
+ CSTR(g, zConfigDbType);
14141415
CSTR(g, zHome);
14151416
CSTR(g, zLocalRoot);
14161417
CSTR(g, zPath);
14171418
CSTR(g, zExtra);
14181419
CSTR(g, zBaseURL);
14191420
--- src/json.c
+++ src/json.c
@@ -1409,10 +1409,11 @@
1409 INT(g, isHome);
1410 INT(g, nAux);
1411 INT(g, allowSymlinks);
1412
1413 CSTR(g, zMainDbType);
 
1414 CSTR(g, zHome);
1415 CSTR(g, zLocalRoot);
1416 CSTR(g, zPath);
1417 CSTR(g, zExtra);
1418 CSTR(g, zBaseURL);
1419
--- src/json.c
+++ src/json.c
@@ -1409,10 +1409,11 @@
1409 INT(g, isHome);
1410 INT(g, nAux);
1411 INT(g, allowSymlinks);
1412
1413 CSTR(g, zMainDbType);
1414 CSTR(g, zConfigDbType);
1415 CSTR(g, zHome);
1416 CSTR(g, zLocalRoot);
1417 CSTR(g, zPath);
1418 CSTR(g, zExtra);
1419 CSTR(g, zBaseURL);
1420
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305305
/* Commit */
306306
db_end_transaction(0);
307307
308308
#if 0 /* Do an autosync push, if requested */
309309
/* arugable for JSON mode? */
310
- if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
310
+ if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
311311
#endif
312312
return 0;
313313
}
314314
315315
316316
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305 /* Commit */
306 db_end_transaction(0);
307
308 #if 0 /* Do an autosync push, if requested */
309 /* arugable for JSON mode? */
310 if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
311 #endif
312 return 0;
313 }
314
315
316
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305 /* Commit */
306 db_end_transaction(0);
307
308 #if 0 /* Do an autosync push, if requested */
309 /* arugable for JSON mode? */
310 if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
311 #endif
312 return 0;
313 }
314
315
316
+6 -5
--- src/login.c
+++ src/login.c
@@ -404,10 +404,11 @@
404404
return 0;
405405
}
406406
if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407407
if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
408408
if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
409
+ if( memcmp(zAgent, "NetSurf/", 8)==0 ) return 1;
409410
return 0;
410411
}
411412
412413
/*
413414
** COMMAND: test-ishuman
@@ -565,11 +566,11 @@
565566
style_header("Login/Logout");
566567
@ %s(zErrMsg)
567568
if( zGoto && P("anon")==0 ){
568569
@ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
569570
}
570
- @ <form action="login" method="post">
571
+ form_begin(0, "%R/login");
571572
if( zGoto ){
572573
@ <input type="hidden" name="g" value="%h(zGoto)" />
573574
}
574575
@ <table class="login_out">
575576
@ <tr>
@@ -631,11 +632,11 @@
631632
632633
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
633634
@ Visitors may enter <b>anonymous</b> as the user-ID with
634635
@ the 8-character hexadecimal password shown below:</p>
635636
@ <div class="captcha"><table class="captcha"><tr><td><pre>
636
- @ %s(zCaptcha)
637
+ @ %h(zCaptcha)
637638
@ </pre></td></tr></table>
638639
if( bAutoCaptcha ) {
639640
@ <input type="button" value="Fill out captcha"
640641
@ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
641642
}
@@ -652,11 +653,11 @@
652653
if( g.perm.Password ){
653654
@ <hr />
654655
@ <p>To change your password, enter your old password and your
655656
@ new password twice below then press the "Change Password"
656657
@ button.</p>
657
- @ <form action="login" method="post">
658
+ form_begin(0, "%R/login");
658659
@ <table>
659660
@ <tr><td class="login_out_label">Old Password:</td>
660661
@ <td><input type="password" name="p" size="30" /></td></tr>
661662
@ <tr><td class="login_out_label">New Password:</td>
662663
@ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1261,11 @@
12601261
uSeed = captcha_seed();
12611262
zDecoded = captcha_decode(uSeed);
12621263
zCaptcha = captcha_render(zDecoded);
12631264
12641265
/* Print out the registration form. */
1265
- @ <form action="register" method="post">
1266
+ form_begin(0, "%R/register");
12661267
if( P("g") ){
12671268
@ <input type="hidden" name="g" value="%h(P("g"))" />
12681269
}
12691270
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
12701271
@ <table class="login_out">
@@ -1290,11 +1291,11 @@
12901291
@ </tr>
12911292
@ <tr><td></td>
12921293
@ <td><input type="submit" name="new" value="Register" /></td></tr>
12931294
@ </table>
12941295
@ <div class="captcha"><table class="captcha"><tr><td><pre>
1295
- @ %s(zCaptcha)
1296
+ @ %h(zCaptcha)
12961297
@ </pre></td></tr></table>
12971298
@ </form>
12981299
style_footer();
12991300
13001301
free(zCaptcha);
13011302
--- src/login.c
+++ src/login.c
@@ -404,10 +404,11 @@
404 return 0;
405 }
406 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
408 if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
 
409 return 0;
410 }
411
412 /*
413 ** COMMAND: test-ishuman
@@ -565,11 +566,11 @@
565 style_header("Login/Logout");
566 @ %s(zErrMsg)
567 if( zGoto && P("anon")==0 ){
568 @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
569 }
570 @ <form action="login" method="post">
571 if( zGoto ){
572 @ <input type="hidden" name="g" value="%h(zGoto)" />
573 }
574 @ <table class="login_out">
575 @ <tr>
@@ -631,11 +632,11 @@
631
632 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
633 @ Visitors may enter <b>anonymous</b> as the user-ID with
634 @ the 8-character hexadecimal password shown below:</p>
635 @ <div class="captcha"><table class="captcha"><tr><td><pre>
636 @ %s(zCaptcha)
637 @ </pre></td></tr></table>
638 if( bAutoCaptcha ) {
639 @ <input type="button" value="Fill out captcha"
640 @ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
641 }
@@ -652,11 +653,11 @@
652 if( g.perm.Password ){
653 @ <hr />
654 @ <p>To change your password, enter your old password and your
655 @ new password twice below then press the "Change Password"
656 @ button.</p>
657 @ <form action="login" method="post">
658 @ <table>
659 @ <tr><td class="login_out_label">Old Password:</td>
660 @ <td><input type="password" name="p" size="30" /></td></tr>
661 @ <tr><td class="login_out_label">New Password:</td>
662 @ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1261,11 @@
1260 uSeed = captcha_seed();
1261 zDecoded = captcha_decode(uSeed);
1262 zCaptcha = captcha_render(zDecoded);
1263
1264 /* Print out the registration form. */
1265 @ <form action="register" method="post">
1266 if( P("g") ){
1267 @ <input type="hidden" name="g" value="%h(P("g"))" />
1268 }
1269 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
1270 @ <table class="login_out">
@@ -1290,11 +1291,11 @@
1290 @ </tr>
1291 @ <tr><td></td>
1292 @ <td><input type="submit" name="new" value="Register" /></td></tr>
1293 @ </table>
1294 @ <div class="captcha"><table class="captcha"><tr><td><pre>
1295 @ %s(zCaptcha)
1296 @ </pre></td></tr></table>
1297 @ </form>
1298 style_footer();
1299
1300 free(zCaptcha);
1301
--- src/login.c
+++ src/login.c
@@ -404,10 +404,11 @@
404 return 0;
405 }
406 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
408 if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
409 if( memcmp(zAgent, "NetSurf/", 8)==0 ) return 1;
410 return 0;
411 }
412
413 /*
414 ** COMMAND: test-ishuman
@@ -565,11 +566,11 @@
566 style_header("Login/Logout");
567 @ %s(zErrMsg)
568 if( zGoto && P("anon")==0 ){
569 @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
570 }
571 form_begin(0, "%R/login");
572 if( zGoto ){
573 @ <input type="hidden" name="g" value="%h(zGoto)" />
574 }
575 @ <table class="login_out">
576 @ <tr>
@@ -631,11 +632,11 @@
632
633 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
634 @ Visitors may enter <b>anonymous</b> as the user-ID with
635 @ the 8-character hexadecimal password shown below:</p>
636 @ <div class="captcha"><table class="captcha"><tr><td><pre>
637 @ %h(zCaptcha)
638 @ </pre></td></tr></table>
639 if( bAutoCaptcha ) {
640 @ <input type="button" value="Fill out captcha"
641 @ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
642 }
@@ -652,11 +653,11 @@
653 if( g.perm.Password ){
654 @ <hr />
655 @ <p>To change your password, enter your old password and your
656 @ new password twice below then press the "Change Password"
657 @ button.</p>
658 form_begin(0, "%R/login");
659 @ <table>
660 @ <tr><td class="login_out_label">Old Password:</td>
661 @ <td><input type="password" name="p" size="30" /></td></tr>
662 @ <tr><td class="login_out_label">New Password:</td>
663 @ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1261,11 @@
1261 uSeed = captcha_seed();
1262 zDecoded = captcha_decode(uSeed);
1263 zCaptcha = captcha_render(zDecoded);
1264
1265 /* Print out the registration form. */
1266 form_begin(0, "%R/register");
1267 if( P("g") ){
1268 @ <input type="hidden" name="g" value="%h(P("g"))" />
1269 }
1270 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
1271 @ <table class="login_out">
@@ -1290,11 +1291,11 @@
1291 @ </tr>
1292 @ <tr><td></td>
1293 @ <td><input type="submit" name="new" value="Register" /></td></tr>
1294 @ </table>
1295 @ <div class="captcha"><table class="captcha"><tr><td><pre>
1296 @ %h(zCaptcha)
1297 @ </pre></td></tr></table>
1298 @ </form>
1299 style_footer();
1300
1301 free(zCaptcha);
1302
+32 -11
--- src/main.c
+++ src/main.c
@@ -119,10 +119,11 @@
119119
int configOpen; /* True if the config database is open */
120120
sqlite3_int64 now; /* Seconds since 1970 */
121121
int repositoryOpen; /* True if the main repository database is open */
122122
char *zRepositoryName; /* Name of the repository database */
123123
const char *zMainDbType;/* "configdb", "localdb", or "repository" */
124
+ const char *zConfigDbType; /* "configdb", "localdb", or "repository" */
124125
const char *zHome; /* Name of user home directory */
125126
int localOpen; /* True if the local database is open */
126127
char *zLocalRoot; /* The directory holding the local database */
127128
int minPrefix; /* Number of digits needed for a distinct UUID */
128129
int fSqlTrace; /* True if --sqltrace flag is present */
@@ -129,11 +130,12 @@
129130
int fSqlStats; /* True if --sqltrace or --sqlstats are present */
130131
int fSqlPrint; /* True if -sqlprint flag is present */
131132
int fQuiet; /* True if -quiet flag is present */
132133
int fHttpTrace; /* Trace outbound HTTP requests */
133134
int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
134
- int fNoSync; /* Do not do an autosync even. --nosync */
135
+ int fSshTrace; /* Trace the SSH setup traffic */
136
+ int fNoSync; /* Do not do an autosync ever. --nosync */
135137
char *zPath; /* Name of webpage being served */
136138
char *zExtra; /* Extra path information past the webpage name */
137139
char *zBaseURL; /* Full text of the URL being served */
138140
char *zTop; /* Parent directory of zPath */
139141
const char *zContentType; /* The content type of the input HTTP request */
@@ -143,17 +145,19 @@
143145
Blob cgiIn; /* Input to an xfer www method */
144146
int cgiOutput; /* Write error and status messages to CGI */
145147
int xferPanic; /* Write error messages in XFER protocol */
146148
int fullHttpReply; /* True for full HTTP reply. False for CGI reply */
147149
Th_Interp *interp; /* The TH1 interpreter */
150
+ char *th1Setup; /* The TH1 post-creation setup script, if any */
148151
FILE *httpIn; /* Accept HTTP input from here */
149152
FILE *httpOut; /* Send HTTP output here */
150153
int xlinkClusterOnly; /* Set when cloning. Only process clusters */
151154
int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
152155
int *aCommitFile; /* Array of files to be committed */
153156
int markPrivate; /* All new artifacts are private if true */
154157
int clockSkewSeen; /* True if clocks on client and server out of sync */
158
+ int wikiFlags; /* Wiki conversion flags applied to %w and %W */
155159
char isHTTP; /* True if server/CGI modes, else assume CLI. */
156160
char javascriptHyperlink; /* If true, set href= using script, not HTML */
157161
158162
int urlIsFile; /* True if a "file:" url */
159163
int urlIsHttps; /* True if a "https:" url */
@@ -515,11 +519,11 @@
515519
if(stdin != zInFile){
516520
fclose(zInFile);
517521
}
518522
zInFile = NULL;
519523
}
520
- blob_strip_bom(&file, 1);
524
+ blob_to_utf8_no_bom(&file, 1);
521525
z = blob_str(&file);
522526
for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
523527
newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) );
524528
for(j=0; j<i; j++) newArgv[j] = g.argv[j];
525529
@@ -611,10 +615,11 @@
611615
g.isHTTP = 0;
612616
g.fQuiet = find_option("quiet", 0, 0)!=0;
613617
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
614618
g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
615619
g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
620
+ g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
616621
if( g.fSqlTrace ) g.fSqlStats = 1;
617622
g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
618623
g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
619624
g.zLogin = find_option("user", "U", 1);
620625
g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1379,27 +1384,39 @@
13791384
while( 1 ){
13801385
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
13811386
zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
13821387
13831388
/* To avoid mischief, make sure the repository basename contains no
1384
- ** characters other than alphanumerics, "-", "/", and "_".
1389
+ ** characters other than alphanumerics, "-", "/", "_", and "." beside
1390
+ ** "/" or ".".
13851391
*/
13861392
for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){
1387
- if( !fossil_isalnum(zRepo[j]) && zRepo[j]!='-' && zRepo[j]!='/' ){
1393
+ char c = zRepo[j];
1394
+ if( !fossil_isalnum(c) && c!='-' && c!='/'
1395
+ && (c!='.' || zRepo[j+1]=='/' || zRepo[j-1]=='/' || zRepo[j+1]=='.')
1396
+ ){
13881397
zRepo[j] = '_';
13891398
}
13901399
}
13911400
if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; }
13921401
13931402
szFile = file_size(zRepo);
1394
- if( zPathInfo[i]=='/' && szFile<0 ){
1403
+ if( szFile<0 ){
13951404
assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
13961405
zRepo[j] = 0;
1397
- if( file_isdir(zRepo)==1 ){
1406
+ if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){
13981407
fossil_free(zToFree);
13991408
i++;
14001409
continue;
1410
+ }
1411
+ if( file_isfile(zRepo) ){
1412
+ Blob content;
1413
+ blob_read_from_file(&content, zRepo);
1414
+ cgi_set_content_type(mimetype_from_name(zRepo));
1415
+ cgi_set_content(&content);
1416
+ cgi_reply();
1417
+ return;
14011418
}
14021419
zRepo[j] = '.';
14031420
}
14041421
14051422
if( szFile<1024 ){
@@ -1740,13 +1757,17 @@
17401757
** is disallowed.
17411758
*/
17421759
static void find_server_repository(int disallowDir){
17431760
if( g.argc<3 ){
17441761
db_must_be_within_tree();
1745
- }else if( !disallowDir && file_isdir(g.argv[2])==1 ){
1746
- g.zRepositoryName = mprintf("%s", g.argv[2]);
1747
- file_simplify_name(g.zRepositoryName, -1, 0);
1762
+ }else if( file_isdir(g.argv[2])==1 ){
1763
+ if( disallowDir ){
1764
+ fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[2]);
1765
+ }else{
1766
+ g.zRepositoryName = mprintf("%s", g.argv[2]);
1767
+ file_simplify_name(g.zRepositoryName, -1, 0);
1768
+ }
17481769
}else{
17491770
db_open_repository(g.argv[2]);
17501771
}
17511772
}
17521773
@@ -1934,11 +1955,11 @@
19341955
isUiCmd = g.argv[1][0]=='u';
19351956
if( isUiCmd ){
19361957
flags |= HTTP_SERVER_LOCALHOST;
19371958
g.useLocalauth = 1;
19381959
}
1939
- find_server_repository(isUiCmd);
1960
+ find_server_repository(isUiCmd && zNotFound==0);
19401961
if( zPort ){
19411962
iPort = mxPort = atoi(zPort);
19421963
}else{
19431964
iPort = db_get_int("http-port", 8080);
19441965
mxPort = iPort+100;
@@ -1973,11 +1994,11 @@
19731994
g.httpOut = stdout;
19741995
if( g.fHttpTrace || g.fSqlTrace ){
19751996
fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
19761997
}
19771998
g.cgiOutput = 1;
1978
- find_server_repository(isUiCmd);
1999
+ find_server_repository(isUiCmd && zNotFound==0);
19792000
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
19802001
cgi_handle_http_request(0);
19812002
process_one_web_page(zNotFound);
19822003
#else
19832004
/* Win32 implementation */
19842005
--- src/main.c
+++ src/main.c
@@ -119,10 +119,11 @@
119 int configOpen; /* True if the config database is open */
120 sqlite3_int64 now; /* Seconds since 1970 */
121 int repositoryOpen; /* True if the main repository database is open */
122 char *zRepositoryName; /* Name of the repository database */
123 const char *zMainDbType;/* "configdb", "localdb", or "repository" */
 
124 const char *zHome; /* Name of user home directory */
125 int localOpen; /* True if the local database is open */
126 char *zLocalRoot; /* The directory holding the local database */
127 int minPrefix; /* Number of digits needed for a distinct UUID */
128 int fSqlTrace; /* True if --sqltrace flag is present */
@@ -129,11 +130,12 @@
129 int fSqlStats; /* True if --sqltrace or --sqlstats are present */
130 int fSqlPrint; /* True if -sqlprint flag is present */
131 int fQuiet; /* True if -quiet flag is present */
132 int fHttpTrace; /* Trace outbound HTTP requests */
133 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
134 int fNoSync; /* Do not do an autosync even. --nosync */
 
135 char *zPath; /* Name of webpage being served */
136 char *zExtra; /* Extra path information past the webpage name */
137 char *zBaseURL; /* Full text of the URL being served */
138 char *zTop; /* Parent directory of zPath */
139 const char *zContentType; /* The content type of the input HTTP request */
@@ -143,17 +145,19 @@
143 Blob cgiIn; /* Input to an xfer www method */
144 int cgiOutput; /* Write error and status messages to CGI */
145 int xferPanic; /* Write error messages in XFER protocol */
146 int fullHttpReply; /* True for full HTTP reply. False for CGI reply */
147 Th_Interp *interp; /* The TH1 interpreter */
 
148 FILE *httpIn; /* Accept HTTP input from here */
149 FILE *httpOut; /* Send HTTP output here */
150 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
151 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
152 int *aCommitFile; /* Array of files to be committed */
153 int markPrivate; /* All new artifacts are private if true */
154 int clockSkewSeen; /* True if clocks on client and server out of sync */
 
155 char isHTTP; /* True if server/CGI modes, else assume CLI. */
156 char javascriptHyperlink; /* If true, set href= using script, not HTML */
157
158 int urlIsFile; /* True if a "file:" url */
159 int urlIsHttps; /* True if a "https:" url */
@@ -515,11 +519,11 @@
515 if(stdin != zInFile){
516 fclose(zInFile);
517 }
518 zInFile = NULL;
519 }
520 blob_strip_bom(&file, 1);
521 z = blob_str(&file);
522 for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
523 newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) );
524 for(j=0; j<i; j++) newArgv[j] = g.argv[j];
525
@@ -611,10 +615,11 @@
611 g.isHTTP = 0;
612 g.fQuiet = find_option("quiet", 0, 0)!=0;
613 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
614 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
615 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
 
616 if( g.fSqlTrace ) g.fSqlStats = 1;
617 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
618 g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
619 g.zLogin = find_option("user", "U", 1);
620 g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1379,27 +1384,39 @@
1379 while( 1 ){
1380 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1381 zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
1382
1383 /* To avoid mischief, make sure the repository basename contains no
1384 ** characters other than alphanumerics, "-", "/", and "_".
 
1385 */
1386 for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){
1387 if( !fossil_isalnum(zRepo[j]) && zRepo[j]!='-' && zRepo[j]!='/' ){
 
 
 
1388 zRepo[j] = '_';
1389 }
1390 }
1391 if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; }
1392
1393 szFile = file_size(zRepo);
1394 if( zPathInfo[i]=='/' && szFile<0 ){
1395 assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
1396 zRepo[j] = 0;
1397 if( file_isdir(zRepo)==1 ){
1398 fossil_free(zToFree);
1399 i++;
1400 continue;
 
 
 
 
 
 
 
 
1401 }
1402 zRepo[j] = '.';
1403 }
1404
1405 if( szFile<1024 ){
@@ -1740,13 +1757,17 @@
1740 ** is disallowed.
1741 */
1742 static void find_server_repository(int disallowDir){
1743 if( g.argc<3 ){
1744 db_must_be_within_tree();
1745 }else if( !disallowDir && file_isdir(g.argv[2])==1 ){
1746 g.zRepositoryName = mprintf("%s", g.argv[2]);
1747 file_simplify_name(g.zRepositoryName, -1, 0);
 
 
 
 
1748 }else{
1749 db_open_repository(g.argv[2]);
1750 }
1751 }
1752
@@ -1934,11 +1955,11 @@
1934 isUiCmd = g.argv[1][0]=='u';
1935 if( isUiCmd ){
1936 flags |= HTTP_SERVER_LOCALHOST;
1937 g.useLocalauth = 1;
1938 }
1939 find_server_repository(isUiCmd);
1940 if( zPort ){
1941 iPort = mxPort = atoi(zPort);
1942 }else{
1943 iPort = db_get_int("http-port", 8080);
1944 mxPort = iPort+100;
@@ -1973,11 +1994,11 @@
1973 g.httpOut = stdout;
1974 if( g.fHttpTrace || g.fSqlTrace ){
1975 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
1976 }
1977 g.cgiOutput = 1;
1978 find_server_repository(isUiCmd);
1979 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
1980 cgi_handle_http_request(0);
1981 process_one_web_page(zNotFound);
1982 #else
1983 /* Win32 implementation */
1984
--- src/main.c
+++ src/main.c
@@ -119,10 +119,11 @@
119 int configOpen; /* True if the config database is open */
120 sqlite3_int64 now; /* Seconds since 1970 */
121 int repositoryOpen; /* True if the main repository database is open */
122 char *zRepositoryName; /* Name of the repository database */
123 const char *zMainDbType;/* "configdb", "localdb", or "repository" */
124 const char *zConfigDbType; /* "configdb", "localdb", or "repository" */
125 const char *zHome; /* Name of user home directory */
126 int localOpen; /* True if the local database is open */
127 char *zLocalRoot; /* The directory holding the local database */
128 int minPrefix; /* Number of digits needed for a distinct UUID */
129 int fSqlTrace; /* True if --sqltrace flag is present */
@@ -129,11 +130,12 @@
130 int fSqlStats; /* True if --sqltrace or --sqlstats are present */
131 int fSqlPrint; /* True if -sqlprint flag is present */
132 int fQuiet; /* True if -quiet flag is present */
133 int fHttpTrace; /* Trace outbound HTTP requests */
134 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
135 int fSshTrace; /* Trace the SSH setup traffic */
136 int fNoSync; /* Do not do an autosync ever. --nosync */
137 char *zPath; /* Name of webpage being served */
138 char *zExtra; /* Extra path information past the webpage name */
139 char *zBaseURL; /* Full text of the URL being served */
140 char *zTop; /* Parent directory of zPath */
141 const char *zContentType; /* The content type of the input HTTP request */
@@ -143,17 +145,19 @@
145 Blob cgiIn; /* Input to an xfer www method */
146 int cgiOutput; /* Write error and status messages to CGI */
147 int xferPanic; /* Write error messages in XFER protocol */
148 int fullHttpReply; /* True for full HTTP reply. False for CGI reply */
149 Th_Interp *interp; /* The TH1 interpreter */
150 char *th1Setup; /* The TH1 post-creation setup script, if any */
151 FILE *httpIn; /* Accept HTTP input from here */
152 FILE *httpOut; /* Send HTTP output here */
153 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
154 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
155 int *aCommitFile; /* Array of files to be committed */
156 int markPrivate; /* All new artifacts are private if true */
157 int clockSkewSeen; /* True if clocks on client and server out of sync */
158 int wikiFlags; /* Wiki conversion flags applied to %w and %W */
159 char isHTTP; /* True if server/CGI modes, else assume CLI. */
160 char javascriptHyperlink; /* If true, set href= using script, not HTML */
161
162 int urlIsFile; /* True if a "file:" url */
163 int urlIsHttps; /* True if a "https:" url */
@@ -515,11 +519,11 @@
519 if(stdin != zInFile){
520 fclose(zInFile);
521 }
522 zInFile = NULL;
523 }
524 blob_to_utf8_no_bom(&file, 1);
525 z = blob_str(&file);
526 for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
527 newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) );
528 for(j=0; j<i; j++) newArgv[j] = g.argv[j];
529
@@ -611,10 +615,11 @@
615 g.isHTTP = 0;
616 g.fQuiet = find_option("quiet", 0, 0)!=0;
617 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
618 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
619 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
620 g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
621 if( g.fSqlTrace ) g.fSqlStats = 1;
622 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
623 g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
624 g.zLogin = find_option("user", "U", 1);
625 g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1379,27 +1384,39 @@
1384 while( 1 ){
1385 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1386 zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo);
1387
1388 /* To avoid mischief, make sure the repository basename contains no
1389 ** characters other than alphanumerics, "-", "/", "_", and "." beside
1390 ** "/" or ".".
1391 */
1392 for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){
1393 char c = zRepo[j];
1394 if( !fossil_isalnum(c) && c!='-' && c!='/'
1395 && (c!='.' || zRepo[j+1]=='/' || zRepo[j-1]=='/' || zRepo[j+1]=='.')
1396 ){
1397 zRepo[j] = '_';
1398 }
1399 }
1400 if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; }
1401
1402 szFile = file_size(zRepo);
1403 if( szFile<0 ){
1404 assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
1405 zRepo[j] = 0;
1406 if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){
1407 fossil_free(zToFree);
1408 i++;
1409 continue;
1410 }
1411 if( file_isfile(zRepo) ){
1412 Blob content;
1413 blob_read_from_file(&content, zRepo);
1414 cgi_set_content_type(mimetype_from_name(zRepo));
1415 cgi_set_content(&content);
1416 cgi_reply();
1417 return;
1418 }
1419 zRepo[j] = '.';
1420 }
1421
1422 if( szFile<1024 ){
@@ -1740,13 +1757,17 @@
1757 ** is disallowed.
1758 */
1759 static void find_server_repository(int disallowDir){
1760 if( g.argc<3 ){
1761 db_must_be_within_tree();
1762 }else if( file_isdir(g.argv[2])==1 ){
1763 if( disallowDir ){
1764 fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[2]);
1765 }else{
1766 g.zRepositoryName = mprintf("%s", g.argv[2]);
1767 file_simplify_name(g.zRepositoryName, -1, 0);
1768 }
1769 }else{
1770 db_open_repository(g.argv[2]);
1771 }
1772 }
1773
@@ -1934,11 +1955,11 @@
1955 isUiCmd = g.argv[1][0]=='u';
1956 if( isUiCmd ){
1957 flags |= HTTP_SERVER_LOCALHOST;
1958 g.useLocalauth = 1;
1959 }
1960 find_server_repository(isUiCmd && zNotFound==0);
1961 if( zPort ){
1962 iPort = mxPort = atoi(zPort);
1963 }else{
1964 iPort = db_get_int("http-port", 8080);
1965 mxPort = iPort+100;
@@ -1973,11 +1994,11 @@
1994 g.httpOut = stdout;
1995 if( g.fHttpTrace || g.fSqlTrace ){
1996 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
1997 }
1998 g.cgiOutput = 1;
1999 find_server_repository(isUiCmd && zNotFound==0);
2000 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
2001 cgi_handle_http_request(0);
2002 process_one_web_page(zNotFound);
2003 #else
2004 /* Win32 implementation */
2005
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -364,10 +364,14 @@
364364
# will run on the platform that is doing the build. This is used
365365
# to compile code-generator programs as part of the build process.
366366
# See TCC below for the C compiler for building the finished binary.
367367
#
368368
BCC = gcc
369
+
370
+#### Enable compiling with debug symbols (much larger binary)
371
+#
372
+# FOSSIL_ENABLE_SYMBOLS = 1
369373
370374
#### Enable JSON (http://www.json.org) support using "cson"
371375
#
372376
# FOSSIL_ENABLE_JSON = 1
373377
@@ -442,10 +446,17 @@
442446
# as BCC, unless you are cross-compiling. This C compiler builds
443447
# the finished binary for fossil. The BCC compiler above is used
444448
# for building intermediate code-generator tools.
445449
#
446450
TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
451
+
452
+#### Add the necessary command line options to build with debugging
453
+# symbols, if enabled.
454
+#
455
+ifdef FOSSIL_ENABLE_SYMBOLS
456
+TCC += -g
457
+endif
447458
448459
#### Compile resources for use in building executables that will run
449460
# on the target platform.
450461
#
451462
RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
452463
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -364,10 +364,14 @@
364 # will run on the platform that is doing the build. This is used
365 # to compile code-generator programs as part of the build process.
366 # See TCC below for the C compiler for building the finished binary.
367 #
368 BCC = gcc
 
 
 
 
369
370 #### Enable JSON (http://www.json.org) support using "cson"
371 #
372 # FOSSIL_ENABLE_JSON = 1
373
@@ -442,10 +446,17 @@
442 # as BCC, unless you are cross-compiling. This C compiler builds
443 # the finished binary for fossil. The BCC compiler above is used
444 # for building intermediate code-generator tools.
445 #
446 TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
 
 
 
 
 
 
 
447
448 #### Compile resources for use in building executables that will run
449 # on the target platform.
450 #
451 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
452
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -364,10 +364,14 @@
364 # will run on the platform that is doing the build. This is used
365 # to compile code-generator programs as part of the build process.
366 # See TCC below for the C compiler for building the finished binary.
367 #
368 BCC = gcc
369
370 #### Enable compiling with debug symbols (much larger binary)
371 #
372 # FOSSIL_ENABLE_SYMBOLS = 1
373
374 #### Enable JSON (http://www.json.org) support using "cson"
375 #
376 # FOSSIL_ENABLE_JSON = 1
377
@@ -442,10 +446,17 @@
446 # as BCC, unless you are cross-compiling. This C compiler builds
447 # the finished binary for fossil. The BCC compiler above is used
448 # for building intermediate code-generator tools.
449 #
450 TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
451
452 #### Add the necessary command line options to build with debugging
453 # symbols, if enabled.
454 #
455 ifdef FOSSIL_ENABLE_SYMBOLS
456 TCC += -g
457 endif
458
459 #### Compile resources for use in building executables that will run
460 # on the target platform.
461 #
462 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
463
+1 -1
--- src/manifest.c
+++ src/manifest.c
@@ -833,11 +833,11 @@
833833
default: {
834834
SYNTAX("unrecognized card");
835835
}
836836
}
837837
}
838
- if( x.z<x.zEnd ) SYNTAX("card in the wrong order");
838
+ if( x.z<x.zEnd ) SYNTAX("extra characters at end of card");
839839
840840
if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
841841
if( p->nCChild>0 ) SYNTAX("M-card in check-in");
842842
if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
843843
if( p->nField>0 ) SYNTAX("J-card in check-in");
844844
--- src/manifest.c
+++ src/manifest.c
@@ -833,11 +833,11 @@
833 default: {
834 SYNTAX("unrecognized card");
835 }
836 }
837 }
838 if( x.z<x.zEnd ) SYNTAX("card in the wrong order");
839
840 if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
841 if( p->nCChild>0 ) SYNTAX("M-card in check-in");
842 if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
843 if( p->nField>0 ) SYNTAX("J-card in check-in");
844
--- src/manifest.c
+++ src/manifest.c
@@ -833,11 +833,11 @@
833 default: {
834 SYNTAX("unrecognized card");
835 }
836 }
837 }
838 if( x.z<x.zEnd ) SYNTAX("extra characters at end of card");
839
840 if( p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline ){
841 if( p->nCChild>0 ) SYNTAX("M-card in check-in");
842 if( p->rDate<=0.0 ) SYNTAX("missing date for check-in");
843 if( p->nField>0 ) SYNTAX("J-card in check-in");
844
+23 -10
--- src/printf.c
+++ src/printf.c
@@ -44,12 +44,11 @@
4444
#define etHTMLIZE 16 /* Make text safe for HTML */
4545
#define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */
4646
#define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */
4747
#define etFOSSILIZE 19 /* The fossil header encoding format. */
4848
#define etPATH 20 /* Path type */
49
-#define etWIKISTR 21 /* Wiki text rendered from a char*: %w */
50
-#define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */
49
+#define etWIKISTR 21 /* Timeline comment text rendered from a char*: %w */
5150
#define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */
5251
#define etROOT 24 /* String value of g.zTop: % */
5352
5453
5554
/*
@@ -92,11 +91,10 @@
9291
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
9392
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
9493
{ 'b', 0, 2, etBLOB, 0, 0 },
9594
{ 'B', 0, 2, etBLOBSQL, 0, 0 },
9695
{ 'w', 0, 2, etWIKISTR, 0, 0 },
97
- { 'W', 0, 2, etWIKIBLOB, 0, 0 },
9896
{ 'h', 0, 4, etHTMLIZE, 0, 0 },
9997
{ 'R', 0, 0, etROOT, 0, 0 },
10098
{ 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */
10199
{ 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */
102100
{ 'F', 0, 4, etFOSSILIZE, 0, 0 },
@@ -156,10 +154,31 @@
156154
static int StrNLen32(const char *z, int N){
157155
int n = 0;
158156
while( (N-- != 0) && *(z++)!=0 ){ n++; }
159157
return n;
160158
}
159
+
160
+/*
161
+** Return an appropriate set of flags for wiki_convert() for displaying
162
+** comments on a timeline. These flag settings are determined by
163
+** configuration parameters.
164
+*/
165
+static int wiki_convert_flags(void){
166
+ static int wikiFlags = 0;
167
+ if( wikiFlags==0 ){
168
+ if( db_get_boolean("timeline-block-markup", 0) ){
169
+ wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
170
+ }else{
171
+ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
172
+ }
173
+ if( db_get_boolean("timeline-plaintext", 0) ){
174
+ wikiFlags |= WIKI_LINKSONLY;
175
+ }
176
+ }
177
+ return wikiFlags;
178
+}
179
+
161180
162181
163182
/*
164183
** The root program. All variations call this core.
165184
**
@@ -698,21 +717,15 @@
698717
case etWIKISTR: {
699718
int limit = flag_alternateform ? va_arg(ap,int) : -1;
700719
char *zWiki = va_arg(ap, char*);
701720
Blob wiki;
702721
blob_init(&wiki, zWiki, limit);
703
- wiki_convert(&wiki, pBlob, WIKI_INLINE);
722
+ wiki_convert(&wiki, pBlob, wiki_convert_flags());
704723
blob_reset(&wiki);
705724
length = width = 0;
706725
break;
707726
}
708
- case etWIKIBLOB: {
709
- Blob *pWiki = va_arg(ap, Blob*);
710
- wiki_convert(pWiki, pBlob, WIKI_INLINE);
711
- length = width = 0;
712
- break;
713
- }
714727
case etERROR:
715728
buf[0] = '%';
716729
buf[1] = c;
717730
errorflag = 0;
718731
idx = 1+(c!=0);
719732
--- src/printf.c
+++ src/printf.c
@@ -44,12 +44,11 @@
44 #define etHTMLIZE 16 /* Make text safe for HTML */
45 #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */
46 #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */
47 #define etFOSSILIZE 19 /* The fossil header encoding format. */
48 #define etPATH 20 /* Path type */
49 #define etWIKISTR 21 /* Wiki text rendered from a char*: %w */
50 #define etWIKIBLOB 22 /* Wiki text rendered from a Blob*: %W */
51 #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */
52 #define etROOT 24 /* String value of g.zTop: % */
53
54
55 /*
@@ -92,11 +91,10 @@
92 { 'q', 0, 4, etSQLESCAPE, 0, 0 },
93 { 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
94 { 'b', 0, 2, etBLOB, 0, 0 },
95 { 'B', 0, 2, etBLOBSQL, 0, 0 },
96 { 'w', 0, 2, etWIKISTR, 0, 0 },
97 { 'W', 0, 2, etWIKIBLOB, 0, 0 },
98 { 'h', 0, 4, etHTMLIZE, 0, 0 },
99 { 'R', 0, 0, etROOT, 0, 0 },
100 { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */
101 { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */
102 { 'F', 0, 4, etFOSSILIZE, 0, 0 },
@@ -156,10 +154,31 @@
156 static int StrNLen32(const char *z, int N){
157 int n = 0;
158 while( (N-- != 0) && *(z++)!=0 ){ n++; }
159 return n;
160 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
162
163 /*
164 ** The root program. All variations call this core.
165 **
@@ -698,21 +717,15 @@
698 case etWIKISTR: {
699 int limit = flag_alternateform ? va_arg(ap,int) : -1;
700 char *zWiki = va_arg(ap, char*);
701 Blob wiki;
702 blob_init(&wiki, zWiki, limit);
703 wiki_convert(&wiki, pBlob, WIKI_INLINE);
704 blob_reset(&wiki);
705 length = width = 0;
706 break;
707 }
708 case etWIKIBLOB: {
709 Blob *pWiki = va_arg(ap, Blob*);
710 wiki_convert(pWiki, pBlob, WIKI_INLINE);
711 length = width = 0;
712 break;
713 }
714 case etERROR:
715 buf[0] = '%';
716 buf[1] = c;
717 errorflag = 0;
718 idx = 1+(c!=0);
719
--- src/printf.c
+++ src/printf.c
@@ -44,12 +44,11 @@
44 #define etHTMLIZE 16 /* Make text safe for HTML */
45 #define etHTTPIZE 17 /* Make text safe for HTTP. "/" encoded as %2f */
46 #define etURLIZE 18 /* Make text safe for HTTP. "/" not encoded */
47 #define etFOSSILIZE 19 /* The fossil header encoding format. */
48 #define etPATH 20 /* Path type */
49 #define etWIKISTR 21 /* Timeline comment text rendered from a char*: %w */
 
50 #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */
51 #define etROOT 24 /* String value of g.zTop: % */
52
53
54 /*
@@ -92,11 +91,10 @@
91 { 'q', 0, 4, etSQLESCAPE, 0, 0 },
92 { 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
93 { 'b', 0, 2, etBLOB, 0, 0 },
94 { 'B', 0, 2, etBLOBSQL, 0, 0 },
95 { 'w', 0, 2, etWIKISTR, 0, 0 },
 
96 { 'h', 0, 4, etHTMLIZE, 0, 0 },
97 { 'R', 0, 0, etROOT, 0, 0 },
98 { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */
99 { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */
100 { 'F', 0, 4, etFOSSILIZE, 0, 0 },
@@ -156,10 +154,31 @@
154 static int StrNLen32(const char *z, int N){
155 int n = 0;
156 while( (N-- != 0) && *(z++)!=0 ){ n++; }
157 return n;
158 }
159
160 /*
161 ** Return an appropriate set of flags for wiki_convert() for displaying
162 ** comments on a timeline. These flag settings are determined by
163 ** configuration parameters.
164 */
165 static int wiki_convert_flags(void){
166 static int wikiFlags = 0;
167 if( wikiFlags==0 ){
168 if( db_get_boolean("timeline-block-markup", 0) ){
169 wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
170 }else{
171 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
172 }
173 if( db_get_boolean("timeline-plaintext", 0) ){
174 wikiFlags |= WIKI_LINKSONLY;
175 }
176 }
177 return wikiFlags;
178 }
179
180
181
182 /*
183 ** The root program. All variations call this core.
184 **
@@ -698,21 +717,15 @@
717 case etWIKISTR: {
718 int limit = flag_alternateform ? va_arg(ap,int) : -1;
719 char *zWiki = va_arg(ap, char*);
720 Blob wiki;
721 blob_init(&wiki, zWiki, limit);
722 wiki_convert(&wiki, pBlob, wiki_convert_flags());
723 blob_reset(&wiki);
724 length = width = 0;
725 break;
726 }
 
 
 
 
 
 
727 case etERROR:
728 buf[0] = '%';
729 buf[1] = c;
730 errorflag = 0;
731 idx = 1+(c!=0);
732
+1 -1
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730730
}
731731
if( zData[0] ){
732732
Blob content;
733733
@ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734734
blob_init(&content, zData, -1);
735
- wiki_convert(&content, 0, 0);
735
+ wiki_convert(&content, 0, WIKI_NOBADLINKS);
736736
blob_reset(&content);
737737
}
738738
}else if( azName[i][0]=='#' ){
739739
zTid = zData;
740740
@ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741741
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730 }
731 if( zData[0] ){
732 Blob content;
733 @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734 blob_init(&content, zData, -1);
735 wiki_convert(&content, 0, 0);
736 blob_reset(&content);
737 }
738 }else if( azName[i][0]=='#' ){
739 zTid = zData;
740 @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730 }
731 if( zData[0] ){
732 Blob content;
733 @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734 blob_init(&content, zData, -1);
735 wiki_convert(&content, 0, WIKI_NOBADLINKS);
736 blob_reset(&content);
737 }
738 }else if( azName[i][0]=='#' ){
739 zTid = zData;
740 @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741
+11 -5
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934934
@ if it does, your server will end up computing diffs and annotations for
935935
@ every historical version of every file and creating ZIPs and tarballs of
936936
@ every historical check-in, which can use a lot of CPU and bandwidth
937937
@ even for relatively small projects.</p>
938938
939
+ @ <hr />
940
+ onoff_attribute("Require a CAPTCHA if not logged in",
941
+ "require-captcha", "reqcapt", 1);
942
+ @ <p>Require a CAPTCHA for edit operations (appending, creating, or
943
+ @ editing wiki or tickets or adding attachments to wiki or tickets)
944
+ @ for users who are not logged in.</p>
945
+
939946
@ <hr />
940947
entry_attribute("Public pages", 30, "public-pages",
941948
"pubpage", "");
942949
@ <p>A comma-separated list of glob patterns for pages that are accessible
943950
@ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
944951
@ "Default privileges" setting below. Example use case: Set this field
945952
@ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
946953
@ latest version of the embedded documentation in the www/ folder without
947954
@ allowing them to see the rest of the source code.
948955
@ </p>
949
-
950956
951957
@ <hr />
952958
onoff_attribute("Allow users to register themselves",
953959
"self-register", "selfregister", 0);
954960
@ <p>Allow users to register themselves through the HTTP UI.
@@ -1018,21 +1024,21 @@
10181024
@ is not currently part of any login-group.
10191025
@ To join a login group, fill out the form below.</p>
10201026
@
10211027
@ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
10221028
login_insert_csrf_secret();
1023
- @ <blockquote><table broder="0">
1029
+ @ <blockquote><table border="0">
10241030
@
10251031
@ <tr><td align="right"><b>Repository filename in group to join:</b></td>
10261032
@ <td width="5"></td><td>
10271033
@ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
10281034
@
1029
- @ <td align="right"><b>Login on the above repo:</b></td>
1035
+ @ <tr><td align="right"><b>Login on the above repo:</b></td>
10301036
@ <td width="5"></td><td>
10311037
@ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
10321038
@
1033
- @ <td align="right"><b>Password:</b></td>
1039
+ @ <tr><td align="right"><b>Password:</b></td>
10341040
@ <td width="5"></td><td>
10351041
@ <input type="password" size="20" name="pw"></td></tr>
10361042
@
10371043
@ <tr><td align="right"><b>Name of login-group:</b></td>
10381044
@ <td width="5"></td><td>
@@ -1039,11 +1045,11 @@
10391045
@ <input type="text" size="30" value="%h(zNewName)" name="newname">
10401046
@ (only used if creating a new login-group).</td></tr>
10411047
@
10421048
@ <tr><td colspan="3" align="center">
10431049
@ <input type="submit" value="Join" name="join"></td></tr>
1044
- @ </table>
1050
+ @ </table></blockquote></div></form>
10451051
}else{
10461052
Stmt q;
10471053
int n = 0;
10481054
@ <p>This repository (in the file "%h(zSelfRepo)")
10491055
@ is currently part of the "<b>%h(zGroup)</b>" login group.
10501056
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934 @ if it does, your server will end up computing diffs and annotations for
935 @ every historical version of every file and creating ZIPs and tarballs of
936 @ every historical check-in, which can use a lot of CPU and bandwidth
937 @ even for relatively small projects.</p>
938
 
 
 
 
 
 
 
939 @ <hr />
940 entry_attribute("Public pages", 30, "public-pages",
941 "pubpage", "");
942 @ <p>A comma-separated list of glob patterns for pages that are accessible
943 @ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
944 @ "Default privileges" setting below. Example use case: Set this field
945 @ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
946 @ latest version of the embedded documentation in the www/ folder without
947 @ allowing them to see the rest of the source code.
948 @ </p>
949
950
951 @ <hr />
952 onoff_attribute("Allow users to register themselves",
953 "self-register", "selfregister", 0);
954 @ <p>Allow users to register themselves through the HTTP UI.
@@ -1018,21 +1024,21 @@
1018 @ is not currently part of any login-group.
1019 @ To join a login group, fill out the form below.</p>
1020 @
1021 @ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
1022 login_insert_csrf_secret();
1023 @ <blockquote><table broder="0">
1024 @
1025 @ <tr><td align="right"><b>Repository filename in group to join:</b></td>
1026 @ <td width="5"></td><td>
1027 @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
1028 @
1029 @ <td align="right"><b>Login on the above repo:</b></td>
1030 @ <td width="5"></td><td>
1031 @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
1032 @
1033 @ <td align="right"><b>Password:</b></td>
1034 @ <td width="5"></td><td>
1035 @ <input type="password" size="20" name="pw"></td></tr>
1036 @
1037 @ <tr><td align="right"><b>Name of login-group:</b></td>
1038 @ <td width="5"></td><td>
@@ -1039,11 +1045,11 @@
1039 @ <input type="text" size="30" value="%h(zNewName)" name="newname">
1040 @ (only used if creating a new login-group).</td></tr>
1041 @
1042 @ <tr><td colspan="3" align="center">
1043 @ <input type="submit" value="Join" name="join"></td></tr>
1044 @ </table>
1045 }else{
1046 Stmt q;
1047 int n = 0;
1048 @ <p>This repository (in the file "%h(zSelfRepo)")
1049 @ is currently part of the "<b>%h(zGroup)</b>" login group.
1050
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934 @ if it does, your server will end up computing diffs and annotations for
935 @ every historical version of every file and creating ZIPs and tarballs of
936 @ every historical check-in, which can use a lot of CPU and bandwidth
937 @ even for relatively small projects.</p>
938
939 @ <hr />
940 onoff_attribute("Require a CAPTCHA if not logged in",
941 "require-captcha", "reqcapt", 1);
942 @ <p>Require a CAPTCHA for edit operations (appending, creating, or
943 @ editing wiki or tickets or adding attachments to wiki or tickets)
944 @ for users who are not logged in.</p>
945
946 @ <hr />
947 entry_attribute("Public pages", 30, "public-pages",
948 "pubpage", "");
949 @ <p>A comma-separated list of glob patterns for pages that are accessible
950 @ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
951 @ "Default privileges" setting below. Example use case: Set this field
952 @ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
953 @ latest version of the embedded documentation in the www/ folder without
954 @ allowing them to see the rest of the source code.
955 @ </p>
 
956
957 @ <hr />
958 onoff_attribute("Allow users to register themselves",
959 "self-register", "selfregister", 0);
960 @ <p>Allow users to register themselves through the HTTP UI.
@@ -1018,21 +1024,21 @@
1024 @ is not currently part of any login-group.
1025 @ To join a login group, fill out the form below.</p>
1026 @
1027 @ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
1028 login_insert_csrf_secret();
1029 @ <blockquote><table border="0">
1030 @
1031 @ <tr><td align="right"><b>Repository filename in group to join:</b></td>
1032 @ <td width="5"></td><td>
1033 @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
1034 @
1035 @ <tr><td align="right"><b>Login on the above repo:</b></td>
1036 @ <td width="5"></td><td>
1037 @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
1038 @
1039 @ <tr><td align="right"><b>Password:</b></td>
1040 @ <td width="5"></td><td>
1041 @ <input type="password" size="20" name="pw"></td></tr>
1042 @
1043 @ <tr><td align="right"><b>Name of login-group:</b></td>
1044 @ <td width="5"></td><td>
@@ -1039,11 +1045,11 @@
1045 @ <input type="text" size="30" value="%h(zNewName)" name="newname">
1046 @ (only used if creating a new login-group).</td></tr>
1047 @
1048 @ <tr><td colspan="3" align="center">
1049 @ <input type="submit" value="Join" name="join"></td></tr>
1050 @ </table></blockquote></div></form>
1051 }else{
1052 Stmt q;
1053 int n = 0;
1054 @ <p>This repository (in the file "%h(zSelfRepo)")
1055 @ is currently part of the "<b>%h(zGroup)</b>" login group.
1056
+115 -99
--- src/skins.c
+++ src/skins.c
@@ -24,20 +24,20 @@
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(name,mtime,value)
3131
@ VALUES('css',now(),'/* General settings for the entire page */
3232
@ body {
3333
@ margin: 0ex 1ex;
3434
@ padding: 0px;
3535
@ background-color: white;
3636
@ font-family: sans-serif;
3737
@ }
38
-@
38
+@
3939
@ /* The project logo in the upper left-hand corner of each page */
4040
@ div.logo {
4141
@ display: table-row;
4242
@ text-align: center;
4343
@ /* vertical-align: bottom;*/
@@ -44,12 +44,13 @@
4444
@ font-size: 2em;
4545
@ font-weight: bold;
4646
@ background-color: #707070;
4747
@ color: #ffffff;
4848
@ min-width: 200px;
49
+@ white-space: nowrap;
4950
@ }
50
-@
51
+@
5152
@ /* The page title centered at the top of each page */
5253
@ div.title {
5354
@ display: table-cell;
5455
@ font-size: 1.5em;
5556
@ font-weight: bold;
@@ -57,28 +58,29 @@
5758
@ padding: 0 0 0 10px;
5859
@ color: #404040;
5960
@ vertical-align: bottom;
6061
@ width: 100%;
6162
@ }
62
-@
63
+@
6364
@ /* The login status message in the top right-hand corner */
6465
@ div.status {
6566
@ display: table-cell;
6667
@ text-align: right;
6768
@ vertical-align: bottom;
6869
@ color: #404040;
6970
@ font-size: 0.8em;
7071
@ font-weight: bold;
7172
@ min-width: 200px;
73
+@ white-space: nowrap;
7274
@ }
73
-@
75
+@
7476
@ /* The header across the top of the page */
7577
@ div.header {
7678
@ display: table;
7779
@ width: 100%;
7880
@ }
79
-@
81
+@
8082
@ /* The main menu bar that appears at the top of the page beneath
8183
@ ** the header */
8284
@ div.mainmenu {
8385
@ padding: 5px 10px 5px 10px;
8486
@ font-size: 0.9em;
@@ -115,43 +117,45 @@
115117
@ }
116118
@ /* Hyperlink colors */
117119
@ div.content a { color: #604000; }
118120
@ div.content a:link { color: #604000;}
119121
@ div.content a:visited { color: #600000; }
120
-@
122
+@
121123
@ /* Some pages have section dividers */
122124
@ div.section {
123125
@ margin-bottom: 0px;
124126
@ margin-top: 1em;
125127
@ padding: 1px 1px 1px 1px;
126128
@ font-size: 1.2em;
127129
@ font-weight: bold;
128130
@ background-color: #404040;
129131
@ color: white;
132
+@ white-space: nowrap;
130133
@ }
131
-@
134
+@
132135
@ /* The "Date" that occurs on the left hand side of timelines */
133136
@ div.divider {
134137
@ background: #a0a0a0;
135138
@ border: 2px #505050 solid;
136139
@ font-size: 1em; font-weight: normal;
137140
@ padding: .25em;
138141
@ margin: .2em 0 .2em 0;
139142
@ float: left;
140143
@ clear: left;
144
+@ white-space: nowrap;
141145
@ }
142
-@
146
+@
143147
@ /* The footer at the very bottom of the page */
144148
@ div.footer {
145149
@ font-size: 0.8em;
146150
@ margin-top: 12px;
147151
@ padding: 5px 10px 5px 10px;
148152
@ text-align: right;
149153
@ background-color: #404040;
150154
@ color: white;
151155
@ }
152
-@
156
+@
153157
@ /* The label/value pairs on (for example) the vinfo page */
154158
@ table.label-value th {
155159
@ vertical-align: top;
156160
@ text-align: right;
157161
@ padding: 0.2ex 2ex;
@@ -166,17 +170,17 @@
166170
@ media="screen">
167171
@ </head>
168172
@ <body>
169173
@ <div class="header">
170174
@ <div class="title"><small>$<project_name></small><br />$<title></div>
171
-@ <div class="status"><nobr><th1>
175
+@ <div class="status"><th1>
172176
@ if {[info exists login]} {
173177
@ puts "Logged in as $login"
174178
@ } else {
175179
@ puts "Not logged in"
176180
@ }
177
-@ </th1></nobr></div>
181
+@ </th1></div>
178182
@ </div>
179183
@ <div class="mainmenu">
180184
@ <th1>
181185
@ html "<a href=''$home$index_page''>Home</a>\n"
182186
@ if {[anycap jor]} {
@@ -207,40 +211,41 @@
207211
@ }
208212
@ </th1></div>
209213
@ ');
210214
@ REPLACE INTO config(name,mtime,value)
211215
@ VALUES('footer',now(),'<div class="footer">
212
-@ Fossil version $manifest_version $manifest_date
216
+@ Fossil version $manifest_version $manifest_date
213217
@ </div>
214218
@ </body></html>
215219
@ ');
216220
;
217221
218222
/*
219223
** A tan theme with the project title above the user identification
220224
** and no logo image.
221225
*/
222
-static const char zBuiltinSkin2[] =
226
+static const char zBuiltinSkin2[] =
223227
@ REPLACE INTO config(name,mtime,value)
224228
@ VALUES('css',now(),'/* General settings for the entire page */
225229
@ body {
226230
@ margin: 0ex 0ex;
227231
@ padding: 0px;
228232
@ background-color: #fef3bc;
229233
@ font-family: sans-serif;
230234
@ }
231
-@
235
+@
232236
@ /* The project logo in the upper left-hand corner of each page */
233237
@ div.logo {
234238
@ display: inline;
235239
@ text-align: center;
236240
@ vertical-align: bottom;
237241
@ font-weight: bold;
238242
@ font-size: 2.5em;
239243
@ color: #a09048;
244
+@ white-space: nowrap;
240245
@ }
241
-@
246
+@
242247
@ /* The page title centered at the top of each page */
243248
@ div.title {
244249
@ display: table-cell;
245250
@ font-size: 2em;
246251
@ font-weight: bold;
@@ -248,28 +253,29 @@
248253
@ padding: 0 0 0 5px;
249254
@ color: #a09048;
250255
@ vertical-align: bottom;
251256
@ width: 100%;
252257
@ }
253
-@
258
+@
254259
@ /* The login status message in the top right-hand corner */
255260
@ div.status {
256261
@ display: table-cell;
257262
@ text-align: right;
258263
@ vertical-align: bottom;
259264
@ color: #a09048;
260265
@ padding: 5px 5px 0 0;
261266
@ font-size: 0.8em;
262267
@ font-weight: bold;
268
+@ white-space: nowrap;
263269
@ }
264
-@
270
+@
265271
@ /* The header across the top of the page */
266272
@ div.header {
267273
@ display: table;
268274
@ width: 100%;
269275
@ }
270
-@
276
+@
271277
@ /* The main menu bar that appears at the top of the page beneath
272278
@ ** the header */
273279
@ div.mainmenu {
274280
@ padding: 5px 10px 5px 10px;
275281
@ font-size: 0.9em;
@@ -277,11 +283,11 @@
277283
@ text-align: center;
278284
@ letter-spacing: 1px;
279285
@ background-color: #a09048;
280286
@ color: black;
281287
@ }
282
-@
288
+@
283289
@ /* The submenu bar that *sometimes* appears below the main menu */
284290
@ div.submenu, div.sectionmenu {
285291
@ padding: 3px 10px 3px 0px;
286292
@ font-size: 0.9em;
287293
@ text-align: center;
@@ -296,65 +302,67 @@
296302
@ }
297303
@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
298304
@ color: #a09048;
299305
@ background-color: white;
300306
@ }
301
-@
307
+@
302308
@ /* All page content from the bottom of the menu or submenu down to
303309
@ ** the footer */
304310
@ div.content {
305311
@ padding: 1ex 5px;
306312
@ }
307313
@ div.content a { color: #706532; }
308314
@ div.content a:link { color: #706532; }
309315
@ div.content a:visited { color: #704032; }
310316
@ div.content a:hover { background-color: white; color: #706532; }
311
-@
317
+@
312318
@ /* Some pages have section dividers */
313319
@ div.section {
314320
@ margin-bottom: 0px;
315321
@ margin-top: 1em;
316322
@ padding: 3px 3px 0 3px;
317323
@ font-size: 1.2em;
318324
@ font-weight: bold;
319325
@ background-color: #a09048;
320326
@ color: white;
327
+@ white-space: nowrap;
321328
@ }
322
-@
329
+@
323330
@ /* The "Date" that occurs on the left hand side of timelines */
324331
@ div.divider {
325332
@ background: #e1d498;
326333
@ border: 2px #a09048 solid;
327334
@ font-size: 1em; font-weight: normal;
328335
@ padding: .25em;
329336
@ margin: .2em 0 .2em 0;
330337
@ float: left;
331338
@ clear: left;
339
+@ white-space: nowrap;
332340
@ }
333
-@
341
+@
334342
@ /* The footer at the very bottom of the page */
335343
@ div.footer {
336344
@ font-size: 0.8em;
337345
@ margin-top: 12px;
338346
@ padding: 5px 10px 5px 10px;
339347
@ text-align: right;
340348
@ background-color: #a09048;
341349
@ color: white;
342350
@ }
343
-@
351
+@
344352
@ /* Hyperlink colors */
345353
@ div.footer a { color: white; }
346354
@ div.footer a:link { color: white; }
347355
@ div.footer a:visited { color: white; }
348356
@ div.footer a:hover { background-color: white; color: #558195; }
349
-@
357
+@
350358
@ /* <verbatim> blocks */
351359
@ pre.verbatim {
352360
@ background-color: #f5f5f5;
353361
@ padding: 0.5em;
354362
@ }
355
-@
363
+@
356364
@ /* The label/value pairs on (for example) the ci page */
357365
@ table.label-value th {
358366
@ vertical-align: top;
359367
@ text-align: right;
360368
@ padding: 0.2ex 2ex;
@@ -370,18 +378,18 @@
370378
@ </head>
371379
@ <body>
372380
@ <div class="header">
373381
@ <div class="title">$<title></div>
374382
@ <div class="status">
375
-@ <div class="logo"><nobr>$<project_name></nobr></div><br/>
376
-@ <nobr><th1>
383
+@ <div class="logo">$<project_name></div><br/>
384
+@ <th1>
377385
@ if {[info exists login]} {
378386
@ puts "Logged in as $login"
379387
@ } else {
380388
@ puts "Not logged in"
381389
@ }
382
-@ </th1></nobr></div>
390
+@ </th1></div>
383391
@ </div>
384392
@ <div class="mainmenu">
385393
@ <th1>
386394
@ html "<a href=''$home$index_page''>Home</a>\n"
387395
@ if {[anycap jor]} {
@@ -422,38 +430,39 @@
422430
423431
/*
424432
** Black letters on a white or cream background with the main menu
425433
** stuck on the left-hand side.
426434
*/
427
-static const char zBuiltinSkin3[] =
435
+static const char zBuiltinSkin3[] =
428436
@ REPLACE INTO config(name,mtime,value)
429437
@ VALUES('css',now(),'/* General settings for the entire page */
430438
@ body {
431439
@ margin:0px 0px 0px 0px;
432440
@ padding:0px;
433441
@ font-family:verdana, arial, helvetica, "sans serif";
434442
@ color:#333;
435443
@ background-color:white;
436444
@ }
437
-@
445
+@
438446
@ /* consistent colours */
439447
@ h2 {
440448
@ color: #333;
441449
@ }
442450
@ h3 {
443451
@ color: #333;
444452
@ }
445
-@
453
+@
446454
@ /* The project logo in the upper left-hand corner of each page */
447455
@ div.logo {
448456
@ display: table-cell;
449457
@ text-align: left;
450458
@ vertical-align: bottom;
451459
@ font-weight: bold;
452460
@ color: #333;
461
+@ white-space: nowrap;
453462
@ }
454
-@
463
+@
455464
@ /* The page title centered at the top of each page */
456465
@ div.title {
457466
@ display: table-cell;
458467
@ font-size: 2em;
459468
@ font-weight: bold;
@@ -460,11 +469,11 @@
460469
@ text-align: center;
461470
@ color: #333;
462471
@ vertical-align: bottom;
463472
@ width: 100%;
464473
@ }
465
-@
474
+@
466475
@ /* The login status message in the top right-hand corner */
467476
@ div.status {
468477
@ display: table-cell;
469478
@ padding-right: 10px;
470479
@ text-align: right;
@@ -471,22 +480,23 @@
471480
@ vertical-align: bottom;
472481
@ padding-bottom: 5px;
473482
@ color: #333;
474483
@ font-size: 0.8em;
475484
@ font-weight: bold;
485
+@ white-space: nowrap;
476486
@ }
477
-@
487
+@
478488
@ /* The header across the top of the page */
479489
@ div.header {
480490
@ margin:10px 0px 10px 0px;
481491
@ padding:1px 0px 0px 20px;
482492
@ border-style:solid;
483493
@ border-color:black;
484494
@ border-width:1px 0px;
485495
@ background-color:#eee;
486496
@ }
487
-@
497
+@
488498
@ /* The main menu bar that appears at the top left of the page beneath
489499
@ ** the header. Width must be co-ordinated with the container below */
490500
@ div.mainmenu {
491501
@ float: left;
492502
@ margin-left: 10px;
@@ -496,11 +506,11 @@
496506
@ padding:5px;
497507
@ background-color:#eee;
498508
@ border:1px solid #999;
499509
@ width:8em;
500510
@ }
501
-@
511
+@
502512
@ /* Main menu is now a list */
503513
@ div.mainmenu ul {
504514
@ padding: 0;
505515
@ list-style:none;
506516
@ }
@@ -511,17 +521,17 @@
511521
@ }
512522
@ div.mainmenu a:hover {
513523
@ color: #eee;
514524
@ background-color: #333;
515525
@ }
516
-@
526
+@
517527
@ /* Container for the sub-menu and content so they don''t spread
518528
@ ** out underneath the main menu */
519529
@ #container {
520530
@ padding-left: 9em;
521531
@ }
522
-@
532
+@
523533
@ /* The submenu bar that *sometimes* appears below the main menu */
524534
@ div.submenu, div.sectionmenu {
525535
@ padding: 3px 10px 3px 10px;
526536
@ font-size: 0.9em;
527537
@ text-align: center;
@@ -538,17 +548,17 @@
538548
@ }
539549
@ div.submenu a:hover, div.sectionmenu>a.button:hover {
540550
@ color: #eee;
541551
@ background-color: #333;
542552
@ }
543
-@
553
+@
544554
@ /* All page content from the bottom of the menu or submenu down to
545555
@ ** the footer */
546556
@ div.content {
547557
@ padding: 2ex 1ex 0ex 2ex;
548558
@ }
549
-@
559
+@
550560
@ /* Some pages have section dividers */
551561
@ div.section {
552562
@ margin-bottom: 0px;
553563
@ margin-top: 1em;
554564
@ padding: 1px 1px 1px 1px;
@@ -557,40 +567,42 @@
557567
@ border-style:solid;
558568
@ border-color:#999;
559569
@ border-width:1px 0px;
560570
@ background-color: #eee;
561571
@ color: #333;
572
+@ white-space: nowrap;
562573
@ }
563
-@
574
+@
564575
@ /* The "Date" that occurs on the left hand side of timelines */
565576
@ div.divider {
566577
@ background: #eee;
567578
@ border: 2px #999 solid;
568579
@ font-size: 1em; font-weight: normal;
569580
@ padding: .25em;
570581
@ margin: .2em 0 .2em 0;
571582
@ float: left;
572583
@ clear: left;
573
-@ color: #333
584
+@ color: #333;
585
+@ white-space: nowrap;
574586
@ }
575
-@
587
+@
576588
@ /* The footer at the very bottom of the page */
577589
@ div.footer {
578590
@ font-size: 0.8em;
579591
@ margin-top: 12px;
580592
@ padding: 5px 10px 5px 10px;
581593
@ text-align: right;
582594
@ background-color: #eee;
583595
@ color: #555;
584596
@ }
585
-@
597
+@
586598
@ /* <verbatim> blocks */
587599
@ pre.verbatim {
588600
@ background-color: #f5f5f5;
589601
@ padding: 0.5em;
590602
@ }
591
-@
603
+@
592604
@ /* The label/value pairs on (for example) the ci page */
593605
@ table.label-value th {
594606
@ vertical-align: top;
595607
@ text-align: right;
596608
@ padding: 0.2ex 2ex;
@@ -606,20 +618,20 @@
606618
@ </head>
607619
@ <body>
608620
@ <div class="header">
609621
@ <div class="logo">
610622
@ <img src="$home/logo" alt="logo">
611
-@ <br /><nobr>$<project_name></nobr>
623
+@ <br />$<project_name>
612624
@ </div>
613625
@ <div class="title">$<title></div>
614
-@ <div class="status"><nobr><th1>
626
+@ <div class="status"><th1>
615627
@ if {[info exists login]} {
616628
@ puts "Logged in as $login"
617629
@ } else {
618630
@ puts "Not logged in"
619631
@ }
620
-@ </th1></nobr></div>
632
+@ </th1></div>
621633
@ </div>
622634
@ <div class="mainmenu">
623635
@ <th1>
624636
@ html "<a href=''$home$index_page''>Home</a>\n"
625637
@ if {[anycap jor]} {
@@ -661,11 +673,11 @@
661673
662674
663675
/*
664676
** Shadow boxes and rounded corners.
665677
*/
666
-static const char zBuiltinSkin4[] =
678
+static const char zBuiltinSkin4[] =
667679
@ REPLACE INTO config(name,mtime,value)
668680
@ VALUES('css',now(),'/* General settings for the entire page */
669681
@ html {
670682
@ min-height: 100%;
671683
@ }
@@ -675,27 +687,28 @@
675687
@ background-color: white;
676688
@ color: #333;
677689
@ font-family: Verdana, sans-serif;
678690
@ font-size: 0.8em;
679691
@ }
680
-@
692
+@
681693
@ /* The project logo in the upper left-hand corner of each page */
682694
@ div.logo {
683695
@ display: table-cell;
684696
@ text-align: right;
685697
@ vertical-align: bottom;
686698
@ font-weight: normal;
699
+@ white-space: nowrap;
687700
@ }
688
-@
701
+@
689702
@ /* Widths */
690703
@ div.header, div.mainmenu, div.submenu, div.content, div.footer {
691704
@ max-width: 900px;
692705
@ margin: auto;
693706
@ padding: 3px 20px 3px 20px;
694707
@ clear: both;
695708
@ }
696
-@
709
+@
697710
@ /* The page title at the top of each page */
698711
@ div.title {
699712
@ display: table-cell;
700713
@ padding-left: 10px;
701714
@ font-size: 2em;
@@ -706,20 +719,21 @@
706719
@ font-family: Verdana, sans-serif;
707720
@ font-weight: bold;
708721
@ color: #558195;
709722
@ text-shadow: 0px 2px 2px #999999;
710723
@ }
711
-@
724
+@
712725
@ /* The login status message in the top right-hand corner */
713726
@ div.status {
714727
@ display: table-cell;
715728
@ text-align: right;
716729
@ vertical-align: bottom;
717730
@ color: #333;
718731
@ margin-right: -20px;
732
+@ white-space: nowrap;
719733
@ }
720
-@
734
+@
721735
@ /* The main menu bar that appears at the top of the page beneath
722736
@ ** the header */
723737
@ div.mainmenu {
724738
@ text-align: center;
725739
@ color: white;
@@ -729,11 +743,11 @@
729743
@ padding-top: 8px;
730744
@ padding-bottom: 8px;
731745
@ background-color: #446979;
732746
@ box-shadow: 0px 3px 4px #333333;
733747
@ }
734
-@
748
+@
735749
@ /* The submenu bar that *sometimes* appears below the main menu */
736750
@ div.submenu {
737751
@ padding-top:10px;
738752
@ padding-bottom:0;
739753
@ text-align: right;
@@ -754,24 +768,24 @@
754768
@ color: #000;
755769
@ font-family: Arial;
756770
@ text-decoration: none;
757771
@ margin:auto;
758772
@ border-radius: 5px;
759
-@ background-color: #e0e0e0 ;
773
+@ background-color: #e0e0e0;
760774
@ text-shadow: 0px -1px 0px #eee;
761775
@ border: 1px solid #000;
762776
@ }
763
-@
777
+@
764778
@ div.mainmenu a:hover {
765779
@ color: #000;
766780
@ background-color: white;
767781
@ }
768
-@
782
+@
769783
@ div.submenu a:hover, div.sectionmenu>a.button:hover {
770
-@ background-color: #c0c0c0 ;
784
+@ background-color: #c0c0c0;
771785
@ }
772
-@
786
+@
773787
@ /* All page content from the bottom of the menu or submenu down to
774788
@ ** the footer */
775789
@ div.content {
776790
@ background-color: #fff;
777791
@ box-shadow: 0px 3px 4px #999;
@@ -778,12 +792,12 @@
778792
@ border-bottom-right-radius: 5px;
779793
@ border-bottom-left-radius: 5px;
780794
@ padding-bottom: 1em;
781795
@ min-height:40%;
782796
@ }
783
-@
784
-@
797
+@
798
+@
785799
@ /* Some pages have section dividers */
786800
@ div.section {
787801
@ margin-bottom: 0.5em;
788802
@ margin-top: 1em;
789803
@ margin-right: auto;
@@ -793,70 +807,71 @@
793807
@ text-align: center;
794808
@ color: white;
795809
@ border-radius: 5px;
796810
@ background-color: #446979;
797811
@ box-shadow: 0px 3px 4px #333333;
812
+@ white-space: nowrap;
798813
@ }
799
-@
814
+@
800815
@ /* The "Date" that occurs on the left hand side of timelines */
801816
@ div.divider {
802
-@ font-size: 1.2em;
817
+@ font-size: 1.2em;
803818
@ font-family: Georgia, serif;
804819
@ font-weight: bold;
805820
@ margin-top: 1em;
806821
@ white-space: nowrap;
807822
@ }
808
-@
823
+@
809824
@ /* The footer at the very bottom of the page */
810825
@ div.footer {
811826
@ font-size: 0.9em;
812827
@ text-align: right;
813828
@ margin-bottom: 1em;
814829
@ color: #666;
815830
@ }
816
-@
831
+@
817832
@ /* Hyperlink colors in the footer */
818833
@ div.footer a { color: white; }
819834
@ div.footer a:link { color: white; }
820835
@ div.footer a:visited { color: white; }
821836
@ div.footer a:hover { background-color: white; color: #558195; }
822
-@
837
+@
823838
@ /* <verbatim> blocks */
824839
@ pre.verbatim, blockquote pre {
825840
@ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
826841
@ background-color: #f3f3f3;
827842
@ padding: 0.5em;
828843
@ white-space: pre-wrap;
829844
@ }
830
-@
845
+@
831846
@ blockquote pre {
832847
@ border: 1px #000 dashed;
833848
@ }
834
-@
849
+@
835850
@ /* The label/value pairs on (for example) the ci page */
836851
@ table.label-value th {
837852
@ vertical-align: top;
838853
@ text-align: right;
839854
@ padding: 0.2ex 2ex;
840855
@ }
841
-@
842
-@
856
+@
857
+@
843858
@ table.report {
844859
@ border-collapse:collapse;
845860
@ border: 1px solid #999;
846861
@ margin: 1em 0 1em 0;
847862
@ }
848
-@
863
+@
849864
@ table.report tr th {
850865
@ padding: 3px 5px;
851
-@ text-transform : capitalize;
866
+@ text-transform: capitalize;
852867
@ }
853
-@
868
+@
854869
@ table.report tr td {
855870
@ padding: 3px 5px;
856871
@ }
857
-@
872
+@
858873
@ textarea {
859874
@ font-size: 1em;
860875
@ }');
861876
@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
862877
@ <head>
@@ -869,20 +884,20 @@
869884
@ </head>
870885
@ <body>
871886
@ <div class="header">
872887
@ <div class="logo">
873888
@ <img src="$home/logo" alt="logo">
874
-@ <br /><nobr>$<project_name></nobr>
889
+@ <br />$<project_name>
875890
@ </div>
876891
@ <div class="title">$<title></div>
877
-@ <div class="status"><nobr><th1>
892
+@ <div class="status"><th1>
878893
@ if {[info exists login]} {
879894
@ puts "Logged in as $login"
880895
@ } else {
881896
@ puts "Not logged in"
882897
@ }
883
-@ </th1></nobr></div>
898
+@ </th1></div>
884899
@ </div>
885900
@ <div class="mainmenu">
886901
@ <th1>
887902
@ html "<a href=''$home$index_page''>Home</a>\n"
888903
@ if {[anycap jor]} {
@@ -937,11 +952,11 @@
937952
** Fossil repository. Additionally, if the Tcl integration feature is
938953
** enabled, the loaded version of Tcl is included, with a hyperlink to the
939954
** official Tcl/Tk web site. The footer also contains a TH1 script block
940955
** to help accomplish these tasks.
941956
*/
942
-static const char zBuiltinSkin5[] =
957
+static const char zBuiltinSkin5[] =
943958
@ REPLACE INTO config(name,mtime,value)
944959
@ VALUES('css',now(),'/* General settings for the entire page */
945960
@ body {
946961
@ margin: 0ex 1ex;
947962
@ padding: 0px;
@@ -955,10 +970,11 @@
955970
@ text-align: center;
956971
@ vertical-align: bottom;
957972
@ font-weight: bold;
958973
@ color: #558195;
959974
@ min-width: 200px;
975
+@ white-space: nowrap;
960976
@ }
961977
@
962978
@ /* The page title centered at the top of each page */
963979
@ div.title {
964980
@ display: table-cell;
@@ -966,11 +982,11 @@
966982
@ font-weight: bold;
967983
@ text-align: center;
968984
@ padding: 0 0 0 1em;
969985
@ color: #558195;
970986
@ vertical-align: bottom;
971
-@ width: 100% ;
987
+@ width: 100%;
972988
@ }
973989
@
974990
@ /* The login status message in the top right-hand corner */
975991
@ div.status {
976992
@ display: table-cell;
@@ -984,11 +1000,11 @@
9841000
@ }
9851001
@
9861002
@ /* The header across the top of the page */
9871003
@ div.header {
9881004
@ display: table;
989
-@ width: 100% ;
1005
+@ width: 100%;
9901006
@ }
9911007
@
9921008
@ /* The main menu bar that appears at the top of the page beneath
9931009
@ ** the header */
9941010
@ div.mainmenu {
@@ -1069,11 +1085,11 @@
10691085
@ /* Hyperlink colors in the footer */
10701086
@ div.footer a { color: white; }
10711087
@ div.footer a:link { color: white; }
10721088
@ div.footer a:visited { color: white; }
10731089
@ div.footer a:hover { background-color: white; color: #558195; }
1074
-@
1090
+@
10751091
@ /* verbatim blocks */
10761092
@ pre.verbatim {
10771093
@ background-color: #f5f5f5;
10781094
@ padding: 0.5em;
10791095
@}
@@ -1159,46 +1175,46 @@
11591175
@ <a href="$logourl">
11601176
@ <img src="$baseurl/logo" border="0" alt="$project_name">
11611177
@ </a>
11621178
@ </div>
11631179
@ <div class="title"><small>$<project_name></small><br />$<title></div>
1164
-@ <div class="status"><nobr><th1>
1180
+@ <div class="status"><th1>
11651181
@ if {[info exists login]} {
11661182
@ puts "Logged in as $login"
11671183
@ } else {
11681184
@ puts "Not logged in"
11691185
@ }
1170
-@ </th1></nobr></div>
1186
+@ </th1></div>
11711187
@ </div>
11721188
@ <div class="mainmenu">
11731189
@ <th1>
11741190
@ html "<a href=''$home$index_page''>Home</a>\n"
11751191
@ if {[anycap jor]} {
1176
-@ html "<a href=''timeline''>Timeline</a>\n"
1192
+@ html "<a href=''$home/timeline''>Timeline</a>\n"
11771193
@ }
11781194
@ if {[hascap oh]} {
1179
-@ html "<a href=''dir?ci=tip''>Files</a>\n"
1195
+@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
11801196
@ }
11811197
@ if {[hascap o]} {
1182
-@ html "<a href=''brlist''>Branches</a>\n"
1183
-@ html "<a href=''taglist''>Tags</a>\n"
1198
+@ html "<a href=''$home/brlist''>Branches</a>\n"
1199
+@ html "<a href=''$home/taglist''>Tags</a>\n"
11841200
@ }
11851201
@ if {[hascap r]} {
1186
-@ html "<a href=''reportlist''>Tickets</a>\n"
1202
+@ html "<a href=''$home/reportlist''>Tickets</a>\n"
11871203
@ }
11881204
@ if {[hascap j]} {
1189
-@ html "<a href=''wiki''>Wiki</a>\n"
1205
+@ html "<a href=''$home/wiki''>Wiki</a>\n"
11901206
@ }
11911207
@ if {[hascap s]} {
1192
-@ html "<a href=''setup''>Admin</a>\n"
1208
+@ html "<a href=''$home/setup''>Admin</a>\n"
11931209
@ } elseif {[hascap a]} {
1194
-@ html "<a href=''setup_ulist''>Users</a>\n"
1210
+@ html "<a href=''$home/setup_ulist''>Users</a>\n"
11951211
@ }
11961212
@ if {[info exists login]} {
1197
-@ html "<a href=''login''>Logout</a>\n"
1213
+@ html "<a href=''$home/login''>Logout</a>\n"
11981214
@ } else {
1199
-@ html "<a href=''login''>Login</a>\n"
1215
+@ html "<a href=''$home/login''>Login</a>\n"
12001216
@ }
12011217
@ </th1></div>
12021218
@ ');
12031219
@ REPLACE INTO config(name,mtime,value)
12041220
@ VALUES('footer',now(),'<div class="footer">
@@ -1381,11 +1397,11 @@
13811397
style_header("Skins");
13821398
if( zErr ){
13831399
@ <p><font color="red">%h(zErr)</font></p>
13841400
}
13851401
@ <p>A "skin" is a combination of
1386
- @ <a href="setup_editcss">CSS</a>,
1402
+ @ <a href="setup_editcss">CSS</a>,
13871403
@ <a href="setup_header">Header</a>,
13881404
@ <a href="setup_footer">Footer</a>, and
13891405
@ <a href="setup_logo">Logo</a> that determines the look and feel
13901406
@ of the web interface.</p>
13911407
@
@@ -1395,11 +1411,11 @@
13951411
z = aBuiltinSkin[i].zName;
13961412
if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
13971413
@ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
13981414
}else{
13991415
@ <li><form action="%s(g.zTop)/setup_skin" method="post"><div>
1400
- @ %h(z).&nbsp;&nbsp;
1416
+ @ %h(z).&nbsp;&nbsp;
14011417
@ <input type="hidden" name="sn" value="%h(z)" />
14021418
@ <input type="submit" name="load" value="Use This Skin" />
14031419
@ </div></form></li>
14041420
}
14051421
}
@@ -1413,11 +1429,11 @@
14131429
const char *zV = db_column_text(&q, 1);
14141430
if( fossil_strcmp(zV, zCurrent)==0 ){
14151431
@ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
14161432
}else{
14171433
@ <li><form action="%s(g.zTop)/setup_skin" method="post">
1418
- @ %h(zN).&nbsp;&nbsp;
1434
+ @ %h(zN).&nbsp;&nbsp;
14191435
@ <input type="hidden" name="sn" value="%h(zN)">
14201436
@ <input type="submit" name="load" value="Use This Skin">
14211437
@ <input type="submit" name="del1" value="Delete This Skin">
14221438
@ </form></li>
14231439
}
14241440
--- src/skins.c
+++ src/skins.c
@@ -24,20 +24,20 @@
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(name,mtime,value)
31 @ VALUES('css',now(),'/* General settings for the entire page */
32 @ body {
33 @ margin: 0ex 1ex;
34 @ padding: 0px;
35 @ background-color: white;
36 @ font-family: sans-serif;
37 @ }
38 @
39 @ /* The project logo in the upper left-hand corner of each page */
40 @ div.logo {
41 @ display: table-row;
42 @ text-align: center;
43 @ /* vertical-align: bottom;*/
@@ -44,12 +44,13 @@
44 @ font-size: 2em;
45 @ font-weight: bold;
46 @ background-color: #707070;
47 @ color: #ffffff;
48 @ min-width: 200px;
 
49 @ }
50 @
51 @ /* The page title centered at the top of each page */
52 @ div.title {
53 @ display: table-cell;
54 @ font-size: 1.5em;
55 @ font-weight: bold;
@@ -57,28 +58,29 @@
57 @ padding: 0 0 0 10px;
58 @ color: #404040;
59 @ vertical-align: bottom;
60 @ width: 100%;
61 @ }
62 @
63 @ /* The login status message in the top right-hand corner */
64 @ div.status {
65 @ display: table-cell;
66 @ text-align: right;
67 @ vertical-align: bottom;
68 @ color: #404040;
69 @ font-size: 0.8em;
70 @ font-weight: bold;
71 @ min-width: 200px;
 
72 @ }
73 @
74 @ /* The header across the top of the page */
75 @ div.header {
76 @ display: table;
77 @ width: 100%;
78 @ }
79 @
80 @ /* The main menu bar that appears at the top of the page beneath
81 @ ** the header */
82 @ div.mainmenu {
83 @ padding: 5px 10px 5px 10px;
84 @ font-size: 0.9em;
@@ -115,43 +117,45 @@
115 @ }
116 @ /* Hyperlink colors */
117 @ div.content a { color: #604000; }
118 @ div.content a:link { color: #604000;}
119 @ div.content a:visited { color: #600000; }
120 @
121 @ /* Some pages have section dividers */
122 @ div.section {
123 @ margin-bottom: 0px;
124 @ margin-top: 1em;
125 @ padding: 1px 1px 1px 1px;
126 @ font-size: 1.2em;
127 @ font-weight: bold;
128 @ background-color: #404040;
129 @ color: white;
 
130 @ }
131 @
132 @ /* The "Date" that occurs on the left hand side of timelines */
133 @ div.divider {
134 @ background: #a0a0a0;
135 @ border: 2px #505050 solid;
136 @ font-size: 1em; font-weight: normal;
137 @ padding: .25em;
138 @ margin: .2em 0 .2em 0;
139 @ float: left;
140 @ clear: left;
 
141 @ }
142 @
143 @ /* The footer at the very bottom of the page */
144 @ div.footer {
145 @ font-size: 0.8em;
146 @ margin-top: 12px;
147 @ padding: 5px 10px 5px 10px;
148 @ text-align: right;
149 @ background-color: #404040;
150 @ color: white;
151 @ }
152 @
153 @ /* The label/value pairs on (for example) the vinfo page */
154 @ table.label-value th {
155 @ vertical-align: top;
156 @ text-align: right;
157 @ padding: 0.2ex 2ex;
@@ -166,17 +170,17 @@
166 @ media="screen">
167 @ </head>
168 @ <body>
169 @ <div class="header">
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"
176 @ }
177 @ </th1></nobr></div>
178 @ </div>
179 @ <div class="mainmenu">
180 @ <th1>
181 @ html "<a href=''$home$index_page''>Home</a>\n"
182 @ if {[anycap jor]} {
@@ -207,40 +211,41 @@
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config(name,mtime,value)
211 @ VALUES('footer',now(),'<div class="footer">
212 @ Fossil version $manifest_version $manifest_date
213 @ </div>
214 @ </body></html>
215 @ ');
216 ;
217
218 /*
219 ** A tan theme with the project title above the user identification
220 ** and no logo image.
221 */
222 static const char zBuiltinSkin2[] =
223 @ REPLACE INTO config(name,mtime,value)
224 @ VALUES('css',now(),'/* General settings for the entire page */
225 @ body {
226 @ margin: 0ex 0ex;
227 @ padding: 0px;
228 @ background-color: #fef3bc;
229 @ font-family: sans-serif;
230 @ }
231 @
232 @ /* The project logo in the upper left-hand corner of each page */
233 @ div.logo {
234 @ display: inline;
235 @ text-align: center;
236 @ vertical-align: bottom;
237 @ font-weight: bold;
238 @ font-size: 2.5em;
239 @ color: #a09048;
 
240 @ }
241 @
242 @ /* The page title centered at the top of each page */
243 @ div.title {
244 @ display: table-cell;
245 @ font-size: 2em;
246 @ font-weight: bold;
@@ -248,28 +253,29 @@
248 @ padding: 0 0 0 5px;
249 @ color: #a09048;
250 @ vertical-align: bottom;
251 @ width: 100%;
252 @ }
253 @
254 @ /* The login status message in the top right-hand corner */
255 @ div.status {
256 @ display: table-cell;
257 @ text-align: right;
258 @ vertical-align: bottom;
259 @ color: #a09048;
260 @ padding: 5px 5px 0 0;
261 @ font-size: 0.8em;
262 @ font-weight: bold;
 
263 @ }
264 @
265 @ /* The header across the top of the page */
266 @ div.header {
267 @ display: table;
268 @ width: 100%;
269 @ }
270 @
271 @ /* The main menu bar that appears at the top of the page beneath
272 @ ** the header */
273 @ div.mainmenu {
274 @ padding: 5px 10px 5px 10px;
275 @ font-size: 0.9em;
@@ -277,11 +283,11 @@
277 @ text-align: center;
278 @ letter-spacing: 1px;
279 @ background-color: #a09048;
280 @ color: black;
281 @ }
282 @
283 @ /* The submenu bar that *sometimes* appears below the main menu */
284 @ div.submenu, div.sectionmenu {
285 @ padding: 3px 10px 3px 0px;
286 @ font-size: 0.9em;
287 @ text-align: center;
@@ -296,65 +302,67 @@
296 @ }
297 @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
298 @ color: #a09048;
299 @ background-color: white;
300 @ }
301 @
302 @ /* All page content from the bottom of the menu or submenu down to
303 @ ** the footer */
304 @ div.content {
305 @ padding: 1ex 5px;
306 @ }
307 @ div.content a { color: #706532; }
308 @ div.content a:link { color: #706532; }
309 @ div.content a:visited { color: #704032; }
310 @ div.content a:hover { background-color: white; color: #706532; }
311 @
312 @ /* Some pages have section dividers */
313 @ div.section {
314 @ margin-bottom: 0px;
315 @ margin-top: 1em;
316 @ padding: 3px 3px 0 3px;
317 @ font-size: 1.2em;
318 @ font-weight: bold;
319 @ background-color: #a09048;
320 @ color: white;
 
321 @ }
322 @
323 @ /* The "Date" that occurs on the left hand side of timelines */
324 @ div.divider {
325 @ background: #e1d498;
326 @ border: 2px #a09048 solid;
327 @ font-size: 1em; font-weight: normal;
328 @ padding: .25em;
329 @ margin: .2em 0 .2em 0;
330 @ float: left;
331 @ clear: left;
 
332 @ }
333 @
334 @ /* The footer at the very bottom of the page */
335 @ div.footer {
336 @ font-size: 0.8em;
337 @ margin-top: 12px;
338 @ padding: 5px 10px 5px 10px;
339 @ text-align: right;
340 @ background-color: #a09048;
341 @ color: white;
342 @ }
343 @
344 @ /* Hyperlink colors */
345 @ div.footer a { color: white; }
346 @ div.footer a:link { color: white; }
347 @ div.footer a:visited { color: white; }
348 @ div.footer a:hover { background-color: white; color: #558195; }
349 @
350 @ /* <verbatim> blocks */
351 @ pre.verbatim {
352 @ background-color: #f5f5f5;
353 @ padding: 0.5em;
354 @ }
355 @
356 @ /* The label/value pairs on (for example) the ci page */
357 @ table.label-value th {
358 @ vertical-align: top;
359 @ text-align: right;
360 @ padding: 0.2ex 2ex;
@@ -370,18 +378,18 @@
370 @ </head>
371 @ <body>
372 @ <div class="header">
373 @ <div class="title">$<title></div>
374 @ <div class="status">
375 @ <div class="logo"><nobr>$<project_name></nobr></div><br/>
376 @ <nobr><th1>
377 @ if {[info exists login]} {
378 @ puts "Logged in as $login"
379 @ } else {
380 @ puts "Not logged in"
381 @ }
382 @ </th1></nobr></div>
383 @ </div>
384 @ <div class="mainmenu">
385 @ <th1>
386 @ html "<a href=''$home$index_page''>Home</a>\n"
387 @ if {[anycap jor]} {
@@ -422,38 +430,39 @@
422
423 /*
424 ** Black letters on a white or cream background with the main menu
425 ** stuck on the left-hand side.
426 */
427 static const char zBuiltinSkin3[] =
428 @ REPLACE INTO config(name,mtime,value)
429 @ VALUES('css',now(),'/* General settings for the entire page */
430 @ body {
431 @ margin:0px 0px 0px 0px;
432 @ padding:0px;
433 @ font-family:verdana, arial, helvetica, "sans serif";
434 @ color:#333;
435 @ background-color:white;
436 @ }
437 @
438 @ /* consistent colours */
439 @ h2 {
440 @ color: #333;
441 @ }
442 @ h3 {
443 @ color: #333;
444 @ }
445 @
446 @ /* The project logo in the upper left-hand corner of each page */
447 @ div.logo {
448 @ display: table-cell;
449 @ text-align: left;
450 @ vertical-align: bottom;
451 @ font-weight: bold;
452 @ color: #333;
 
453 @ }
454 @
455 @ /* The page title centered at the top of each page */
456 @ div.title {
457 @ display: table-cell;
458 @ font-size: 2em;
459 @ font-weight: bold;
@@ -460,11 +469,11 @@
460 @ text-align: center;
461 @ color: #333;
462 @ vertical-align: bottom;
463 @ width: 100%;
464 @ }
465 @
466 @ /* The login status message in the top right-hand corner */
467 @ div.status {
468 @ display: table-cell;
469 @ padding-right: 10px;
470 @ text-align: right;
@@ -471,22 +480,23 @@
471 @ vertical-align: bottom;
472 @ padding-bottom: 5px;
473 @ color: #333;
474 @ font-size: 0.8em;
475 @ font-weight: bold;
 
476 @ }
477 @
478 @ /* The header across the top of the page */
479 @ div.header {
480 @ margin:10px 0px 10px 0px;
481 @ padding:1px 0px 0px 20px;
482 @ border-style:solid;
483 @ border-color:black;
484 @ border-width:1px 0px;
485 @ background-color:#eee;
486 @ }
487 @
488 @ /* The main menu bar that appears at the top left of the page beneath
489 @ ** the header. Width must be co-ordinated with the container below */
490 @ div.mainmenu {
491 @ float: left;
492 @ margin-left: 10px;
@@ -496,11 +506,11 @@
496 @ padding:5px;
497 @ background-color:#eee;
498 @ border:1px solid #999;
499 @ width:8em;
500 @ }
501 @
502 @ /* Main menu is now a list */
503 @ div.mainmenu ul {
504 @ padding: 0;
505 @ list-style:none;
506 @ }
@@ -511,17 +521,17 @@
511 @ }
512 @ div.mainmenu a:hover {
513 @ color: #eee;
514 @ background-color: #333;
515 @ }
516 @
517 @ /* Container for the sub-menu and content so they don''t spread
518 @ ** out underneath the main menu */
519 @ #container {
520 @ padding-left: 9em;
521 @ }
522 @
523 @ /* The submenu bar that *sometimes* appears below the main menu */
524 @ div.submenu, div.sectionmenu {
525 @ padding: 3px 10px 3px 10px;
526 @ font-size: 0.9em;
527 @ text-align: center;
@@ -538,17 +548,17 @@
538 @ }
539 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
540 @ color: #eee;
541 @ background-color: #333;
542 @ }
543 @
544 @ /* All page content from the bottom of the menu or submenu down to
545 @ ** the footer */
546 @ div.content {
547 @ padding: 2ex 1ex 0ex 2ex;
548 @ }
549 @
550 @ /* Some pages have section dividers */
551 @ div.section {
552 @ margin-bottom: 0px;
553 @ margin-top: 1em;
554 @ padding: 1px 1px 1px 1px;
@@ -557,40 +567,42 @@
557 @ border-style:solid;
558 @ border-color:#999;
559 @ border-width:1px 0px;
560 @ background-color: #eee;
561 @ color: #333;
 
562 @ }
563 @
564 @ /* The "Date" that occurs on the left hand side of timelines */
565 @ div.divider {
566 @ background: #eee;
567 @ border: 2px #999 solid;
568 @ font-size: 1em; font-weight: normal;
569 @ padding: .25em;
570 @ margin: .2em 0 .2em 0;
571 @ float: left;
572 @ clear: left;
573 @ color: #333
 
574 @ }
575 @
576 @ /* The footer at the very bottom of the page */
577 @ div.footer {
578 @ font-size: 0.8em;
579 @ margin-top: 12px;
580 @ padding: 5px 10px 5px 10px;
581 @ text-align: right;
582 @ background-color: #eee;
583 @ color: #555;
584 @ }
585 @
586 @ /* <verbatim> blocks */
587 @ pre.verbatim {
588 @ background-color: #f5f5f5;
589 @ padding: 0.5em;
590 @ }
591 @
592 @ /* The label/value pairs on (for example) the ci page */
593 @ table.label-value th {
594 @ vertical-align: top;
595 @ text-align: right;
596 @ padding: 0.2ex 2ex;
@@ -606,20 +618,20 @@
606 @ </head>
607 @ <body>
608 @ <div class="header">
609 @ <div class="logo">
610 @ <img src="$home/logo" alt="logo">
611 @ <br /><nobr>$<project_name></nobr>
612 @ </div>
613 @ <div class="title">$<title></div>
614 @ <div class="status"><nobr><th1>
615 @ if {[info exists login]} {
616 @ puts "Logged in as $login"
617 @ } else {
618 @ puts "Not logged in"
619 @ }
620 @ </th1></nobr></div>
621 @ </div>
622 @ <div class="mainmenu">
623 @ <th1>
624 @ html "<a href=''$home$index_page''>Home</a>\n"
625 @ if {[anycap jor]} {
@@ -661,11 +673,11 @@
661
662
663 /*
664 ** Shadow boxes and rounded corners.
665 */
666 static const char zBuiltinSkin4[] =
667 @ REPLACE INTO config(name,mtime,value)
668 @ VALUES('css',now(),'/* General settings for the entire page */
669 @ html {
670 @ min-height: 100%;
671 @ }
@@ -675,27 +687,28 @@
675 @ background-color: white;
676 @ color: #333;
677 @ font-family: Verdana, sans-serif;
678 @ font-size: 0.8em;
679 @ }
680 @
681 @ /* The project logo in the upper left-hand corner of each page */
682 @ div.logo {
683 @ display: table-cell;
684 @ text-align: right;
685 @ vertical-align: bottom;
686 @ font-weight: normal;
 
687 @ }
688 @
689 @ /* Widths */
690 @ div.header, div.mainmenu, div.submenu, div.content, div.footer {
691 @ max-width: 900px;
692 @ margin: auto;
693 @ padding: 3px 20px 3px 20px;
694 @ clear: both;
695 @ }
696 @
697 @ /* The page title at the top of each page */
698 @ div.title {
699 @ display: table-cell;
700 @ padding-left: 10px;
701 @ font-size: 2em;
@@ -706,20 +719,21 @@
706 @ font-family: Verdana, sans-serif;
707 @ font-weight: bold;
708 @ color: #558195;
709 @ text-shadow: 0px 2px 2px #999999;
710 @ }
711 @
712 @ /* The login status message in the top right-hand corner */
713 @ div.status {
714 @ display: table-cell;
715 @ text-align: right;
716 @ vertical-align: bottom;
717 @ color: #333;
718 @ margin-right: -20px;
 
719 @ }
720 @
721 @ /* The main menu bar that appears at the top of the page beneath
722 @ ** the header */
723 @ div.mainmenu {
724 @ text-align: center;
725 @ color: white;
@@ -729,11 +743,11 @@
729 @ padding-top: 8px;
730 @ padding-bottom: 8px;
731 @ background-color: #446979;
732 @ box-shadow: 0px 3px 4px #333333;
733 @ }
734 @
735 @ /* The submenu bar that *sometimes* appears below the main menu */
736 @ div.submenu {
737 @ padding-top:10px;
738 @ padding-bottom:0;
739 @ text-align: right;
@@ -754,24 +768,24 @@
754 @ color: #000;
755 @ font-family: Arial;
756 @ text-decoration: none;
757 @ margin:auto;
758 @ border-radius: 5px;
759 @ background-color: #e0e0e0 ;
760 @ text-shadow: 0px -1px 0px #eee;
761 @ border: 1px solid #000;
762 @ }
763 @
764 @ div.mainmenu a:hover {
765 @ color: #000;
766 @ background-color: white;
767 @ }
768 @
769 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
770 @ background-color: #c0c0c0 ;
771 @ }
772 @
773 @ /* All page content from the bottom of the menu or submenu down to
774 @ ** the footer */
775 @ div.content {
776 @ background-color: #fff;
777 @ box-shadow: 0px 3px 4px #999;
@@ -778,12 +792,12 @@
778 @ border-bottom-right-radius: 5px;
779 @ border-bottom-left-radius: 5px;
780 @ padding-bottom: 1em;
781 @ min-height:40%;
782 @ }
783 @
784 @
785 @ /* Some pages have section dividers */
786 @ div.section {
787 @ margin-bottom: 0.5em;
788 @ margin-top: 1em;
789 @ margin-right: auto;
@@ -793,70 +807,71 @@
793 @ text-align: center;
794 @ color: white;
795 @ border-radius: 5px;
796 @ background-color: #446979;
797 @ box-shadow: 0px 3px 4px #333333;
 
798 @ }
799 @
800 @ /* The "Date" that occurs on the left hand side of timelines */
801 @ div.divider {
802 @ font-size: 1.2em;
803 @ font-family: Georgia, serif;
804 @ font-weight: bold;
805 @ margin-top: 1em;
806 @ white-space: nowrap;
807 @ }
808 @
809 @ /* The footer at the very bottom of the page */
810 @ div.footer {
811 @ font-size: 0.9em;
812 @ text-align: right;
813 @ margin-bottom: 1em;
814 @ color: #666;
815 @ }
816 @
817 @ /* Hyperlink colors in the footer */
818 @ div.footer a { color: white; }
819 @ div.footer a:link { color: white; }
820 @ div.footer a:visited { color: white; }
821 @ div.footer a:hover { background-color: white; color: #558195; }
822 @
823 @ /* <verbatim> blocks */
824 @ pre.verbatim, blockquote pre {
825 @ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
826 @ background-color: #f3f3f3;
827 @ padding: 0.5em;
828 @ white-space: pre-wrap;
829 @ }
830 @
831 @ blockquote pre {
832 @ border: 1px #000 dashed;
833 @ }
834 @
835 @ /* The label/value pairs on (for example) the ci page */
836 @ table.label-value th {
837 @ vertical-align: top;
838 @ text-align: right;
839 @ padding: 0.2ex 2ex;
840 @ }
841 @
842 @
843 @ table.report {
844 @ border-collapse:collapse;
845 @ border: 1px solid #999;
846 @ margin: 1em 0 1em 0;
847 @ }
848 @
849 @ table.report tr th {
850 @ padding: 3px 5px;
851 @ text-transform : capitalize;
852 @ }
853 @
854 @ table.report tr td {
855 @ padding: 3px 5px;
856 @ }
857 @
858 @ textarea {
859 @ font-size: 1em;
860 @ }');
861 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
862 @ <head>
@@ -869,20 +884,20 @@
869 @ </head>
870 @ <body>
871 @ <div class="header">
872 @ <div class="logo">
873 @ <img src="$home/logo" alt="logo">
874 @ <br /><nobr>$<project_name></nobr>
875 @ </div>
876 @ <div class="title">$<title></div>
877 @ <div class="status"><nobr><th1>
878 @ if {[info exists login]} {
879 @ puts "Logged in as $login"
880 @ } else {
881 @ puts "Not logged in"
882 @ }
883 @ </th1></nobr></div>
884 @ </div>
885 @ <div class="mainmenu">
886 @ <th1>
887 @ html "<a href=''$home$index_page''>Home</a>\n"
888 @ if {[anycap jor]} {
@@ -937,11 +952,11 @@
937 ** Fossil repository. Additionally, if the Tcl integration feature is
938 ** enabled, the loaded version of Tcl is included, with a hyperlink to the
939 ** official Tcl/Tk web site. The footer also contains a TH1 script block
940 ** to help accomplish these tasks.
941 */
942 static const char zBuiltinSkin5[] =
943 @ REPLACE INTO config(name,mtime,value)
944 @ VALUES('css',now(),'/* General settings for the entire page */
945 @ body {
946 @ margin: 0ex 1ex;
947 @ padding: 0px;
@@ -955,10 +970,11 @@
955 @ text-align: center;
956 @ vertical-align: bottom;
957 @ font-weight: bold;
958 @ color: #558195;
959 @ min-width: 200px;
 
960 @ }
961 @
962 @ /* The page title centered at the top of each page */
963 @ div.title {
964 @ display: table-cell;
@@ -966,11 +982,11 @@
966 @ font-weight: bold;
967 @ text-align: center;
968 @ padding: 0 0 0 1em;
969 @ color: #558195;
970 @ vertical-align: bottom;
971 @ width: 100% ;
972 @ }
973 @
974 @ /* The login status message in the top right-hand corner */
975 @ div.status {
976 @ display: table-cell;
@@ -984,11 +1000,11 @@
984 @ }
985 @
986 @ /* The header across the top of the page */
987 @ div.header {
988 @ display: table;
989 @ width: 100% ;
990 @ }
991 @
992 @ /* The main menu bar that appears at the top of the page beneath
993 @ ** the header */
994 @ div.mainmenu {
@@ -1069,11 +1085,11 @@
1069 @ /* Hyperlink colors in the footer */
1070 @ div.footer a { color: white; }
1071 @ div.footer a:link { color: white; }
1072 @ div.footer a:visited { color: white; }
1073 @ div.footer a:hover { background-color: white; color: #558195; }
1074 @
1075 @ /* verbatim blocks */
1076 @ pre.verbatim {
1077 @ background-color: #f5f5f5;
1078 @ padding: 0.5em;
1079 @}
@@ -1159,46 +1175,46 @@
1159 @ <a href="$logourl">
1160 @ <img src="$baseurl/logo" border="0" alt="$project_name">
1161 @ </a>
1162 @ </div>
1163 @ <div class="title"><small>$<project_name></small><br />$<title></div>
1164 @ <div class="status"><nobr><th1>
1165 @ if {[info exists login]} {
1166 @ puts "Logged in as $login"
1167 @ } else {
1168 @ puts "Not logged in"
1169 @ }
1170 @ </th1></nobr></div>
1171 @ </div>
1172 @ <div class="mainmenu">
1173 @ <th1>
1174 @ html "<a href=''$home$index_page''>Home</a>\n"
1175 @ if {[anycap jor]} {
1176 @ html "<a href=''timeline''>Timeline</a>\n"
1177 @ }
1178 @ if {[hascap oh]} {
1179 @ html "<a href=''dir?ci=tip''>Files</a>\n"
1180 @ }
1181 @ if {[hascap o]} {
1182 @ html "<a href=''brlist''>Branches</a>\n"
1183 @ html "<a href=''taglist''>Tags</a>\n"
1184 @ }
1185 @ if {[hascap r]} {
1186 @ html "<a href=''reportlist''>Tickets</a>\n"
1187 @ }
1188 @ if {[hascap j]} {
1189 @ html "<a href=''wiki''>Wiki</a>\n"
1190 @ }
1191 @ if {[hascap s]} {
1192 @ html "<a href=''setup''>Admin</a>\n"
1193 @ } elseif {[hascap a]} {
1194 @ html "<a href=''setup_ulist''>Users</a>\n"
1195 @ }
1196 @ if {[info exists login]} {
1197 @ html "<a href=''login''>Logout</a>\n"
1198 @ } else {
1199 @ html "<a href=''login''>Login</a>\n"
1200 @ }
1201 @ </th1></div>
1202 @ ');
1203 @ REPLACE INTO config(name,mtime,value)
1204 @ VALUES('footer',now(),'<div class="footer">
@@ -1381,11 +1397,11 @@
1381 style_header("Skins");
1382 if( zErr ){
1383 @ <p><font color="red">%h(zErr)</font></p>
1384 }
1385 @ <p>A "skin" is a combination of
1386 @ <a href="setup_editcss">CSS</a>,
1387 @ <a href="setup_header">Header</a>,
1388 @ <a href="setup_footer">Footer</a>, and
1389 @ <a href="setup_logo">Logo</a> that determines the look and feel
1390 @ of the web interface.</p>
1391 @
@@ -1395,11 +1411,11 @@
1395 z = aBuiltinSkin[i].zName;
1396 if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
1397 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
1398 }else{
1399 @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div>
1400 @ %h(z).&nbsp;&nbsp;
1401 @ <input type="hidden" name="sn" value="%h(z)" />
1402 @ <input type="submit" name="load" value="Use This Skin" />
1403 @ </div></form></li>
1404 }
1405 }
@@ -1413,11 +1429,11 @@
1413 const char *zV = db_column_text(&q, 1);
1414 if( fossil_strcmp(zV, zCurrent)==0 ){
1415 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
1416 }else{
1417 @ <li><form action="%s(g.zTop)/setup_skin" method="post">
1418 @ %h(zN).&nbsp;&nbsp;
1419 @ <input type="hidden" name="sn" value="%h(zN)">
1420 @ <input type="submit" name="load" value="Use This Skin">
1421 @ <input type="submit" name="del1" value="Delete This Skin">
1422 @ </form></li>
1423 }
1424
--- src/skins.c
+++ src/skins.c
@@ -24,20 +24,20 @@
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(name,mtime,value)
31 @ VALUES('css',now(),'/* General settings for the entire page */
32 @ body {
33 @ margin: 0ex 1ex;
34 @ padding: 0px;
35 @ background-color: white;
36 @ font-family: sans-serif;
37 @ }
38 @
39 @ /* The project logo in the upper left-hand corner of each page */
40 @ div.logo {
41 @ display: table-row;
42 @ text-align: center;
43 @ /* vertical-align: bottom;*/
@@ -44,12 +44,13 @@
44 @ font-size: 2em;
45 @ font-weight: bold;
46 @ background-color: #707070;
47 @ color: #ffffff;
48 @ min-width: 200px;
49 @ white-space: nowrap;
50 @ }
51 @
52 @ /* The page title centered at the top of each page */
53 @ div.title {
54 @ display: table-cell;
55 @ font-size: 1.5em;
56 @ font-weight: bold;
@@ -57,28 +58,29 @@
58 @ padding: 0 0 0 10px;
59 @ color: #404040;
60 @ vertical-align: bottom;
61 @ width: 100%;
62 @ }
63 @
64 @ /* The login status message in the top right-hand corner */
65 @ div.status {
66 @ display: table-cell;
67 @ text-align: right;
68 @ vertical-align: bottom;
69 @ color: #404040;
70 @ font-size: 0.8em;
71 @ font-weight: bold;
72 @ min-width: 200px;
73 @ white-space: nowrap;
74 @ }
75 @
76 @ /* The header across the top of the page */
77 @ div.header {
78 @ display: table;
79 @ width: 100%;
80 @ }
81 @
82 @ /* The main menu bar that appears at the top of the page beneath
83 @ ** the header */
84 @ div.mainmenu {
85 @ padding: 5px 10px 5px 10px;
86 @ font-size: 0.9em;
@@ -115,43 +117,45 @@
117 @ }
118 @ /* Hyperlink colors */
119 @ div.content a { color: #604000; }
120 @ div.content a:link { color: #604000;}
121 @ div.content a:visited { color: #600000; }
122 @
123 @ /* Some pages have section dividers */
124 @ div.section {
125 @ margin-bottom: 0px;
126 @ margin-top: 1em;
127 @ padding: 1px 1px 1px 1px;
128 @ font-size: 1.2em;
129 @ font-weight: bold;
130 @ background-color: #404040;
131 @ color: white;
132 @ white-space: nowrap;
133 @ }
134 @
135 @ /* The "Date" that occurs on the left hand side of timelines */
136 @ div.divider {
137 @ background: #a0a0a0;
138 @ border: 2px #505050 solid;
139 @ font-size: 1em; font-weight: normal;
140 @ padding: .25em;
141 @ margin: .2em 0 .2em 0;
142 @ float: left;
143 @ clear: left;
144 @ white-space: nowrap;
145 @ }
146 @
147 @ /* The footer at the very bottom of the page */
148 @ div.footer {
149 @ font-size: 0.8em;
150 @ margin-top: 12px;
151 @ padding: 5px 10px 5px 10px;
152 @ text-align: right;
153 @ background-color: #404040;
154 @ color: white;
155 @ }
156 @
157 @ /* The label/value pairs on (for example) the vinfo page */
158 @ table.label-value th {
159 @ vertical-align: top;
160 @ text-align: right;
161 @ padding: 0.2ex 2ex;
@@ -166,17 +170,17 @@
170 @ media="screen">
171 @ </head>
172 @ <body>
173 @ <div class="header">
174 @ <div class="title"><small>$<project_name></small><br />$<title></div>
175 @ <div class="status"><th1>
176 @ if {[info exists login]} {
177 @ puts "Logged in as $login"
178 @ } else {
179 @ puts "Not logged in"
180 @ }
181 @ </th1></div>
182 @ </div>
183 @ <div class="mainmenu">
184 @ <th1>
185 @ html "<a href=''$home$index_page''>Home</a>\n"
186 @ if {[anycap jor]} {
@@ -207,40 +211,41 @@
211 @ }
212 @ </th1></div>
213 @ ');
214 @ REPLACE INTO config(name,mtime,value)
215 @ VALUES('footer',now(),'<div class="footer">
216 @ Fossil version $manifest_version $manifest_date
217 @ </div>
218 @ </body></html>
219 @ ');
220 ;
221
222 /*
223 ** A tan theme with the project title above the user identification
224 ** and no logo image.
225 */
226 static const char zBuiltinSkin2[] =
227 @ REPLACE INTO config(name,mtime,value)
228 @ VALUES('css',now(),'/* General settings for the entire page */
229 @ body {
230 @ margin: 0ex 0ex;
231 @ padding: 0px;
232 @ background-color: #fef3bc;
233 @ font-family: sans-serif;
234 @ }
235 @
236 @ /* The project logo in the upper left-hand corner of each page */
237 @ div.logo {
238 @ display: inline;
239 @ text-align: center;
240 @ vertical-align: bottom;
241 @ font-weight: bold;
242 @ font-size: 2.5em;
243 @ color: #a09048;
244 @ white-space: nowrap;
245 @ }
246 @
247 @ /* The page title centered at the top of each page */
248 @ div.title {
249 @ display: table-cell;
250 @ font-size: 2em;
251 @ font-weight: bold;
@@ -248,28 +253,29 @@
253 @ padding: 0 0 0 5px;
254 @ color: #a09048;
255 @ vertical-align: bottom;
256 @ width: 100%;
257 @ }
258 @
259 @ /* The login status message in the top right-hand corner */
260 @ div.status {
261 @ display: table-cell;
262 @ text-align: right;
263 @ vertical-align: bottom;
264 @ color: #a09048;
265 @ padding: 5px 5px 0 0;
266 @ font-size: 0.8em;
267 @ font-weight: bold;
268 @ white-space: nowrap;
269 @ }
270 @
271 @ /* The header across the top of the page */
272 @ div.header {
273 @ display: table;
274 @ width: 100%;
275 @ }
276 @
277 @ /* The main menu bar that appears at the top of the page beneath
278 @ ** the header */
279 @ div.mainmenu {
280 @ padding: 5px 10px 5px 10px;
281 @ font-size: 0.9em;
@@ -277,11 +283,11 @@
283 @ text-align: center;
284 @ letter-spacing: 1px;
285 @ background-color: #a09048;
286 @ color: black;
287 @ }
288 @
289 @ /* The submenu bar that *sometimes* appears below the main menu */
290 @ div.submenu, div.sectionmenu {
291 @ padding: 3px 10px 3px 0px;
292 @ font-size: 0.9em;
293 @ text-align: center;
@@ -296,65 +302,67 @@
302 @ }
303 @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
304 @ color: #a09048;
305 @ background-color: white;
306 @ }
307 @
308 @ /* All page content from the bottom of the menu or submenu down to
309 @ ** the footer */
310 @ div.content {
311 @ padding: 1ex 5px;
312 @ }
313 @ div.content a { color: #706532; }
314 @ div.content a:link { color: #706532; }
315 @ div.content a:visited { color: #704032; }
316 @ div.content a:hover { background-color: white; color: #706532; }
317 @
318 @ /* Some pages have section dividers */
319 @ div.section {
320 @ margin-bottom: 0px;
321 @ margin-top: 1em;
322 @ padding: 3px 3px 0 3px;
323 @ font-size: 1.2em;
324 @ font-weight: bold;
325 @ background-color: #a09048;
326 @ color: white;
327 @ white-space: nowrap;
328 @ }
329 @
330 @ /* The "Date" that occurs on the left hand side of timelines */
331 @ div.divider {
332 @ background: #e1d498;
333 @ border: 2px #a09048 solid;
334 @ font-size: 1em; font-weight: normal;
335 @ padding: .25em;
336 @ margin: .2em 0 .2em 0;
337 @ float: left;
338 @ clear: left;
339 @ white-space: nowrap;
340 @ }
341 @
342 @ /* The footer at the very bottom of the page */
343 @ div.footer {
344 @ font-size: 0.8em;
345 @ margin-top: 12px;
346 @ padding: 5px 10px 5px 10px;
347 @ text-align: right;
348 @ background-color: #a09048;
349 @ color: white;
350 @ }
351 @
352 @ /* Hyperlink colors */
353 @ div.footer a { color: white; }
354 @ div.footer a:link { color: white; }
355 @ div.footer a:visited { color: white; }
356 @ div.footer a:hover { background-color: white; color: #558195; }
357 @
358 @ /* <verbatim> blocks */
359 @ pre.verbatim {
360 @ background-color: #f5f5f5;
361 @ padding: 0.5em;
362 @ }
363 @
364 @ /* The label/value pairs on (for example) the ci page */
365 @ table.label-value th {
366 @ vertical-align: top;
367 @ text-align: right;
368 @ padding: 0.2ex 2ex;
@@ -370,18 +378,18 @@
378 @ </head>
379 @ <body>
380 @ <div class="header">
381 @ <div class="title">$<title></div>
382 @ <div class="status">
383 @ <div class="logo">$<project_name></div><br/>
384 @ <th1>
385 @ if {[info exists login]} {
386 @ puts "Logged in as $login"
387 @ } else {
388 @ puts "Not logged in"
389 @ }
390 @ </th1></div>
391 @ </div>
392 @ <div class="mainmenu">
393 @ <th1>
394 @ html "<a href=''$home$index_page''>Home</a>\n"
395 @ if {[anycap jor]} {
@@ -422,38 +430,39 @@
430
431 /*
432 ** Black letters on a white or cream background with the main menu
433 ** stuck on the left-hand side.
434 */
435 static const char zBuiltinSkin3[] =
436 @ REPLACE INTO config(name,mtime,value)
437 @ VALUES('css',now(),'/* General settings for the entire page */
438 @ body {
439 @ margin:0px 0px 0px 0px;
440 @ padding:0px;
441 @ font-family:verdana, arial, helvetica, "sans serif";
442 @ color:#333;
443 @ background-color:white;
444 @ }
445 @
446 @ /* consistent colours */
447 @ h2 {
448 @ color: #333;
449 @ }
450 @ h3 {
451 @ color: #333;
452 @ }
453 @
454 @ /* The project logo in the upper left-hand corner of each page */
455 @ div.logo {
456 @ display: table-cell;
457 @ text-align: left;
458 @ vertical-align: bottom;
459 @ font-weight: bold;
460 @ color: #333;
461 @ white-space: nowrap;
462 @ }
463 @
464 @ /* The page title centered at the top of each page */
465 @ div.title {
466 @ display: table-cell;
467 @ font-size: 2em;
468 @ font-weight: bold;
@@ -460,11 +469,11 @@
469 @ text-align: center;
470 @ color: #333;
471 @ vertical-align: bottom;
472 @ width: 100%;
473 @ }
474 @
475 @ /* The login status message in the top right-hand corner */
476 @ div.status {
477 @ display: table-cell;
478 @ padding-right: 10px;
479 @ text-align: right;
@@ -471,22 +480,23 @@
480 @ vertical-align: bottom;
481 @ padding-bottom: 5px;
482 @ color: #333;
483 @ font-size: 0.8em;
484 @ font-weight: bold;
485 @ white-space: nowrap;
486 @ }
487 @
488 @ /* The header across the top of the page */
489 @ div.header {
490 @ margin:10px 0px 10px 0px;
491 @ padding:1px 0px 0px 20px;
492 @ border-style:solid;
493 @ border-color:black;
494 @ border-width:1px 0px;
495 @ background-color:#eee;
496 @ }
497 @
498 @ /* The main menu bar that appears at the top left of the page beneath
499 @ ** the header. Width must be co-ordinated with the container below */
500 @ div.mainmenu {
501 @ float: left;
502 @ margin-left: 10px;
@@ -496,11 +506,11 @@
506 @ padding:5px;
507 @ background-color:#eee;
508 @ border:1px solid #999;
509 @ width:8em;
510 @ }
511 @
512 @ /* Main menu is now a list */
513 @ div.mainmenu ul {
514 @ padding: 0;
515 @ list-style:none;
516 @ }
@@ -511,17 +521,17 @@
521 @ }
522 @ div.mainmenu a:hover {
523 @ color: #eee;
524 @ background-color: #333;
525 @ }
526 @
527 @ /* Container for the sub-menu and content so they don''t spread
528 @ ** out underneath the main menu */
529 @ #container {
530 @ padding-left: 9em;
531 @ }
532 @
533 @ /* The submenu bar that *sometimes* appears below the main menu */
534 @ div.submenu, div.sectionmenu {
535 @ padding: 3px 10px 3px 10px;
536 @ font-size: 0.9em;
537 @ text-align: center;
@@ -538,17 +548,17 @@
548 @ }
549 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
550 @ color: #eee;
551 @ background-color: #333;
552 @ }
553 @
554 @ /* All page content from the bottom of the menu or submenu down to
555 @ ** the footer */
556 @ div.content {
557 @ padding: 2ex 1ex 0ex 2ex;
558 @ }
559 @
560 @ /* Some pages have section dividers */
561 @ div.section {
562 @ margin-bottom: 0px;
563 @ margin-top: 1em;
564 @ padding: 1px 1px 1px 1px;
@@ -557,40 +567,42 @@
567 @ border-style:solid;
568 @ border-color:#999;
569 @ border-width:1px 0px;
570 @ background-color: #eee;
571 @ color: #333;
572 @ white-space: nowrap;
573 @ }
574 @
575 @ /* The "Date" that occurs on the left hand side of timelines */
576 @ div.divider {
577 @ background: #eee;
578 @ border: 2px #999 solid;
579 @ font-size: 1em; font-weight: normal;
580 @ padding: .25em;
581 @ margin: .2em 0 .2em 0;
582 @ float: left;
583 @ clear: left;
584 @ color: #333;
585 @ white-space: nowrap;
586 @ }
587 @
588 @ /* The footer at the very bottom of the page */
589 @ div.footer {
590 @ font-size: 0.8em;
591 @ margin-top: 12px;
592 @ padding: 5px 10px 5px 10px;
593 @ text-align: right;
594 @ background-color: #eee;
595 @ color: #555;
596 @ }
597 @
598 @ /* <verbatim> blocks */
599 @ pre.verbatim {
600 @ background-color: #f5f5f5;
601 @ padding: 0.5em;
602 @ }
603 @
604 @ /* The label/value pairs on (for example) the ci page */
605 @ table.label-value th {
606 @ vertical-align: top;
607 @ text-align: right;
608 @ padding: 0.2ex 2ex;
@@ -606,20 +618,20 @@
618 @ </head>
619 @ <body>
620 @ <div class="header">
621 @ <div class="logo">
622 @ <img src="$home/logo" alt="logo">
623 @ <br />$<project_name>
624 @ </div>
625 @ <div class="title">$<title></div>
626 @ <div class="status"><th1>
627 @ if {[info exists login]} {
628 @ puts "Logged in as $login"
629 @ } else {
630 @ puts "Not logged in"
631 @ }
632 @ </th1></div>
633 @ </div>
634 @ <div class="mainmenu">
635 @ <th1>
636 @ html "<a href=''$home$index_page''>Home</a>\n"
637 @ if {[anycap jor]} {
@@ -661,11 +673,11 @@
673
674
675 /*
676 ** Shadow boxes and rounded corners.
677 */
678 static const char zBuiltinSkin4[] =
679 @ REPLACE INTO config(name,mtime,value)
680 @ VALUES('css',now(),'/* General settings for the entire page */
681 @ html {
682 @ min-height: 100%;
683 @ }
@@ -675,27 +687,28 @@
687 @ background-color: white;
688 @ color: #333;
689 @ font-family: Verdana, sans-serif;
690 @ font-size: 0.8em;
691 @ }
692 @
693 @ /* The project logo in the upper left-hand corner of each page */
694 @ div.logo {
695 @ display: table-cell;
696 @ text-align: right;
697 @ vertical-align: bottom;
698 @ font-weight: normal;
699 @ white-space: nowrap;
700 @ }
701 @
702 @ /* Widths */
703 @ div.header, div.mainmenu, div.submenu, div.content, div.footer {
704 @ max-width: 900px;
705 @ margin: auto;
706 @ padding: 3px 20px 3px 20px;
707 @ clear: both;
708 @ }
709 @
710 @ /* The page title at the top of each page */
711 @ div.title {
712 @ display: table-cell;
713 @ padding-left: 10px;
714 @ font-size: 2em;
@@ -706,20 +719,21 @@
719 @ font-family: Verdana, sans-serif;
720 @ font-weight: bold;
721 @ color: #558195;
722 @ text-shadow: 0px 2px 2px #999999;
723 @ }
724 @
725 @ /* The login status message in the top right-hand corner */
726 @ div.status {
727 @ display: table-cell;
728 @ text-align: right;
729 @ vertical-align: bottom;
730 @ color: #333;
731 @ margin-right: -20px;
732 @ white-space: nowrap;
733 @ }
734 @
735 @ /* The main menu bar that appears at the top of the page beneath
736 @ ** the header */
737 @ div.mainmenu {
738 @ text-align: center;
739 @ color: white;
@@ -729,11 +743,11 @@
743 @ padding-top: 8px;
744 @ padding-bottom: 8px;
745 @ background-color: #446979;
746 @ box-shadow: 0px 3px 4px #333333;
747 @ }
748 @
749 @ /* The submenu bar that *sometimes* appears below the main menu */
750 @ div.submenu {
751 @ padding-top:10px;
752 @ padding-bottom:0;
753 @ text-align: right;
@@ -754,24 +768,24 @@
768 @ color: #000;
769 @ font-family: Arial;
770 @ text-decoration: none;
771 @ margin:auto;
772 @ border-radius: 5px;
773 @ background-color: #e0e0e0;
774 @ text-shadow: 0px -1px 0px #eee;
775 @ border: 1px solid #000;
776 @ }
777 @
778 @ div.mainmenu a:hover {
779 @ color: #000;
780 @ background-color: white;
781 @ }
782 @
783 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
784 @ background-color: #c0c0c0;
785 @ }
786 @
787 @ /* All page content from the bottom of the menu or submenu down to
788 @ ** the footer */
789 @ div.content {
790 @ background-color: #fff;
791 @ box-shadow: 0px 3px 4px #999;
@@ -778,12 +792,12 @@
792 @ border-bottom-right-radius: 5px;
793 @ border-bottom-left-radius: 5px;
794 @ padding-bottom: 1em;
795 @ min-height:40%;
796 @ }
797 @
798 @
799 @ /* Some pages have section dividers */
800 @ div.section {
801 @ margin-bottom: 0.5em;
802 @ margin-top: 1em;
803 @ margin-right: auto;
@@ -793,70 +807,71 @@
807 @ text-align: center;
808 @ color: white;
809 @ border-radius: 5px;
810 @ background-color: #446979;
811 @ box-shadow: 0px 3px 4px #333333;
812 @ white-space: nowrap;
813 @ }
814 @
815 @ /* The "Date" that occurs on the left hand side of timelines */
816 @ div.divider {
817 @ font-size: 1.2em;
818 @ font-family: Georgia, serif;
819 @ font-weight: bold;
820 @ margin-top: 1em;
821 @ white-space: nowrap;
822 @ }
823 @
824 @ /* The footer at the very bottom of the page */
825 @ div.footer {
826 @ font-size: 0.9em;
827 @ text-align: right;
828 @ margin-bottom: 1em;
829 @ color: #666;
830 @ }
831 @
832 @ /* Hyperlink colors in the footer */
833 @ div.footer a { color: white; }
834 @ div.footer a:link { color: white; }
835 @ div.footer a:visited { color: white; }
836 @ div.footer a:hover { background-color: white; color: #558195; }
837 @
838 @ /* <verbatim> blocks */
839 @ pre.verbatim, blockquote pre {
840 @ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
841 @ background-color: #f3f3f3;
842 @ padding: 0.5em;
843 @ white-space: pre-wrap;
844 @ }
845 @
846 @ blockquote pre {
847 @ border: 1px #000 dashed;
848 @ }
849 @
850 @ /* The label/value pairs on (for example) the ci page */
851 @ table.label-value th {
852 @ vertical-align: top;
853 @ text-align: right;
854 @ padding: 0.2ex 2ex;
855 @ }
856 @
857 @
858 @ table.report {
859 @ border-collapse:collapse;
860 @ border: 1px solid #999;
861 @ margin: 1em 0 1em 0;
862 @ }
863 @
864 @ table.report tr th {
865 @ padding: 3px 5px;
866 @ text-transform: capitalize;
867 @ }
868 @
869 @ table.report tr td {
870 @ padding: 3px 5px;
871 @ }
872 @
873 @ textarea {
874 @ font-size: 1em;
875 @ }');
876 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
877 @ <head>
@@ -869,20 +884,20 @@
884 @ </head>
885 @ <body>
886 @ <div class="header">
887 @ <div class="logo">
888 @ <img src="$home/logo" alt="logo">
889 @ <br />$<project_name>
890 @ </div>
891 @ <div class="title">$<title></div>
892 @ <div class="status"><th1>
893 @ if {[info exists login]} {
894 @ puts "Logged in as $login"
895 @ } else {
896 @ puts "Not logged in"
897 @ }
898 @ </th1></div>
899 @ </div>
900 @ <div class="mainmenu">
901 @ <th1>
902 @ html "<a href=''$home$index_page''>Home</a>\n"
903 @ if {[anycap jor]} {
@@ -937,11 +952,11 @@
952 ** Fossil repository. Additionally, if the Tcl integration feature is
953 ** enabled, the loaded version of Tcl is included, with a hyperlink to the
954 ** official Tcl/Tk web site. The footer also contains a TH1 script block
955 ** to help accomplish these tasks.
956 */
957 static const char zBuiltinSkin5[] =
958 @ REPLACE INTO config(name,mtime,value)
959 @ VALUES('css',now(),'/* General settings for the entire page */
960 @ body {
961 @ margin: 0ex 1ex;
962 @ padding: 0px;
@@ -955,10 +970,11 @@
970 @ text-align: center;
971 @ vertical-align: bottom;
972 @ font-weight: bold;
973 @ color: #558195;
974 @ min-width: 200px;
975 @ white-space: nowrap;
976 @ }
977 @
978 @ /* The page title centered at the top of each page */
979 @ div.title {
980 @ display: table-cell;
@@ -966,11 +982,11 @@
982 @ font-weight: bold;
983 @ text-align: center;
984 @ padding: 0 0 0 1em;
985 @ color: #558195;
986 @ vertical-align: bottom;
987 @ width: 100%;
988 @ }
989 @
990 @ /* The login status message in the top right-hand corner */
991 @ div.status {
992 @ display: table-cell;
@@ -984,11 +1000,11 @@
1000 @ }
1001 @
1002 @ /* The header across the top of the page */
1003 @ div.header {
1004 @ display: table;
1005 @ width: 100%;
1006 @ }
1007 @
1008 @ /* The main menu bar that appears at the top of the page beneath
1009 @ ** the header */
1010 @ div.mainmenu {
@@ -1069,11 +1085,11 @@
1085 @ /* Hyperlink colors in the footer */
1086 @ div.footer a { color: white; }
1087 @ div.footer a:link { color: white; }
1088 @ div.footer a:visited { color: white; }
1089 @ div.footer a:hover { background-color: white; color: #558195; }
1090 @
1091 @ /* verbatim blocks */
1092 @ pre.verbatim {
1093 @ background-color: #f5f5f5;
1094 @ padding: 0.5em;
1095 @}
@@ -1159,46 +1175,46 @@
1175 @ <a href="$logourl">
1176 @ <img src="$baseurl/logo" border="0" alt="$project_name">
1177 @ </a>
1178 @ </div>
1179 @ <div class="title"><small>$<project_name></small><br />$<title></div>
1180 @ <div class="status"><th1>
1181 @ if {[info exists login]} {
1182 @ puts "Logged in as $login"
1183 @ } else {
1184 @ puts "Not logged in"
1185 @ }
1186 @ </th1></div>
1187 @ </div>
1188 @ <div class="mainmenu">
1189 @ <th1>
1190 @ html "<a href=''$home$index_page''>Home</a>\n"
1191 @ if {[anycap jor]} {
1192 @ html "<a href=''$home/timeline''>Timeline</a>\n"
1193 @ }
1194 @ if {[hascap oh]} {
1195 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
1196 @ }
1197 @ if {[hascap o]} {
1198 @ html "<a href=''$home/brlist''>Branches</a>\n"
1199 @ html "<a href=''$home/taglist''>Tags</a>\n"
1200 @ }
1201 @ if {[hascap r]} {
1202 @ html "<a href=''$home/reportlist''>Tickets</a>\n"
1203 @ }
1204 @ if {[hascap j]} {
1205 @ html "<a href=''$home/wiki''>Wiki</a>\n"
1206 @ }
1207 @ if {[hascap s]} {
1208 @ html "<a href=''$home/setup''>Admin</a>\n"
1209 @ } elseif {[hascap a]} {
1210 @ html "<a href=''$home/setup_ulist''>Users</a>\n"
1211 @ }
1212 @ if {[info exists login]} {
1213 @ html "<a href=''$home/login''>Logout</a>\n"
1214 @ } else {
1215 @ html "<a href=''$home/login''>Login</a>\n"
1216 @ }
1217 @ </th1></div>
1218 @ ');
1219 @ REPLACE INTO config(name,mtime,value)
1220 @ VALUES('footer',now(),'<div class="footer">
@@ -1381,11 +1397,11 @@
1397 style_header("Skins");
1398 if( zErr ){
1399 @ <p><font color="red">%h(zErr)</font></p>
1400 }
1401 @ <p>A "skin" is a combination of
1402 @ <a href="setup_editcss">CSS</a>,
1403 @ <a href="setup_header">Header</a>,
1404 @ <a href="setup_footer">Footer</a>, and
1405 @ <a href="setup_logo">Logo</a> that determines the look and feel
1406 @ of the web interface.</p>
1407 @
@@ -1395,11 +1411,11 @@
1411 z = aBuiltinSkin[i].zName;
1412 if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
1413 @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
1414 }else{
1415 @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div>
1416 @ %h(z).&nbsp;&nbsp;
1417 @ <input type="hidden" name="sn" value="%h(z)" />
1418 @ <input type="submit" name="load" value="Use This Skin" />
1419 @ </div></form></li>
1420 }
1421 }
@@ -1413,11 +1429,11 @@
1429 const char *zV = db_column_text(&q, 1);
1430 if( fossil_strcmp(zV, zCurrent)==0 ){
1431 @ <li><p>%h(zN).&nbsp;&nbsp; <b>Currently In Use</b></p>
1432 }else{
1433 @ <li><form action="%s(g.zTop)/setup_skin" method="post">
1434 @ %h(zN).&nbsp;&nbsp;
1435 @ <input type="hidden" name="sn" value="%h(zN)">
1436 @ <input type="submit" name="load" value="Use This Skin">
1437 @ <input type="submit" name="del1" value="Delete This Skin">
1438 @ </form></li>
1439 }
1440
+3 -2
--- src/stash.c
+++ src/stash.c
@@ -159,12 +159,13 @@
159159
verify_all_options();
160160
if( zComment==0 ){
161161
Blob prompt; /* Prompt for stash comment */
162162
Blob comment; /* User comment reply */
163163
#ifdef _WIN32
164
- static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
165
- blob_init(&prompt, (const char *) bom, 3);
164
+ int bomSize;
165
+ const unsigned char *bom = get_utf8_bom(&bomSize);
166
+ blob_init(&prompt, (const char *) bom, bomSize);
166167
#else
167168
blob_zero(&prompt);
168169
#endif
169170
blob_append(&prompt,
170171
"\n"
171172
--- src/stash.c
+++ src/stash.c
@@ -159,12 +159,13 @@
159 verify_all_options();
160 if( zComment==0 ){
161 Blob prompt; /* Prompt for stash comment */
162 Blob comment; /* User comment reply */
163 #ifdef _WIN32
164 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
165 blob_init(&prompt, (const char *) bom, 3);
 
166 #else
167 blob_zero(&prompt);
168 #endif
169 blob_append(&prompt,
170 "\n"
171
--- src/stash.c
+++ src/stash.c
@@ -159,12 +159,13 @@
159 verify_all_options();
160 if( zComment==0 ){
161 Blob prompt; /* Prompt for stash comment */
162 Blob comment; /* User comment reply */
163 #ifdef _WIN32
164 int bomSize;
165 const unsigned char *bom = get_utf8_bom(&bomSize);
166 blob_init(&prompt, (const char *) bom, bomSize);
167 #else
168 blob_zero(&prompt);
169 #endif
170 blob_append(&prompt,
171 "\n"
172
+56 -26
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
4747
*/
4848
static int sideboxUsed = 0;
4949
5050
5151
/*
52
-** List of hyperlinks that need to be resolved by javascript in
52
+** List of hyperlinks and forms that need to be resolved by javascript in
5353
** the footer.
5454
*/
5555
char **aHref = 0;
5656
int nHref = 0;
5757
int nHrefAlloc = 0;
58
+char **aFormAction = 0;
59
+int nFormAction = 0;
5860
5961
/*
6062
** Generate and return a anchor tag like this:
6163
**
6264
** <a href="URL">
@@ -64,11 +66,11 @@
6466
**
6567
** The form of the anchor tag is determined by the g.javascriptHyperlink
6668
** variable. The href="URL" form is used if g.javascriptHyperlink is false.
6769
** If g.javascriptHyperlink is true then the
6870
** id="ID" form is used and javascript is generated in the footer to cause
69
-** href values to be inserted after the page has loaded. If
71
+** href values to be inserted after the page has loaded. If
7072
** g.perm.History is false, then the <a id="ID"> form is still
7173
** generated but the javascript is not generated so the links never
7274
** activate.
7375
**
7476
** Filling in the href="URL" using javascript is a defense against bots.
@@ -119,23 +121,49 @@
119121
aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
120122
}
121123
aHref[nHref++] = zUrl;
122124
return mprintf("<a id=%d>", nHref);
123125
}
126
+
127
+/*
128
+** Generate <form method="post" action=ARG>. The ARG value is inserted
129
+** by javascript.
130
+*/
131
+void form_begin(const char *zOtherArgs, const char *zAction, ...){
132
+ char *zLink;
133
+ va_list ap;
134
+ if( zOtherArgs==0 ) zOtherArgs = "";
135
+ va_start(ap, zAction);
136
+ zLink = vmprintf(zAction, ap);
137
+ va_end(ap);
138
+ if( g.perm.Hyperlink && !g.javascriptHyperlink ){
139
+ @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
140
+ }else{
141
+ int n;
142
+ aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
143
+ aFormAction[nFormAction++] = zLink;
144
+ n = nFormAction;
145
+ @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
146
+ }
147
+}
124148
125149
/*
126150
** Generate javascript that will set the href= attribute on all anchors.
127151
*/
128152
void style_resolve_href(void){
129153
int i;
130
- if( !g.perm.Hyperlink || !g.javascriptHyperlink || nHref==0 ) return;
154
+ if( !g.perm.Hyperlink || !g.javascriptHyperlink ) return;
155
+ if( nHref==0 && nFormAction==0 ) return;
131156
@ <script type="text/JavaScript">
132157
@ /* <![CDATA[ */
133158
@ function u(i,h){gebi(i).href=h;}
134159
for(i=0; i<nHref; i++){
135160
@ u(%d(i+1),"%s(aHref[i])");
136161
}
162
+ for(i=0; i<nFormAction; i++){
163
+ @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
164
+ }
137165
@ /* ]]> */
138166
@ </script>
139167
}
140168
141169
/*
@@ -190,21 +218,21 @@
190218
** Draw the header.
191219
*/
192220
void style_header(const char *zTitleFormat, ...){
193221
va_list ap;
194222
char *zTitle;
195
- const char *zHeader = db_get("header", (char*)zDefaultHeader);
223
+ const char *zHeader = db_get("header", (char*)zDefaultHeader);
196224
login_check_credentials();
197225
198226
va_start(ap, zTitleFormat);
199227
zTitle = vmprintf(zTitleFormat, ap);
200228
va_end(ap);
201
-
229
+
202230
cgi_destination(CGI_HEADER);
203231
204232
@ <!DOCTYPE html>
205
-
233
+
206234
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
207235
208236
/* Generate the header up through the main menu */
209237
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
210238
Th_Store("title", zTitle);
@@ -267,11 +295,11 @@
267295
*/
268296
void style_footer(void){
269297
const char *zFooter;
270298
271299
if( !headerHasBeenGenerated ) return;
272
-
300
+
273301
/* Go back and put the submenu at the top of the page. We delay the
274302
** creation of the submenu until the end so that we can add elements
275303
** to the submenu while generating page text.
276304
*/
277305
cgi_destination(CGI_HEADER);
@@ -308,11 +336,11 @@
308336
309337
zFooter = db_get("footer", (char*)zDefaultFooter);
310338
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
311339
Th_Render(zFooter);
312340
if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
313
-
341
+
314342
/* Render trace log if TH1 tracing is enabled. */
315343
if( g.thTrace ){
316344
cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
317345
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
318346
cgi_append_content("</span>\n", -1);
@@ -338,11 +366,11 @@
338366
339367
/* @-comment: // */
340368
/*
341369
** The default page header.
342370
*/
343
-const char zDefaultHeader[] =
371
+const char zDefaultHeader[] =
344372
@ <html>
345373
@ <head>
346374
@ <base href="$baseurl/$current_page" />
347375
@ <title>$<project_name>: $<title></title>
348376
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -366,42 +394,42 @@
366394
@ </div>
367395
@ <div class="mainmenu">
368396
@ <th1>
369397
@ html "<a href='$home$index_page'>Home</a>\n"
370398
@ if {[anycap jor]} {
371
-@ html "<a href='timeline'>Timeline</a>\n"
399
+@ html "<a href='$home/timeline'>Timeline</a>\n"
372400
@ }
373401
@ if {[hascap oh]} {
374
-@ html "<a href='dir?ci=tip'>Files</a>\n"
402
+@ html "<a href='$home/dir?ci=tip'>Files</a>\n"
375403
@ }
376404
@ if {[hascap o]} {
377
-@ html "<a href='brlist'>Branches</a>\n"
378
-@ html "<a href='taglist'>Tags</a>\n"
405
+@ html "<a href='$home/brlist'>Branches</a>\n"
406
+@ html "<a href='$home/taglist'>Tags</a>\n"
379407
@ }
380408
@ if {[hascap r]} {
381
-@ html "<a href='reportlist'>Tickets</a>\n"
409
+@ html "<a href='$home/reportlist'>Tickets</a>\n"
382410
@ }
383411
@ if {[hascap j]} {
384
-@ html "<a href='wiki'>Wiki</a>\n"
412
+@ html "<a href='$home/wiki'>Wiki</a>\n"
385413
@ }
386414
@ if {[hascap s]} {
387
-@ html "<a href='setup'>Admin</a>\n"
415
+@ html "<a href='$home/setup'>Admin</a>\n"
388416
@ } elseif {[hascap a]} {
389
-@ html "<a href='setup_ulist'>Users</a>\n"
417
+@ html "<a href='$home/setup_ulist'>Users</a>\n"
390418
@ }
391419
@ if {[info exists login]} {
392
-@ html "<a href='login'>Logout</a>\n"
420
+@ html "<a href='$home/login'>Logout</a>\n"
393421
@ } else {
394
-@ html "<a href='login'>Login</a>\n"
422
+@ html "<a href='$home/login'>Login</a>\n"
395423
@ }
396424
@ </th1></div>
397425
;
398426
399427
/*
400428
** The default page footer
401429
*/
402
-const char zDefaultFooter[] =
430
+const char zDefaultFooter[] =
403431
@ <div class="footer">
404432
@ Fossil version $release_version $manifest_version $manifest_date
405433
@ </div>
406434
@ </body></html>
407435
;
@@ -411,11 +439,11 @@
411439
** It's assembled by different strings for each class.
412440
** The default css conatains all definitions.
413441
** The style sheet, send to the client only contains the ones,
414442
** not defined in the user defined css.
415443
*/
416
-const char zDefaultCSS[] =
444
+const char zDefaultCSS[] =
417445
@ /* General settings for the entire page */
418446
@ body {
419447
@ margin: 0ex 1ex;
420448
@ padding: 0px;
421449
@ background-color: white;
@@ -428,10 +456,11 @@
428456
@ text-align: center;
429457
@ vertical-align: bottom;
430458
@ font-weight: bold;
431459
@ color: #558195;
432460
@ min-width: 200px;
461
+@ white-space: nowrap;
433462
@ }
434463
@
435464
@ /* The page title centered at the top of each page */
436465
@ div.title {
437466
@ display: table-cell;
@@ -439,11 +468,11 @@
439468
@ font-weight: bold;
440469
@ text-align: center;
441470
@ padding: 0 0 0 1em;
442471
@ color: #558195;
443472
@ vertical-align: bottom;
444
-@ width: 100% ;
473
+@ width: 100%;
445474
@ }
446475
@
447476
@ /* The login status message in the top right-hand corner */
448477
@ div.status {
449478
@ display: table-cell;
@@ -457,11 +486,11 @@
457486
@ }
458487
@
459488
@ /* The header across the top of the page */
460489
@ div.header {
461490
@ display: table;
462
-@ width: 100% ;
491
+@ width: 100%;
463492
@ }
464493
@
465494
@ /* The main menu bar that appears at the top of the page beneath
466495
@ ** the header */
467496
@ div.mainmenu {
@@ -542,11 +571,11 @@
542571
@ /* Hyperlink colors in the footer */
543572
@ div.footer a { color: white; }
544573
@ div.footer a:link { color: white; }
545574
@ div.footer a:visited { color: white; }
546575
@ div.footer a:hover { background-color: white; color: #558195; }
547
-@
576
+@
548577
@ /* verbatim blocks */
549578
@ pre.verbatim {
550579
@ background-color: #f5f5f5;
551580
@ padding: 0.5em;
552581
@}
@@ -659,16 +688,16 @@
659688
@ text-decoration: line-through;
660689
},
661690
{ "table.browser",
662691
"format for the file display table",
663692
@ /* the format for wiki errors */
664
- @ width: 100% ;
693
+ @ width: 100%;
665694
@ border: 0;
666695
},
667696
{ "td.browser",
668697
"format for cells in the file browser",
669
- @ width: 24% ;
698
+ @ width: 24%;
670699
@ vertical-align: top;
671700
},
672701
{ "ul.browser",
673702
"format for the list in the file browser",
674703
@ margin-left: 0.5em;
@@ -683,10 +712,11 @@
683712
@ margin-top: 10px;
684713
},
685714
{ "div.captcha",
686715
"captcha display options",
687716
@ text-align: center;
717
+ @ padding: 1ex;
688718
},
689719
{ "table.captcha",
690720
"format for the layout table, used for the captcha display",
691721
@ margin: auto;
692722
@ padding: 10px;
693723
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
47 */
48 static int sideboxUsed = 0;
49
50
51 /*
52 ** List of hyperlinks that need to be resolved by javascript in
53 ** the footer.
54 */
55 char **aHref = 0;
56 int nHref = 0;
57 int nHrefAlloc = 0;
 
 
58
59 /*
60 ** Generate and return a anchor tag like this:
61 **
62 ** <a href="URL">
@@ -64,11 +66,11 @@
64 **
65 ** The form of the anchor tag is determined by the g.javascriptHyperlink
66 ** variable. The href="URL" form is used if g.javascriptHyperlink is false.
67 ** If g.javascriptHyperlink is true then the
68 ** id="ID" form is used and javascript is generated in the footer to cause
69 ** href values to be inserted after the page has loaded. If
70 ** g.perm.History is false, then the <a id="ID"> form is still
71 ** generated but the javascript is not generated so the links never
72 ** activate.
73 **
74 ** Filling in the href="URL" using javascript is a defense against bots.
@@ -119,23 +121,49 @@
119 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
120 }
121 aHref[nHref++] = zUrl;
122 return mprintf("<a id=%d>", nHref);
123 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125 /*
126 ** Generate javascript that will set the href= attribute on all anchors.
127 */
128 void style_resolve_href(void){
129 int i;
130 if( !g.perm.Hyperlink || !g.javascriptHyperlink || nHref==0 ) return;
 
131 @ <script type="text/JavaScript">
132 @ /* <![CDATA[ */
133 @ function u(i,h){gebi(i).href=h;}
134 for(i=0; i<nHref; i++){
135 @ u(%d(i+1),"%s(aHref[i])");
136 }
 
 
 
137 @ /* ]]> */
138 @ </script>
139 }
140
141 /*
@@ -190,21 +218,21 @@
190 ** Draw the header.
191 */
192 void style_header(const char *zTitleFormat, ...){
193 va_list ap;
194 char *zTitle;
195 const char *zHeader = db_get("header", (char*)zDefaultHeader);
196 login_check_credentials();
197
198 va_start(ap, zTitleFormat);
199 zTitle = vmprintf(zTitleFormat, ap);
200 va_end(ap);
201
202 cgi_destination(CGI_HEADER);
203
204 @ <!DOCTYPE html>
205
206 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
207
208 /* Generate the header up through the main menu */
209 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
210 Th_Store("title", zTitle);
@@ -267,11 +295,11 @@
267 */
268 void style_footer(void){
269 const char *zFooter;
270
271 if( !headerHasBeenGenerated ) return;
272
273 /* Go back and put the submenu at the top of the page. We delay the
274 ** creation of the submenu until the end so that we can add elements
275 ** to the submenu while generating page text.
276 */
277 cgi_destination(CGI_HEADER);
@@ -308,11 +336,11 @@
308
309 zFooter = db_get("footer", (char*)zDefaultFooter);
310 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
311 Th_Render(zFooter);
312 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
313
314 /* Render trace log if TH1 tracing is enabled. */
315 if( g.thTrace ){
316 cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
317 cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
318 cgi_append_content("</span>\n", -1);
@@ -338,11 +366,11 @@
338
339 /* @-comment: // */
340 /*
341 ** The default page header.
342 */
343 const char zDefaultHeader[] =
344 @ <html>
345 @ <head>
346 @ <base href="$baseurl/$current_page" />
347 @ <title>$<project_name>: $<title></title>
348 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -366,42 +394,42 @@
366 @ </div>
367 @ <div class="mainmenu">
368 @ <th1>
369 @ html "<a href='$home$index_page'>Home</a>\n"
370 @ if {[anycap jor]} {
371 @ html "<a href='timeline'>Timeline</a>\n"
372 @ }
373 @ if {[hascap oh]} {
374 @ html "<a href='dir?ci=tip'>Files</a>\n"
375 @ }
376 @ if {[hascap o]} {
377 @ html "<a href='brlist'>Branches</a>\n"
378 @ html "<a href='taglist'>Tags</a>\n"
379 @ }
380 @ if {[hascap r]} {
381 @ html "<a href='reportlist'>Tickets</a>\n"
382 @ }
383 @ if {[hascap j]} {
384 @ html "<a href='wiki'>Wiki</a>\n"
385 @ }
386 @ if {[hascap s]} {
387 @ html "<a href='setup'>Admin</a>\n"
388 @ } elseif {[hascap a]} {
389 @ html "<a href='setup_ulist'>Users</a>\n"
390 @ }
391 @ if {[info exists login]} {
392 @ html "<a href='login'>Logout</a>\n"
393 @ } else {
394 @ html "<a href='login'>Login</a>\n"
395 @ }
396 @ </th1></div>
397 ;
398
399 /*
400 ** The default page footer
401 */
402 const char zDefaultFooter[] =
403 @ <div class="footer">
404 @ Fossil version $release_version $manifest_version $manifest_date
405 @ </div>
406 @ </body></html>
407 ;
@@ -411,11 +439,11 @@
411 ** It's assembled by different strings for each class.
412 ** The default css conatains all definitions.
413 ** The style sheet, send to the client only contains the ones,
414 ** not defined in the user defined css.
415 */
416 const char zDefaultCSS[] =
417 @ /* General settings for the entire page */
418 @ body {
419 @ margin: 0ex 1ex;
420 @ padding: 0px;
421 @ background-color: white;
@@ -428,10 +456,11 @@
428 @ text-align: center;
429 @ vertical-align: bottom;
430 @ font-weight: bold;
431 @ color: #558195;
432 @ min-width: 200px;
 
433 @ }
434 @
435 @ /* The page title centered at the top of each page */
436 @ div.title {
437 @ display: table-cell;
@@ -439,11 +468,11 @@
439 @ font-weight: bold;
440 @ text-align: center;
441 @ padding: 0 0 0 1em;
442 @ color: #558195;
443 @ vertical-align: bottom;
444 @ width: 100% ;
445 @ }
446 @
447 @ /* The login status message in the top right-hand corner */
448 @ div.status {
449 @ display: table-cell;
@@ -457,11 +486,11 @@
457 @ }
458 @
459 @ /* The header across the top of the page */
460 @ div.header {
461 @ display: table;
462 @ width: 100% ;
463 @ }
464 @
465 @ /* The main menu bar that appears at the top of the page beneath
466 @ ** the header */
467 @ div.mainmenu {
@@ -542,11 +571,11 @@
542 @ /* Hyperlink colors in the footer */
543 @ div.footer a { color: white; }
544 @ div.footer a:link { color: white; }
545 @ div.footer a:visited { color: white; }
546 @ div.footer a:hover { background-color: white; color: #558195; }
547 @
548 @ /* verbatim blocks */
549 @ pre.verbatim {
550 @ background-color: #f5f5f5;
551 @ padding: 0.5em;
552 @}
@@ -659,16 +688,16 @@
659 @ text-decoration: line-through;
660 },
661 { "table.browser",
662 "format for the file display table",
663 @ /* the format for wiki errors */
664 @ width: 100% ;
665 @ border: 0;
666 },
667 { "td.browser",
668 "format for cells in the file browser",
669 @ width: 24% ;
670 @ vertical-align: top;
671 },
672 { "ul.browser",
673 "format for the list in the file browser",
674 @ margin-left: 0.5em;
@@ -683,10 +712,11 @@
683 @ margin-top: 10px;
684 },
685 { "div.captcha",
686 "captcha display options",
687 @ text-align: center;
 
688 },
689 { "table.captcha",
690 "format for the layout table, used for the captcha display",
691 @ margin: auto;
692 @ padding: 10px;
693
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
47 */
48 static int sideboxUsed = 0;
49
50
51 /*
52 ** List of hyperlinks and forms that need to be resolved by javascript in
53 ** the footer.
54 */
55 char **aHref = 0;
56 int nHref = 0;
57 int nHrefAlloc = 0;
58 char **aFormAction = 0;
59 int nFormAction = 0;
60
61 /*
62 ** Generate and return a anchor tag like this:
63 **
64 ** <a href="URL">
@@ -64,11 +66,11 @@
66 **
67 ** The form of the anchor tag is determined by the g.javascriptHyperlink
68 ** variable. The href="URL" form is used if g.javascriptHyperlink is false.
69 ** If g.javascriptHyperlink is true then the
70 ** id="ID" form is used and javascript is generated in the footer to cause
71 ** href values to be inserted after the page has loaded. If
72 ** g.perm.History is false, then the <a id="ID"> form is still
73 ** generated but the javascript is not generated so the links never
74 ** activate.
75 **
76 ** Filling in the href="URL" using javascript is a defense against bots.
@@ -119,23 +121,49 @@
121 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
122 }
123 aHref[nHref++] = zUrl;
124 return mprintf("<a id=%d>", nHref);
125 }
126
127 /*
128 ** Generate <form method="post" action=ARG>. The ARG value is inserted
129 ** by javascript.
130 */
131 void form_begin(const char *zOtherArgs, const char *zAction, ...){
132 char *zLink;
133 va_list ap;
134 if( zOtherArgs==0 ) zOtherArgs = "";
135 va_start(ap, zAction);
136 zLink = vmprintf(zAction, ap);
137 va_end(ap);
138 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
139 @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
140 }else{
141 int n;
142 aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
143 aFormAction[nFormAction++] = zLink;
144 n = nFormAction;
145 @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
146 }
147 }
148
149 /*
150 ** Generate javascript that will set the href= attribute on all anchors.
151 */
152 void style_resolve_href(void){
153 int i;
154 if( !g.perm.Hyperlink || !g.javascriptHyperlink ) return;
155 if( nHref==0 && nFormAction==0 ) return;
156 @ <script type="text/JavaScript">
157 @ /* <![CDATA[ */
158 @ function u(i,h){gebi(i).href=h;}
159 for(i=0; i<nHref; i++){
160 @ u(%d(i+1),"%s(aHref[i])");
161 }
162 for(i=0; i<nFormAction; i++){
163 @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
164 }
165 @ /* ]]> */
166 @ </script>
167 }
168
169 /*
@@ -190,21 +218,21 @@
218 ** Draw the header.
219 */
220 void style_header(const char *zTitleFormat, ...){
221 va_list ap;
222 char *zTitle;
223 const char *zHeader = db_get("header", (char*)zDefaultHeader);
224 login_check_credentials();
225
226 va_start(ap, zTitleFormat);
227 zTitle = vmprintf(zTitleFormat, ap);
228 va_end(ap);
229
230 cgi_destination(CGI_HEADER);
231
232 @ <!DOCTYPE html>
233
234 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
235
236 /* Generate the header up through the main menu */
237 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
238 Th_Store("title", zTitle);
@@ -267,11 +295,11 @@
295 */
296 void style_footer(void){
297 const char *zFooter;
298
299 if( !headerHasBeenGenerated ) return;
300
301 /* Go back and put the submenu at the top of the page. We delay the
302 ** creation of the submenu until the end so that we can add elements
303 ** to the submenu while generating page text.
304 */
305 cgi_destination(CGI_HEADER);
@@ -308,11 +336,11 @@
336
337 zFooter = db_get("footer", (char*)zDefaultFooter);
338 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
339 Th_Render(zFooter);
340 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
341
342 /* Render trace log if TH1 tracing is enabled. */
343 if( g.thTrace ){
344 cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
345 cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
346 cgi_append_content("</span>\n", -1);
@@ -338,11 +366,11 @@
366
367 /* @-comment: // */
368 /*
369 ** The default page header.
370 */
371 const char zDefaultHeader[] =
372 @ <html>
373 @ <head>
374 @ <base href="$baseurl/$current_page" />
375 @ <title>$<project_name>: $<title></title>
376 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -366,42 +394,42 @@
394 @ </div>
395 @ <div class="mainmenu">
396 @ <th1>
397 @ html "<a href='$home$index_page'>Home</a>\n"
398 @ if {[anycap jor]} {
399 @ html "<a href='$home/timeline'>Timeline</a>\n"
400 @ }
401 @ if {[hascap oh]} {
402 @ html "<a href='$home/dir?ci=tip'>Files</a>\n"
403 @ }
404 @ if {[hascap o]} {
405 @ html "<a href='$home/brlist'>Branches</a>\n"
406 @ html "<a href='$home/taglist'>Tags</a>\n"
407 @ }
408 @ if {[hascap r]} {
409 @ html "<a href='$home/reportlist'>Tickets</a>\n"
410 @ }
411 @ if {[hascap j]} {
412 @ html "<a href='$home/wiki'>Wiki</a>\n"
413 @ }
414 @ if {[hascap s]} {
415 @ html "<a href='$home/setup'>Admin</a>\n"
416 @ } elseif {[hascap a]} {
417 @ html "<a href='$home/setup_ulist'>Users</a>\n"
418 @ }
419 @ if {[info exists login]} {
420 @ html "<a href='$home/login'>Logout</a>\n"
421 @ } else {
422 @ html "<a href='$home/login'>Login</a>\n"
423 @ }
424 @ </th1></div>
425 ;
426
427 /*
428 ** The default page footer
429 */
430 const char zDefaultFooter[] =
431 @ <div class="footer">
432 @ Fossil version $release_version $manifest_version $manifest_date
433 @ </div>
434 @ </body></html>
435 ;
@@ -411,11 +439,11 @@
439 ** It's assembled by different strings for each class.
440 ** The default css conatains all definitions.
441 ** The style sheet, send to the client only contains the ones,
442 ** not defined in the user defined css.
443 */
444 const char zDefaultCSS[] =
445 @ /* General settings for the entire page */
446 @ body {
447 @ margin: 0ex 1ex;
448 @ padding: 0px;
449 @ background-color: white;
@@ -428,10 +456,11 @@
456 @ text-align: center;
457 @ vertical-align: bottom;
458 @ font-weight: bold;
459 @ color: #558195;
460 @ min-width: 200px;
461 @ white-space: nowrap;
462 @ }
463 @
464 @ /* The page title centered at the top of each page */
465 @ div.title {
466 @ display: table-cell;
@@ -439,11 +468,11 @@
468 @ font-weight: bold;
469 @ text-align: center;
470 @ padding: 0 0 0 1em;
471 @ color: #558195;
472 @ vertical-align: bottom;
473 @ width: 100%;
474 @ }
475 @
476 @ /* The login status message in the top right-hand corner */
477 @ div.status {
478 @ display: table-cell;
@@ -457,11 +486,11 @@
486 @ }
487 @
488 @ /* The header across the top of the page */
489 @ div.header {
490 @ display: table;
491 @ width: 100%;
492 @ }
493 @
494 @ /* The main menu bar that appears at the top of the page beneath
495 @ ** the header */
496 @ div.mainmenu {
@@ -542,11 +571,11 @@
571 @ /* Hyperlink colors in the footer */
572 @ div.footer a { color: white; }
573 @ div.footer a:link { color: white; }
574 @ div.footer a:visited { color: white; }
575 @ div.footer a:hover { background-color: white; color: #558195; }
576 @
577 @ /* verbatim blocks */
578 @ pre.verbatim {
579 @ background-color: #f5f5f5;
580 @ padding: 0.5em;
581 @}
@@ -659,16 +688,16 @@
688 @ text-decoration: line-through;
689 },
690 { "table.browser",
691 "format for the file display table",
692 @ /* the format for wiki errors */
693 @ width: 100%;
694 @ border: 0;
695 },
696 { "td.browser",
697 "format for cells in the file browser",
698 @ width: 24%;
699 @ vertical-align: top;
700 },
701 { "ul.browser",
702 "format for the list in the file browser",
703 @ margin-left: 0.5em;
@@ -683,10 +712,11 @@
712 @ margin-top: 10px;
713 },
714 { "div.captcha",
715 "captcha display options",
716 @ text-align: center;
717 @ padding: 1ex;
718 },
719 { "table.captcha",
720 "format for the layout table, used for the captcha display",
721 @ margin: auto;
722 @ padding: 10px;
723
+34 -32
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
1919
*/
2020
#include "config.h"
2121
#include "sync.h"
2222
#include <assert.h>
2323
24
-#if INTERFACE
25
-/*
26
-** Flags used to determine which direction(s) an autosync goes in.
27
-*/
28
-#define AUTOSYNC_PUSH 1
29
-#define AUTOSYNC_PULL 2
30
-
31
-#endif /* INTERFACE */
32
-
3324
/*
3425
** If the repository is configured for autosyncing, then do an
3526
** autosync. This will be a pull if the argument is true or a push
3627
** if the argument is false.
3728
**
@@ -44,16 +35,16 @@
4435
int rc;
4536
int configSync = 0; /* configuration changes transferred */
4637
if( g.fNoSync ){
4738
return 0;
4839
}
49
- if( flags==AUTOSYNC_PUSH && db_get_boolean("dont-push",0) ){
40
+ if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
5041
return 0;
5142
}
5243
zAutosync = db_get("autosync", 0);
5344
if( zAutosync ){
54
- if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
45
+ if( (flags & SYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
5546
return 0; /* Do not auto-push when autosync=pullonly */
5647
}
5748
if( is_false(zAutosync) ){
5849
return 0; /* Autosync is completely off */
5950
}
@@ -79,13 +70,14 @@
7970
** autosync, or something?
8071
*/
8172
configSync = CONFIGSET_SHUN;
8273
}
8374
#endif
75
+ if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
8476
fossil_print("Autosync: %s\n", g.urlCanonical);
8577
url_enable_proxy("via proxy: ");
86
- rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
78
+ rc = client_sync(flags, configSync, 0);
8779
if( rc ) fossil_warning("Autosync failed");
8880
return rc;
8981
}
9082
9183
/*
@@ -92,17 +84,22 @@
9284
** This routine processes the command-line argument for push, pull,
9385
** and sync. If a command-line argument is given, that is the URL
9486
** of a server to sync against. If no argument is given, use the
9587
** most recently synced URL. Remember the current URL for next time.
9688
*/
97
-static void process_sync_args(int *pConfigSync, int *pPrivate){
89
+static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
9890
const char *zUrl = 0;
9991
const char *zPw = 0;
100
- int configSync = 0;
92
+ unsigned configSync = 0;
10193
int urlOptional = find_option("autourl",0,0)!=0;
10294
g.dontKeepUrl = find_option("once",0,0)!=0;
103
- *pPrivate = find_option("private",0,0)!=0;
95
+ if( find_option("private",0,0)!=0 ){
96
+ *pSyncFlags |= SYNC_PRIVATE;
97
+ }
98
+ if( find_option("verbose","v",0)!=0 ){
99
+ *pSyncFlags |= SYNC_VERBOSE;
100
+ }
104101
url_proxy_options();
105102
db_find_and_open_repository(0, 0);
106103
db_open_config(0);
107104
if( g.argc==2 ){
108105
zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
127124
db_set("last-sync-url", g.urlCanonical, 0);
128125
if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
129126
}
130127
user_select();
131128
if( g.argc==2 ){
132
- fossil_print("Server: %s\n", g.urlCanonical);
129
+ if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
130
+ fossil_print("Sync with %s\n", g.urlCanonical);
131
+ }else if( (*pSyncFlags) & SYNC_PUSH ){
132
+ fossil_print("Push to %s\n", g.urlCanonical);
133
+ }else if( (*pSyncFlags) & SYNC_PULL ){
134
+ fossil_print("Pull from %s\n", g.urlCanonical);
135
+ }
133136
}
134137
url_enable_proxy("via proxy: ");
135
- *pConfigSync = configSync;
138
+ *pConfigFlags |= configSync;
136139
}
137140
138141
/*
139142
** COMMAND: pull
140143
**
@@ -156,14 +159,14 @@
156159
** remote repository.
157160
**
158161
** See also: clone, push, sync, remote-url
159162
*/
160163
void pull_cmd(void){
161
- int syncFlags;
162
- int bPrivate;
163
- process_sync_args(&syncFlags, &bPrivate);
164
- client_sync(0,1,0,bPrivate,syncFlags,0);
164
+ unsigned configFlags = 0;
165
+ unsigned syncFlags = SYNC_PULL;
166
+ process_sync_args(&configFlags, &syncFlags);
167
+ client_sync(syncFlags, configFlags, 0);
165168
}
166169
167170
/*
168171
** COMMAND: push
169172
**
@@ -185,17 +188,17 @@
185188
** remote repository.
186189
**
187190
** See also: clone, pull, sync, remote-url
188191
*/
189192
void push_cmd(void){
190
- int syncFlags;
191
- int bPrivate;
192
- process_sync_args(&syncFlags, &bPrivate);
193
+ unsigned configFlags = 0;
194
+ unsigned syncFlags = SYNC_PUSH;
195
+ process_sync_args(&configFlags, &syncFlags);
193196
if( db_get_boolean("dont-push",0) ){
194197
fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
195198
}
196
- client_sync(1,0,0,bPrivate,0,0);
199
+ client_sync(syncFlags, 0, 0);
197200
}
198201
199202
200203
/*
201204
** COMMAND: sync
@@ -223,17 +226,16 @@
223226
** remote repository.
224227
**
225228
** See also: clone, push, pull, remote-url
226229
*/
227230
void sync_cmd(void){
228
- int syncFlags;
229
- int bPrivate;
230
- int pushFlag = 1;
231
- process_sync_args(&syncFlags, &bPrivate);
232
- if( db_get_boolean("dont-push",0) ) pushFlag = 0;
233
- client_sync(pushFlag,1,0,bPrivate,syncFlags,0);
234
- if( pushFlag==0 ){
231
+ unsigned configFlags = 0;
232
+ unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
233
+ process_sync_args(&configFlags, &syncFlags);
234
+ if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
235
+ client_sync(syncFlags, configFlags, 0);
236
+ if( (syncFlags & SYNC_PUSH)==0 ){
235237
fossil_warning("pull only: the 'dont-push' option is set");
236238
}
237239
}
238240
239241
/*
240242
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
19 */
20 #include "config.h"
21 #include "sync.h"
22 #include <assert.h>
23
24 #if INTERFACE
25 /*
26 ** Flags used to determine which direction(s) an autosync goes in.
27 */
28 #define AUTOSYNC_PUSH 1
29 #define AUTOSYNC_PULL 2
30
31 #endif /* INTERFACE */
32
33 /*
34 ** If the repository is configured for autosyncing, then do an
35 ** autosync. This will be a pull if the argument is true or a push
36 ** if the argument is false.
37 **
@@ -44,16 +35,16 @@
44 int rc;
45 int configSync = 0; /* configuration changes transferred */
46 if( g.fNoSync ){
47 return 0;
48 }
49 if( flags==AUTOSYNC_PUSH && db_get_boolean("dont-push",0) ){
50 return 0;
51 }
52 zAutosync = db_get("autosync", 0);
53 if( zAutosync ){
54 if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
55 return 0; /* Do not auto-push when autosync=pullonly */
56 }
57 if( is_false(zAutosync) ){
58 return 0; /* Autosync is completely off */
59 }
@@ -79,13 +70,14 @@
79 ** autosync, or something?
80 */
81 configSync = CONFIGSET_SHUN;
82 }
83 #endif
 
84 fossil_print("Autosync: %s\n", g.urlCanonical);
85 url_enable_proxy("via proxy: ");
86 rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
87 if( rc ) fossil_warning("Autosync failed");
88 return rc;
89 }
90
91 /*
@@ -92,17 +84,22 @@
92 ** This routine processes the command-line argument for push, pull,
93 ** and sync. If a command-line argument is given, that is the URL
94 ** of a server to sync against. If no argument is given, use the
95 ** most recently synced URL. Remember the current URL for next time.
96 */
97 static void process_sync_args(int *pConfigSync, int *pPrivate){
98 const char *zUrl = 0;
99 const char *zPw = 0;
100 int configSync = 0;
101 int urlOptional = find_option("autourl",0,0)!=0;
102 g.dontKeepUrl = find_option("once",0,0)!=0;
103 *pPrivate = find_option("private",0,0)!=0;
 
 
 
 
 
104 url_proxy_options();
105 db_find_and_open_repository(0, 0);
106 db_open_config(0);
107 if( g.argc==2 ){
108 zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
127 db_set("last-sync-url", g.urlCanonical, 0);
128 if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
129 }
130 user_select();
131 if( g.argc==2 ){
132 fossil_print("Server: %s\n", g.urlCanonical);
 
 
 
 
 
 
133 }
134 url_enable_proxy("via proxy: ");
135 *pConfigSync = configSync;
136 }
137
138 /*
139 ** COMMAND: pull
140 **
@@ -156,14 +159,14 @@
156 ** remote repository.
157 **
158 ** See also: clone, push, sync, remote-url
159 */
160 void pull_cmd(void){
161 int syncFlags;
162 int bPrivate;
163 process_sync_args(&syncFlags, &bPrivate);
164 client_sync(0,1,0,bPrivate,syncFlags,0);
165 }
166
167 /*
168 ** COMMAND: push
169 **
@@ -185,17 +188,17 @@
185 ** remote repository.
186 **
187 ** See also: clone, pull, sync, remote-url
188 */
189 void push_cmd(void){
190 int syncFlags;
191 int bPrivate;
192 process_sync_args(&syncFlags, &bPrivate);
193 if( db_get_boolean("dont-push",0) ){
194 fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
195 }
196 client_sync(1,0,0,bPrivate,0,0);
197 }
198
199
200 /*
201 ** COMMAND: sync
@@ -223,17 +226,16 @@
223 ** remote repository.
224 **
225 ** See also: clone, push, pull, remote-url
226 */
227 void sync_cmd(void){
228 int syncFlags;
229 int bPrivate;
230 int pushFlag = 1;
231 process_sync_args(&syncFlags, &bPrivate);
232 if( db_get_boolean("dont-push",0) ) pushFlag = 0;
233 client_sync(pushFlag,1,0,bPrivate,syncFlags,0);
234 if( pushFlag==0 ){
235 fossil_warning("pull only: the 'dont-push' option is set");
236 }
237 }
238
239 /*
240
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
19 */
20 #include "config.h"
21 #include "sync.h"
22 #include <assert.h>
23
 
 
 
 
 
 
 
 
 
24 /*
25 ** If the repository is configured for autosyncing, then do an
26 ** autosync. This will be a pull if the argument is true or a push
27 ** if the argument is false.
28 **
@@ -44,16 +35,16 @@
35 int rc;
36 int configSync = 0; /* configuration changes transferred */
37 if( g.fNoSync ){
38 return 0;
39 }
40 if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
41 return 0;
42 }
43 zAutosync = db_get("autosync", 0);
44 if( zAutosync ){
45 if( (flags & SYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
46 return 0; /* Do not auto-push when autosync=pullonly */
47 }
48 if( is_false(zAutosync) ){
49 return 0; /* Autosync is completely off */
50 }
@@ -79,13 +70,14 @@
70 ** autosync, or something?
71 */
72 configSync = CONFIGSET_SHUN;
73 }
74 #endif
75 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
76 fossil_print("Autosync: %s\n", g.urlCanonical);
77 url_enable_proxy("via proxy: ");
78 rc = client_sync(flags, configSync, 0);
79 if( rc ) fossil_warning("Autosync failed");
80 return rc;
81 }
82
83 /*
@@ -92,17 +84,22 @@
84 ** This routine processes the command-line argument for push, pull,
85 ** and sync. If a command-line argument is given, that is the URL
86 ** of a server to sync against. If no argument is given, use the
87 ** most recently synced URL. Remember the current URL for next time.
88 */
89 static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
90 const char *zUrl = 0;
91 const char *zPw = 0;
92 unsigned configSync = 0;
93 int urlOptional = find_option("autourl",0,0)!=0;
94 g.dontKeepUrl = find_option("once",0,0)!=0;
95 if( find_option("private",0,0)!=0 ){
96 *pSyncFlags |= SYNC_PRIVATE;
97 }
98 if( find_option("verbose","v",0)!=0 ){
99 *pSyncFlags |= SYNC_VERBOSE;
100 }
101 url_proxy_options();
102 db_find_and_open_repository(0, 0);
103 db_open_config(0);
104 if( g.argc==2 ){
105 zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
124 db_set("last-sync-url", g.urlCanonical, 0);
125 if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
126 }
127 user_select();
128 if( g.argc==2 ){
129 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
130 fossil_print("Sync with %s\n", g.urlCanonical);
131 }else if( (*pSyncFlags) & SYNC_PUSH ){
132 fossil_print("Push to %s\n", g.urlCanonical);
133 }else if( (*pSyncFlags) & SYNC_PULL ){
134 fossil_print("Pull from %s\n", g.urlCanonical);
135 }
136 }
137 url_enable_proxy("via proxy: ");
138 *pConfigFlags |= configSync;
139 }
140
141 /*
142 ** COMMAND: pull
143 **
@@ -156,14 +159,14 @@
159 ** remote repository.
160 **
161 ** See also: clone, push, sync, remote-url
162 */
163 void pull_cmd(void){
164 unsigned configFlags = 0;
165 unsigned syncFlags = SYNC_PULL;
166 process_sync_args(&configFlags, &syncFlags);
167 client_sync(syncFlags, configFlags, 0);
168 }
169
170 /*
171 ** COMMAND: push
172 **
@@ -185,17 +188,17 @@
188 ** remote repository.
189 **
190 ** See also: clone, pull, sync, remote-url
191 */
192 void push_cmd(void){
193 unsigned configFlags = 0;
194 unsigned syncFlags = SYNC_PUSH;
195 process_sync_args(&configFlags, &syncFlags);
196 if( db_get_boolean("dont-push",0) ){
197 fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
198 }
199 client_sync(syncFlags, 0, 0);
200 }
201
202
203 /*
204 ** COMMAND: sync
@@ -223,17 +226,16 @@
226 ** remote repository.
227 **
228 ** See also: clone, push, pull, remote-url
229 */
230 void sync_cmd(void){
231 unsigned configFlags = 0;
232 unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
233 process_sync_args(&configFlags, &syncFlags);
234 if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
235 client_sync(syncFlags, configFlags, 0);
236 if( (syncFlags & SYNC_PUSH)==0 ){
 
237 fossil_warning("pull only: the 'dont-push' option is set");
238 }
239 }
240
241 /*
242
+133 -18
--- src/th_main.c
+++ src/th_main.c
@@ -77,14 +77,33 @@
7777
if( argc!=2 ){
7878
return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
7979
}
8080
return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
8181
}
82
+
83
+/*
84
+** Return a name for a TH1 return code.
85
+*/
86
+const char *Th_ReturnCodeName(int rc){
87
+ static char zRc[32];
88
+ switch( rc ){
89
+ case TH_OK: return "TH_OK";
90
+ case TH_ERROR: return "TH_ERROR";
91
+ case TH_BREAK: return "TH_BREAK";
92
+ case TH_RETURN: return "TH_RETURN";
93
+ case TH_CONTINUE: return "TH_CONTINUE";
94
+ default: {
95
+ sqlite3_snprintf(sizeof(zRc),zRc,"return code %d",rc);
96
+ }
97
+ }
98
+ return zRc;
99
+}
82100
83101
/*
84102
** Send text to the appropriate output: Either to the console
85
-** or to the CGI reply buffer.
103
+** or to the CGI reply buffer. Escape all characters with special
104
+** meaning to HTML if the encode parameter is true.
86105
*/
87106
static void sendText(const char *z, int n, int encode){
88107
if( enableOutput && n ){
89108
if( n<0 ) n = strlen(z);
90109
if( encode ){
@@ -98,16 +117,25 @@
98117
fflush(stdout);
99118
}
100119
if( encode ) free((char*)z);
101120
}
102121
}
122
+
123
+static void sendError(const char *z, int n, int forceCgi){
124
+ if( forceCgi || g.cgiOutput ){
125
+ sendText("<hr><p class=\"thmainError\">", -1, 0);
126
+ }
127
+ sendText("ERROR: ", -1, 0);
128
+ sendText((char*)z, n, 1);
129
+ sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
130
+}
103131
104132
/*
105133
** TH command: puts STRING
106134
** TH command: html STRING
107135
**
108
-** Output STRING as HTML (html) or unchanged (puts).
136
+** Output STRING escaped for HTML (html) or unchanged (puts).
109137
*/
110138
static int putsCmd(
111139
Th_Interp *interp,
112140
void *pConvert,
113141
int argc,
@@ -115,11 +143,11 @@
115143
int *argl
116144
){
117145
if( argc!=2 ){
118146
return Th_WrongNumArgs(interp, "puts STRING");
119147
}
120
- sendText((char*)argv[1], argl[1], pConvert!=0);
148
+ sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
121149
return TH_OK;
122150
}
123151
124152
/*
125153
** TH command: wiki STRING
@@ -131,17 +159,18 @@
131159
void *p,
132160
int argc,
133161
const char **argv,
134162
int *argl
135163
){
164
+ int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
136165
if( argc!=2 ){
137166
return Th_WrongNumArgs(interp, "wiki STRING");
138167
}
139168
if( enableOutput ){
140169
Blob src;
141170
blob_init(&src, (char*)argv[1], argl[1]);
142
- wiki_convert(&src, 0, WIKI_INLINE);
171
+ wiki_convert(&src, 0, flags);
143172
blob_reset(&src);
144173
}
145174
return TH_OK;
146175
}
147176
@@ -503,69 +532,157 @@
503532
sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
504533
Th_SetResult(interp, zUTime, -1);
505534
return TH_OK;
506535
}
507536
537
+
538
+/*
539
+** TH1 command: randhex N
540
+**
541
+** Return N*2 random hexadecimal digits with N<50. If N is omitted,
542
+** use a value of 10.
543
+*/
544
+static int randhexCmd(
545
+ Th_Interp *interp,
546
+ void *p,
547
+ int argc,
548
+ const char **argv,
549
+ int *argl
550
+){
551
+ int n;
552
+ unsigned char aRand[50];
553
+ unsigned char zOut[100];
554
+ if( argc!=1 && argc!=2 ){
555
+ return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
556
+ }
557
+ if( argc==2 ){
558
+ if( Th_ToInt(interp, argv[1], argl[1], &n) ){
559
+ return TH_ERROR;
560
+ }
561
+ if( n<1 ) n = 1;
562
+ if( n>sizeof(aRand) ) n = sizeof(aRand);
563
+ }else{
564
+ n = 10;
565
+ }
566
+ sqlite3_randomness(n, aRand);
567
+ encode16(aRand, zOut, n);
568
+ Th_SetResult(interp, (const char *)zOut, -1);
569
+ return TH_OK;
570
+}
571
+
508572
509573
/*
510574
** Make sure the interpreter has been initialized. Initialize it if
511575
** it has not been already.
512576
**
513577
** The interpreter is stored in the g.interp global variable.
514578
*/
515
-void Th_FossilInit(void){
579
+void Th_FossilInit(int needConfig, int forceSetup){
580
+ int wasInit = 0;
581
+ static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY };
516582
static struct _Command {
517583
const char *zName;
518584
Th_CommandProc xProc;
519585
void *pContext;
520586
} aCommand[] = {
521587
{"anycap", anycapCmd, 0},
522588
{"combobox", comboboxCmd, 0},
589
+ {"date", dateCmd, 0},
590
+ {"decorate", wikiCmd, (void*)&aFlags[2]},
523591
{"enable_output", enableOutputCmd, 0},
524
- {"linecount", linecntCmd, 0},
525592
{"hascap", hascapCmd, 0},
526593
{"hasfeature", hasfeatureCmd, 0},
594
+ {"html", putsCmd, (void*)&aFlags[0]},
527595
{"htmlize", htmlizeCmd, 0},
528
- {"date", dateCmd, 0},
529
- {"html", putsCmd, 0},
530
- {"puts", putsCmd, (void*)1},
531
- {"wiki", wikiCmd, 0},
596
+ {"linecount", linecntCmd, 0},
597
+ {"puts", putsCmd, (void*)&aFlags[1]},
598
+ {"randhex", randhexCmd, 0},
532599
{"repository", repositoryCmd, 0},
533
- {"utime", utimeCmd, 0},
534600
{"stime", stimeCmd, 0},
601
+ {"utime", utimeCmd, 0},
602
+ {"wiki", wikiCmd, (void*)&aFlags[0]},
535603
{0, 0, 0}
536604
};
605
+ if( needConfig ){
606
+ /*
607
+ ** This function uses several settings which may be defined in the
608
+ ** repository and/or the global configuration. Since the caller
609
+ ** passed a non-zero value for the needConfig parameter, make sure
610
+ ** the necessary database connections are open prior to continuing.
611
+ */
612
+ db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
613
+ db_open_config(0);
614
+ }
537615
if( g.interp==0 ){
538616
int i;
539617
g.interp = Th_CreateInterp(&vtab);
540618
th_register_language(g.interp); /* Basic scripting commands. */
541619
#ifdef FOSSIL_ENABLE_TCL
542620
if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
543
- g.tcl.setup = db_get("tcl-setup", 0); /* Grab optional setup script. */
621
+ if( !g.tcl.setup ){
622
+ g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
623
+ }
544624
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
545625
}
546626
#endif
547627
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
548628
if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
549629
Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
550630
aCommand[i].pContext, 0);
551631
}
632
+ }else{
633
+ wasInit = 1;
634
+ }
635
+ if( forceSetup || !wasInit ){
636
+ int rc = TH_OK;
637
+ if( !g.th1Setup ){
638
+ g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
639
+ }
640
+ if( g.th1Setup ){
641
+ rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
642
+ if( rc==TH_ERROR ){
643
+ int nResult = 0;
644
+ char *zResult = (char*)Th_GetResult(g.interp, &nResult);
645
+ sendError(zResult, nResult, 0);
646
+ }
647
+ }
648
+ if( g.thTrace ){
649
+ Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
650
+ Th_ReturnCodeName(rc));
651
+ }
552652
}
553653
}
554654
555655
/*
556656
** Store a string value in a variable in the interpreter.
557657
*/
558658
void Th_Store(const char *zName, const char *zValue){
559
- Th_FossilInit();
659
+ Th_FossilInit(0, 0);
560660
if( zValue ){
561661
if( g.thTrace ){
562662
Th_Trace("set %h {%h}<br />\n", zName, zValue);
563663
}
564664
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
565665
}
566666
}
667
+
668
+/*
669
+** Store an integer value in a variable in the interpreter.
670
+*/
671
+void Th_StoreInt(const char *zName, int iValue){
672
+ Blob value;
673
+ char *zValue;
674
+ Th_FossilInit(0, 0);
675
+ blob_zero(&value);
676
+ blob_appendf(&value, "%d", iValue);
677
+ zValue = blob_str(&value);
678
+ if( g.thTrace ){
679
+ Th_Trace("set %h {%h}<br />\n", zName, zValue);
680
+ }
681
+ Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
682
+ blob_reset(&value);
683
+}
567684
568685
/*
569686
** Unset a variable.
570687
*/
571688
void Th_Unstore(const char *zName){
@@ -578,11 +695,11 @@
578695
** Retrieve a string value from the interpreter. If no such
579696
** variable exists, return NULL.
580697
*/
581698
char *Th_Fetch(const char *zName, int *pSize){
582699
int rc;
583
- Th_FossilInit();
700
+ Th_FossilInit(0, 0);
584701
rc = Th_GetVar(g.interp, (char*)zName, -1);
585702
if( rc==TH_OK ){
586703
return (char*)Th_GetResult(g.interp, pSize);
587704
}else{
588705
return 0;
@@ -658,11 +775,11 @@
658775
int Th_Render(const char *z){
659776
int i = 0;
660777
int n;
661778
int rc = TH_OK;
662779
char *zResult;
663
- Th_FossilInit();
780
+ Th_FossilInit(0, 0);
664781
while( z[i] ){
665782
if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
666783
const char *zVar;
667784
int nVar;
668785
int encode = 1;
@@ -694,14 +811,12 @@
694811
}else{
695812
i++;
696813
}
697814
}
698815
if( rc==TH_ERROR ){
699
- sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0);
700816
zResult = (char*)Th_GetResult(g.interp, &n);
701
- sendText((char*)zResult, n, 1);
702
- sendText("</p>", -1, 0);
817
+ sendError(zResult, n, 1);
703818
}else{
704819
sendText(z, i, 0);
705820
}
706821
return rc;
707822
}
708823
--- src/th_main.c
+++ src/th_main.c
@@ -77,14 +77,33 @@
77 if( argc!=2 ){
78 return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
79 }
80 return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
81 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
83 /*
84 ** Send text to the appropriate output: Either to the console
85 ** or to the CGI reply buffer.
 
86 */
87 static void sendText(const char *z, int n, int encode){
88 if( enableOutput && n ){
89 if( n<0 ) n = strlen(z);
90 if( encode ){
@@ -98,16 +117,25 @@
98 fflush(stdout);
99 }
100 if( encode ) free((char*)z);
101 }
102 }
 
 
 
 
 
 
 
 
 
103
104 /*
105 ** TH command: puts STRING
106 ** TH command: html STRING
107 **
108 ** Output STRING as HTML (html) or unchanged (puts).
109 */
110 static int putsCmd(
111 Th_Interp *interp,
112 void *pConvert,
113 int argc,
@@ -115,11 +143,11 @@
115 int *argl
116 ){
117 if( argc!=2 ){
118 return Th_WrongNumArgs(interp, "puts STRING");
119 }
120 sendText((char*)argv[1], argl[1], pConvert!=0);
121 return TH_OK;
122 }
123
124 /*
125 ** TH command: wiki STRING
@@ -131,17 +159,18 @@
131 void *p,
132 int argc,
133 const char **argv,
134 int *argl
135 ){
 
136 if( argc!=2 ){
137 return Th_WrongNumArgs(interp, "wiki STRING");
138 }
139 if( enableOutput ){
140 Blob src;
141 blob_init(&src, (char*)argv[1], argl[1]);
142 wiki_convert(&src, 0, WIKI_INLINE);
143 blob_reset(&src);
144 }
145 return TH_OK;
146 }
147
@@ -503,69 +532,157 @@
503 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
504 Th_SetResult(interp, zUTime, -1);
505 return TH_OK;
506 }
507
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
509 /*
510 ** Make sure the interpreter has been initialized. Initialize it if
511 ** it has not been already.
512 **
513 ** The interpreter is stored in the g.interp global variable.
514 */
515 void Th_FossilInit(void){
 
 
516 static struct _Command {
517 const char *zName;
518 Th_CommandProc xProc;
519 void *pContext;
520 } aCommand[] = {
521 {"anycap", anycapCmd, 0},
522 {"combobox", comboboxCmd, 0},
 
 
523 {"enable_output", enableOutputCmd, 0},
524 {"linecount", linecntCmd, 0},
525 {"hascap", hascapCmd, 0},
526 {"hasfeature", hasfeatureCmd, 0},
 
527 {"htmlize", htmlizeCmd, 0},
528 {"date", dateCmd, 0},
529 {"html", putsCmd, 0},
530 {"puts", putsCmd, (void*)1},
531 {"wiki", wikiCmd, 0},
532 {"repository", repositoryCmd, 0},
533 {"utime", utimeCmd, 0},
534 {"stime", stimeCmd, 0},
 
 
535 {0, 0, 0}
536 };
 
 
 
 
 
 
 
 
 
 
537 if( g.interp==0 ){
538 int i;
539 g.interp = Th_CreateInterp(&vtab);
540 th_register_language(g.interp); /* Basic scripting commands. */
541 #ifdef FOSSIL_ENABLE_TCL
542 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
543 g.tcl.setup = db_get("tcl-setup", 0); /* Grab optional setup script. */
 
 
544 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
545 }
546 #endif
547 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
548 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
549 Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
550 aCommand[i].pContext, 0);
551 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552 }
553 }
554
555 /*
556 ** Store a string value in a variable in the interpreter.
557 */
558 void Th_Store(const char *zName, const char *zValue){
559 Th_FossilInit();
560 if( zValue ){
561 if( g.thTrace ){
562 Th_Trace("set %h {%h}<br />\n", zName, zValue);
563 }
564 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
565 }
566 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
568 /*
569 ** Unset a variable.
570 */
571 void Th_Unstore(const char *zName){
@@ -578,11 +695,11 @@
578 ** Retrieve a string value from the interpreter. If no such
579 ** variable exists, return NULL.
580 */
581 char *Th_Fetch(const char *zName, int *pSize){
582 int rc;
583 Th_FossilInit();
584 rc = Th_GetVar(g.interp, (char*)zName, -1);
585 if( rc==TH_OK ){
586 return (char*)Th_GetResult(g.interp, pSize);
587 }else{
588 return 0;
@@ -658,11 +775,11 @@
658 int Th_Render(const char *z){
659 int i = 0;
660 int n;
661 int rc = TH_OK;
662 char *zResult;
663 Th_FossilInit();
664 while( z[i] ){
665 if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
666 const char *zVar;
667 int nVar;
668 int encode = 1;
@@ -694,14 +811,12 @@
694 }else{
695 i++;
696 }
697 }
698 if( rc==TH_ERROR ){
699 sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0);
700 zResult = (char*)Th_GetResult(g.interp, &n);
701 sendText((char*)zResult, n, 1);
702 sendText("</p>", -1, 0);
703 }else{
704 sendText(z, i, 0);
705 }
706 return rc;
707 }
708
--- src/th_main.c
+++ src/th_main.c
@@ -77,14 +77,33 @@
77 if( argc!=2 ){
78 return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
79 }
80 return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
81 }
82
83 /*
84 ** Return a name for a TH1 return code.
85 */
86 const char *Th_ReturnCodeName(int rc){
87 static char zRc[32];
88 switch( rc ){
89 case TH_OK: return "TH_OK";
90 case TH_ERROR: return "TH_ERROR";
91 case TH_BREAK: return "TH_BREAK";
92 case TH_RETURN: return "TH_RETURN";
93 case TH_CONTINUE: return "TH_CONTINUE";
94 default: {
95 sqlite3_snprintf(sizeof(zRc),zRc,"return code %d",rc);
96 }
97 }
98 return zRc;
99 }
100
101 /*
102 ** Send text to the appropriate output: Either to the console
103 ** or to the CGI reply buffer. Escape all characters with special
104 ** meaning to HTML if the encode parameter is true.
105 */
106 static void sendText(const char *z, int n, int encode){
107 if( enableOutput && n ){
108 if( n<0 ) n = strlen(z);
109 if( encode ){
@@ -98,16 +117,25 @@
117 fflush(stdout);
118 }
119 if( encode ) free((char*)z);
120 }
121 }
122
123 static void sendError(const char *z, int n, int forceCgi){
124 if( forceCgi || g.cgiOutput ){
125 sendText("<hr><p class=\"thmainError\">", -1, 0);
126 }
127 sendText("ERROR: ", -1, 0);
128 sendText((char*)z, n, 1);
129 sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
130 }
131
132 /*
133 ** TH command: puts STRING
134 ** TH command: html STRING
135 **
136 ** Output STRING escaped for HTML (html) or unchanged (puts).
137 */
138 static int putsCmd(
139 Th_Interp *interp,
140 void *pConvert,
141 int argc,
@@ -115,11 +143,11 @@
143 int *argl
144 ){
145 if( argc!=2 ){
146 return Th_WrongNumArgs(interp, "puts STRING");
147 }
148 sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
149 return TH_OK;
150 }
151
152 /*
153 ** TH command: wiki STRING
@@ -131,17 +159,18 @@
159 void *p,
160 int argc,
161 const char **argv,
162 int *argl
163 ){
164 int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
165 if( argc!=2 ){
166 return Th_WrongNumArgs(interp, "wiki STRING");
167 }
168 if( enableOutput ){
169 Blob src;
170 blob_init(&src, (char*)argv[1], argl[1]);
171 wiki_convert(&src, 0, flags);
172 blob_reset(&src);
173 }
174 return TH_OK;
175 }
176
@@ -503,69 +532,157 @@
532 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
533 Th_SetResult(interp, zUTime, -1);
534 return TH_OK;
535 }
536
537
538 /*
539 ** TH1 command: randhex N
540 **
541 ** Return N*2 random hexadecimal digits with N<50. If N is omitted,
542 ** use a value of 10.
543 */
544 static int randhexCmd(
545 Th_Interp *interp,
546 void *p,
547 int argc,
548 const char **argv,
549 int *argl
550 ){
551 int n;
552 unsigned char aRand[50];
553 unsigned char zOut[100];
554 if( argc!=1 && argc!=2 ){
555 return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
556 }
557 if( argc==2 ){
558 if( Th_ToInt(interp, argv[1], argl[1], &n) ){
559 return TH_ERROR;
560 }
561 if( n<1 ) n = 1;
562 if( n>sizeof(aRand) ) n = sizeof(aRand);
563 }else{
564 n = 10;
565 }
566 sqlite3_randomness(n, aRand);
567 encode16(aRand, zOut, n);
568 Th_SetResult(interp, (const char *)zOut, -1);
569 return TH_OK;
570 }
571
572
573 /*
574 ** Make sure the interpreter has been initialized. Initialize it if
575 ** it has not been already.
576 **
577 ** The interpreter is stored in the g.interp global variable.
578 */
579 void Th_FossilInit(int needConfig, int forceSetup){
580 int wasInit = 0;
581 static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY };
582 static struct _Command {
583 const char *zName;
584 Th_CommandProc xProc;
585 void *pContext;
586 } aCommand[] = {
587 {"anycap", anycapCmd, 0},
588 {"combobox", comboboxCmd, 0},
589 {"date", dateCmd, 0},
590 {"decorate", wikiCmd, (void*)&aFlags[2]},
591 {"enable_output", enableOutputCmd, 0},
 
592 {"hascap", hascapCmd, 0},
593 {"hasfeature", hasfeatureCmd, 0},
594 {"html", putsCmd, (void*)&aFlags[0]},
595 {"htmlize", htmlizeCmd, 0},
596 {"linecount", linecntCmd, 0},
597 {"puts", putsCmd, (void*)&aFlags[1]},
598 {"randhex", randhexCmd, 0},
 
599 {"repository", repositoryCmd, 0},
 
600 {"stime", stimeCmd, 0},
601 {"utime", utimeCmd, 0},
602 {"wiki", wikiCmd, (void*)&aFlags[0]},
603 {0, 0, 0}
604 };
605 if( needConfig ){
606 /*
607 ** This function uses several settings which may be defined in the
608 ** repository and/or the global configuration. Since the caller
609 ** passed a non-zero value for the needConfig parameter, make sure
610 ** the necessary database connections are open prior to continuing.
611 */
612 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
613 db_open_config(0);
614 }
615 if( g.interp==0 ){
616 int i;
617 g.interp = Th_CreateInterp(&vtab);
618 th_register_language(g.interp); /* Basic scripting commands. */
619 #ifdef FOSSIL_ENABLE_TCL
620 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
621 if( !g.tcl.setup ){
622 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
623 }
624 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
625 }
626 #endif
627 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
628 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
629 Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
630 aCommand[i].pContext, 0);
631 }
632 }else{
633 wasInit = 1;
634 }
635 if( forceSetup || !wasInit ){
636 int rc = TH_OK;
637 if( !g.th1Setup ){
638 g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
639 }
640 if( g.th1Setup ){
641 rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
642 if( rc==TH_ERROR ){
643 int nResult = 0;
644 char *zResult = (char*)Th_GetResult(g.interp, &nResult);
645 sendError(zResult, nResult, 0);
646 }
647 }
648 if( g.thTrace ){
649 Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
650 Th_ReturnCodeName(rc));
651 }
652 }
653 }
654
655 /*
656 ** Store a string value in a variable in the interpreter.
657 */
658 void Th_Store(const char *zName, const char *zValue){
659 Th_FossilInit(0, 0);
660 if( zValue ){
661 if( g.thTrace ){
662 Th_Trace("set %h {%h}<br />\n", zName, zValue);
663 }
664 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
665 }
666 }
667
668 /*
669 ** Store an integer value in a variable in the interpreter.
670 */
671 void Th_StoreInt(const char *zName, int iValue){
672 Blob value;
673 char *zValue;
674 Th_FossilInit(0, 0);
675 blob_zero(&value);
676 blob_appendf(&value, "%d", iValue);
677 zValue = blob_str(&value);
678 if( g.thTrace ){
679 Th_Trace("set %h {%h}<br />\n", zName, zValue);
680 }
681 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
682 blob_reset(&value);
683 }
684
685 /*
686 ** Unset a variable.
687 */
688 void Th_Unstore(const char *zName){
@@ -578,11 +695,11 @@
695 ** Retrieve a string value from the interpreter. If no such
696 ** variable exists, return NULL.
697 */
698 char *Th_Fetch(const char *zName, int *pSize){
699 int rc;
700 Th_FossilInit(0, 0);
701 rc = Th_GetVar(g.interp, (char*)zName, -1);
702 if( rc==TH_OK ){
703 return (char*)Th_GetResult(g.interp, pSize);
704 }else{
705 return 0;
@@ -658,11 +775,11 @@
775 int Th_Render(const char *z){
776 int i = 0;
777 int n;
778 int rc = TH_OK;
779 char *zResult;
780 Th_FossilInit(0, 0);
781 while( z[i] ){
782 if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
783 const char *zVar;
784 int nVar;
785 int encode = 1;
@@ -694,14 +811,12 @@
811 }else{
812 i++;
813 }
814 }
815 if( rc==TH_ERROR ){
 
816 zResult = (char*)Th_GetResult(g.interp, &n);
817 sendError(zResult, n, 1);
 
818 }else{
819 sendText(z, i, 0);
820 }
821 return rc;
822 }
823
+56 -22
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
8585
** Generate a hyperlink to a user. This will link to a timeline showing
8686
** events by that user. If the date+time is specified, then the timeline
8787
** is centered on that date+time.
8888
*/
8989
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
90
+ if( zU==0 || zU[0]==0 ) zU = "anonymous";
9091
if( zSuf==0 ) zSuf = "";
9192
if( g.perm.Hyperlink ){
9293
if( zD && zD[0] ){
9394
@ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
9495
}else{
@@ -191,12 +192,10 @@
191192
int tmFlags, /* Flags controlling display behavior */
192193
const char *zThisUser, /* Suppress links to this user */
193194
const char *zThisTag, /* Suppress links to this tag */
194195
void (*xExtra)(int) /* Routine to call on each line of display */
195196
){
196
- int wikiFlags;
197
- int plainText;
198197
int mxWikiLen;
199198
Blob comment;
200199
int prevTagid = 0;
201200
int suppressCnt = 0;
202201
char zPrevDate[20];
@@ -206,17 +205,11 @@
206205
Stmt fchngQuery; /* Query for file changes on check-ins */
207206
static Stmt qbranch;
208207
int pendingEndTr = 0; /* True if a </td></tr> is needed */
209208
210209
zPrevDate[0] = 0;
211
- plainText = db_get_int("timeline-plaintext", 0);
212210
mxWikiLen = db_get_int("timeline-max-comment", 0);
213
- if( db_get_boolean("timeline-block-markup", 0) ){
214
- wikiFlags = WIKI_INLINE;
215
- }else{
216
- wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
217
- }
218211
if( tmFlags & TIMELINE_GRAPH ){
219212
pGraph = graph_init();
220213
/* style is not moved to css, because this is
221214
** a technical div for the timeline graph
222215
*/
@@ -237,10 +230,11 @@
237230
const char *zDate = db_column_text(pQuery, 2);
238231
const char *zType = db_column_text(pQuery, 7);
239232
const char *zUser = db_column_text(pQuery, 4);
240233
const char *zTagList = db_column_text(pQuery, 8);
241234
int tagid = db_column_int(pQuery, 9);
235
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
242236
const char *zBr = 0; /* Branch */
243237
int commentColumn = 3; /* Column containing comment text */
244238
int modPending; /* Pending moderation */
245239
char zTime[8];
246240
@@ -257,11 +251,11 @@
257251
}
258252
}
259253
prevTagid = tagid;
260254
if( suppressCnt ){
261255
@ <span class="timelineDisabled">... %d(suppressCnt) similar
262
- @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
256
+ @ event%s(suppressCnt>1?"s":"") omitted.</span>
263257
suppressCnt = 0;
264258
}
265259
if( pendingEndTr ){
266260
@ </td></tr>
267261
pendingEndTr = 0;
@@ -355,32 +349,26 @@
355349
if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
356350
Blob truncated;
357351
blob_zero(&truncated);
358352
blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
359353
blob_append(&truncated, "...", 3);
360
- if( plainText ){
361
- @ %h(blob_str(&truncated))
362
- }else{
363
- wiki_convert(&truncated, 0, wikiFlags);
364
- }
354
+ @ %w(blob_str(&truncated))
365355
blob_reset(&truncated);
366
- }else if( plainText ){
367
- @ %h(blob_str(&comment));
368356
}else{
369
- wiki_convert(&comment, 0, wikiFlags);
357
+ @ %w(blob_str(&comment))
370358
}
371359
blob_reset(&comment);
372360
373361
/* Generate the "user: USERNAME" at the end of the comment, together
374362
** with a hyperlink to another timeline for that user.
375363
*/
376364
if( zTagList && zTagList[0]==0 ) zTagList = 0;
377
- if( g.perm.Hyperlink && fossil_strcmp(zUser, zThisUser)!=0 ){
378
- char *zLink = mprintf("%R/timeline?u=%h&c=%t&amp;nd", zUser, zDate);
379
- @ (user: %z(href("%z",zLink))%h(zUser)</a>%s(zTagList?",":"\051")
365
+ if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
366
+ char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
367
+ @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
380368
}else{
381
- @ (user: %h(zUser)%s(zTagList?",":"\051")
369
+ @ (user: %h(zDispUser)%s(zTagList?",":"\051")
382370
}
383371
384372
/* Generate a "detail" link for tags. */
385373
if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
386374
@ [%z(href("%R/info/%S",zUuid))details</a>]
@@ -477,11 +465,11 @@
477465
}
478466
pendingEndTr = 1;
479467
}
480468
if( suppressCnt ){
481469
@ <span class="timelineDisabled">... %d(suppressCnt) similar
482
- @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
470
+ @ event%s(suppressCnt>1?"s":"") omitted.</span>
483471
suppressCnt = 0;
484472
}
485473
if( pendingEndTr ){
486474
@ </td></tr>
487475
}
@@ -878,10 +866,34 @@
878866
rDate+ONE_SECOND
879867
);
880868
fossil_free(zToDel);
881869
}
882870
871
+/*
872
+** Return all possible names for file zUuid.
873
+*/
874
+char *names_of_file(const char *zUuid){
875
+ Stmt q;
876
+ Blob out;
877
+ const char *zSep = "";
878
+ db_prepare(&q,
879
+ "SELECT DISTINCT filename.name FROM mlink, filename"
880
+ " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid='%s')"
881
+ " AND filename.fnid=mlink.fnid",
882
+ zUuid
883
+ );
884
+ blob_zero(&out);
885
+ while( db_step(&q)==SQLITE_ROW ){
886
+ const char *zFN = db_column_text(&q, 0);
887
+ blob_appendf(&out, "%s%z%h</a>", zSep,
888
+ href("%R/finfo?name=%t", zFN), zFN);
889
+ zSep = " or ";
890
+ }
891
+ db_finalize(&q);
892
+ return blob_str(&out);
893
+}
894
+
883895
884896
/*
885897
** WEBPAGE: timeline
886898
**
887899
** Query parameters:
@@ -930,10 +942,11 @@
930942
const char *zBefore = P("b"); /* Events before this time */
931943
const char *zCirca = P("c"); /* Events near this time */
932944
const char *zTagName = P("t"); /* Show events with this tag */
933945
const char *zBrName = P("r"); /* Show events related to this tag */
934946
const char *zSearch = P("s"); /* Search string */
947
+ const char *zUses = P("uf"); /* Only show checkins hold this file */
935948
int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */
936949
int tagid; /* Tag ID */
937950
int tmFlags; /* Timeline flags */
938951
const char *zThisTag = 0; /* Suppress links to this tag */
939952
const char *zThisUser = 0; /* Suppress links to this user */
@@ -981,10 +994,22 @@
981994
url_add_parameter(&url, "brbg", 0);
982995
}
983996
if( P("ubg")!=0 ){
984997
tmFlags |= TIMELINE_UCOLOR;
985998
url_add_parameter(&url, "ubg", 0);
999
+ }
1000
+ if( zUses!=0 ){
1001
+ int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1002
+ if( ufid ){
1003
+ zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1004
+ url_add_parameter(&url, "uf", zUses);
1005
+ db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
1006
+ compute_uses_file("usesfile", ufid, 0);
1007
+ zType = "ci";
1008
+ }else{
1009
+ zUses = 0;
1010
+ }
9861011
}
9871012
9881013
style_header("Timeline");
9891014
login_anonymous_available();
9901015
timeline_temp_table();
@@ -1085,10 +1110,13 @@
10851110
int n;
10861111
const char *zEType = "timeline item";
10871112
char *zDate;
10881113
char *zNEntry = mprintf("%d", nEntry);
10891114
url_add_parameter(&url, "n", zNEntry);
1115
+ if( zUses ){
1116
+ blob_appendf(&sql, " AND event.objid IN usesfile ");
1117
+ }
10901118
if( tagid>0 ){
10911119
blob_appendf(&sql,
10921120
"AND (EXISTS(SELECT 1 FROM tagxref"
10931121
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);
10941122
@@ -1220,10 +1248,16 @@
12201248
if( zAfter==0 && zBefore==0 && zCirca==0 ){
12211249
blob_appendf(&desc, "%d most recent %ss", n, zEType);
12221250
}else{
12231251
blob_appendf(&desc, "%d %ss", n, zEType);
12241252
}
1253
+ if( zUses ){
1254
+ char *zFilenames = names_of_file(zUses);
1255
+ blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames,
1256
+ href("%R/artifact/%S",zUses), zUses);
1257
+ tmFlags |= TIMELINE_DISJOINT;
1258
+ }
12251259
if( zUser ){
12261260
blob_appendf(&desc, " by user %h", zUser);
12271261
tmFlags |= TIMELINE_DISJOINT;
12281262
}
12291263
if( zTagName ){
12301264
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
85 ** Generate a hyperlink to a user. This will link to a timeline showing
86 ** events by that user. If the date+time is specified, then the timeline
87 ** is centered on that date+time.
88 */
89 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
 
90 if( zSuf==0 ) zSuf = "";
91 if( g.perm.Hyperlink ){
92 if( zD && zD[0] ){
93 @ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
94 }else{
@@ -191,12 +192,10 @@
191 int tmFlags, /* Flags controlling display behavior */
192 const char *zThisUser, /* Suppress links to this user */
193 const char *zThisTag, /* Suppress links to this tag */
194 void (*xExtra)(int) /* Routine to call on each line of display */
195 ){
196 int wikiFlags;
197 int plainText;
198 int mxWikiLen;
199 Blob comment;
200 int prevTagid = 0;
201 int suppressCnt = 0;
202 char zPrevDate[20];
@@ -206,17 +205,11 @@
206 Stmt fchngQuery; /* Query for file changes on check-ins */
207 static Stmt qbranch;
208 int pendingEndTr = 0; /* True if a </td></tr> is needed */
209
210 zPrevDate[0] = 0;
211 plainText = db_get_int("timeline-plaintext", 0);
212 mxWikiLen = db_get_int("timeline-max-comment", 0);
213 if( db_get_boolean("timeline-block-markup", 0) ){
214 wikiFlags = WIKI_INLINE;
215 }else{
216 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
217 }
218 if( tmFlags & TIMELINE_GRAPH ){
219 pGraph = graph_init();
220 /* style is not moved to css, because this is
221 ** a technical div for the timeline graph
222 */
@@ -237,10 +230,11 @@
237 const char *zDate = db_column_text(pQuery, 2);
238 const char *zType = db_column_text(pQuery, 7);
239 const char *zUser = db_column_text(pQuery, 4);
240 const char *zTagList = db_column_text(pQuery, 8);
241 int tagid = db_column_int(pQuery, 9);
 
242 const char *zBr = 0; /* Branch */
243 int commentColumn = 3; /* Column containing comment text */
244 int modPending; /* Pending moderation */
245 char zTime[8];
246
@@ -257,11 +251,11 @@
257 }
258 }
259 prevTagid = tagid;
260 if( suppressCnt ){
261 @ <span class="timelineDisabled">... %d(suppressCnt) similar
262 @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
263 suppressCnt = 0;
264 }
265 if( pendingEndTr ){
266 @ </td></tr>
267 pendingEndTr = 0;
@@ -355,32 +349,26 @@
355 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
356 Blob truncated;
357 blob_zero(&truncated);
358 blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
359 blob_append(&truncated, "...", 3);
360 if( plainText ){
361 @ %h(blob_str(&truncated))
362 }else{
363 wiki_convert(&truncated, 0, wikiFlags);
364 }
365 blob_reset(&truncated);
366 }else if( plainText ){
367 @ %h(blob_str(&comment));
368 }else{
369 wiki_convert(&comment, 0, wikiFlags);
370 }
371 blob_reset(&comment);
372
373 /* Generate the "user: USERNAME" at the end of the comment, together
374 ** with a hyperlink to another timeline for that user.
375 */
376 if( zTagList && zTagList[0]==0 ) zTagList = 0;
377 if( g.perm.Hyperlink && fossil_strcmp(zUser, zThisUser)!=0 ){
378 char *zLink = mprintf("%R/timeline?u=%h&c=%t&amp;nd", zUser, zDate);
379 @ (user: %z(href("%z",zLink))%h(zUser)</a>%s(zTagList?",":"\051")
380 }else{
381 @ (user: %h(zUser)%s(zTagList?",":"\051")
382 }
383
384 /* Generate a "detail" link for tags. */
385 if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
386 @ [%z(href("%R/info/%S",zUuid))details</a>]
@@ -477,11 +465,11 @@
477 }
478 pendingEndTr = 1;
479 }
480 if( suppressCnt ){
481 @ <span class="timelineDisabled">... %d(suppressCnt) similar
482 @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
483 suppressCnt = 0;
484 }
485 if( pendingEndTr ){
486 @ </td></tr>
487 }
@@ -878,10 +866,34 @@
878 rDate+ONE_SECOND
879 );
880 fossil_free(zToDel);
881 }
882
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
884 /*
885 ** WEBPAGE: timeline
886 **
887 ** Query parameters:
@@ -930,10 +942,11 @@
930 const char *zBefore = P("b"); /* Events before this time */
931 const char *zCirca = P("c"); /* Events near this time */
932 const char *zTagName = P("t"); /* Show events with this tag */
933 const char *zBrName = P("r"); /* Show events related to this tag */
934 const char *zSearch = P("s"); /* Search string */
 
935 int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */
936 int tagid; /* Tag ID */
937 int tmFlags; /* Timeline flags */
938 const char *zThisTag = 0; /* Suppress links to this tag */
939 const char *zThisUser = 0; /* Suppress links to this user */
@@ -981,10 +994,22 @@
981 url_add_parameter(&url, "brbg", 0);
982 }
983 if( P("ubg")!=0 ){
984 tmFlags |= TIMELINE_UCOLOR;
985 url_add_parameter(&url, "ubg", 0);
 
 
 
 
 
 
 
 
 
 
 
 
986 }
987
988 style_header("Timeline");
989 login_anonymous_available();
990 timeline_temp_table();
@@ -1085,10 +1110,13 @@
1085 int n;
1086 const char *zEType = "timeline item";
1087 char *zDate;
1088 char *zNEntry = mprintf("%d", nEntry);
1089 url_add_parameter(&url, "n", zNEntry);
 
 
 
1090 if( tagid>0 ){
1091 blob_appendf(&sql,
1092 "AND (EXISTS(SELECT 1 FROM tagxref"
1093 " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);
1094
@@ -1220,10 +1248,16 @@
1220 if( zAfter==0 && zBefore==0 && zCirca==0 ){
1221 blob_appendf(&desc, "%d most recent %ss", n, zEType);
1222 }else{
1223 blob_appendf(&desc, "%d %ss", n, zEType);
1224 }
 
 
 
 
 
 
1225 if( zUser ){
1226 blob_appendf(&desc, " by user %h", zUser);
1227 tmFlags |= TIMELINE_DISJOINT;
1228 }
1229 if( zTagName ){
1230
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
85 ** Generate a hyperlink to a user. This will link to a timeline showing
86 ** events by that user. If the date+time is specified, then the timeline
87 ** is centered on that date+time.
88 */
89 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
90 if( zU==0 || zU[0]==0 ) zU = "anonymous";
91 if( zSuf==0 ) zSuf = "";
92 if( g.perm.Hyperlink ){
93 if( zD && zD[0] ){
94 @ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
95 }else{
@@ -191,12 +192,10 @@
192 int tmFlags, /* Flags controlling display behavior */
193 const char *zThisUser, /* Suppress links to this user */
194 const char *zThisTag, /* Suppress links to this tag */
195 void (*xExtra)(int) /* Routine to call on each line of display */
196 ){
 
 
197 int mxWikiLen;
198 Blob comment;
199 int prevTagid = 0;
200 int suppressCnt = 0;
201 char zPrevDate[20];
@@ -206,17 +205,11 @@
205 Stmt fchngQuery; /* Query for file changes on check-ins */
206 static Stmt qbranch;
207 int pendingEndTr = 0; /* True if a </td></tr> is needed */
208
209 zPrevDate[0] = 0;
 
210 mxWikiLen = db_get_int("timeline-max-comment", 0);
 
 
 
 
 
211 if( tmFlags & TIMELINE_GRAPH ){
212 pGraph = graph_init();
213 /* style is not moved to css, because this is
214 ** a technical div for the timeline graph
215 */
@@ -237,10 +230,11 @@
230 const char *zDate = db_column_text(pQuery, 2);
231 const char *zType = db_column_text(pQuery, 7);
232 const char *zUser = db_column_text(pQuery, 4);
233 const char *zTagList = db_column_text(pQuery, 8);
234 int tagid = db_column_int(pQuery, 9);
235 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
236 const char *zBr = 0; /* Branch */
237 int commentColumn = 3; /* Column containing comment text */
238 int modPending; /* Pending moderation */
239 char zTime[8];
240
@@ -257,11 +251,11 @@
251 }
252 }
253 prevTagid = tagid;
254 if( suppressCnt ){
255 @ <span class="timelineDisabled">... %d(suppressCnt) similar
256 @ event%s(suppressCnt>1?"s":"") omitted.</span>
257 suppressCnt = 0;
258 }
259 if( pendingEndTr ){
260 @ </td></tr>
261 pendingEndTr = 0;
@@ -355,32 +349,26 @@
349 if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
350 Blob truncated;
351 blob_zero(&truncated);
352 blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
353 blob_append(&truncated, "...", 3);
354 @ %w(blob_str(&truncated))
 
 
 
 
355 blob_reset(&truncated);
 
 
356 }else{
357 @ %w(blob_str(&comment))
358 }
359 blob_reset(&comment);
360
361 /* Generate the "user: USERNAME" at the end of the comment, together
362 ** with a hyperlink to another timeline for that user.
363 */
364 if( zTagList && zTagList[0]==0 ) zTagList = 0;
365 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
366 char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
367 @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
368 }else{
369 @ (user: %h(zDispUser)%s(zTagList?",":"\051")
370 }
371
372 /* Generate a "detail" link for tags. */
373 if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
374 @ [%z(href("%R/info/%S",zUuid))details</a>]
@@ -477,11 +465,11 @@
465 }
466 pendingEndTr = 1;
467 }
468 if( suppressCnt ){
469 @ <span class="timelineDisabled">... %d(suppressCnt) similar
470 @ event%s(suppressCnt>1?"s":"") omitted.</span>
471 suppressCnt = 0;
472 }
473 if( pendingEndTr ){
474 @ </td></tr>
475 }
@@ -878,10 +866,34 @@
866 rDate+ONE_SECOND
867 );
868 fossil_free(zToDel);
869 }
870
871 /*
872 ** Return all possible names for file zUuid.
873 */
874 char *names_of_file(const char *zUuid){
875 Stmt q;
876 Blob out;
877 const char *zSep = "";
878 db_prepare(&q,
879 "SELECT DISTINCT filename.name FROM mlink, filename"
880 " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid='%s')"
881 " AND filename.fnid=mlink.fnid",
882 zUuid
883 );
884 blob_zero(&out);
885 while( db_step(&q)==SQLITE_ROW ){
886 const char *zFN = db_column_text(&q, 0);
887 blob_appendf(&out, "%s%z%h</a>", zSep,
888 href("%R/finfo?name=%t", zFN), zFN);
889 zSep = " or ";
890 }
891 db_finalize(&q);
892 return blob_str(&out);
893 }
894
895
896 /*
897 ** WEBPAGE: timeline
898 **
899 ** Query parameters:
@@ -930,10 +942,11 @@
942 const char *zBefore = P("b"); /* Events before this time */
943 const char *zCirca = P("c"); /* Events near this time */
944 const char *zTagName = P("t"); /* Show events with this tag */
945 const char *zBrName = P("r"); /* Show events related to this tag */
946 const char *zSearch = P("s"); /* Search string */
947 const char *zUses = P("uf"); /* Only show checkins hold this file */
948 int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */
949 int tagid; /* Tag ID */
950 int tmFlags; /* Timeline flags */
951 const char *zThisTag = 0; /* Suppress links to this tag */
952 const char *zThisUser = 0; /* Suppress links to this user */
@@ -981,10 +994,22 @@
994 url_add_parameter(&url, "brbg", 0);
995 }
996 if( P("ubg")!=0 ){
997 tmFlags |= TIMELINE_UCOLOR;
998 url_add_parameter(&url, "ubg", 0);
999 }
1000 if( zUses!=0 ){
1001 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1002 if( ufid ){
1003 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1004 url_add_parameter(&url, "uf", zUses);
1005 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
1006 compute_uses_file("usesfile", ufid, 0);
1007 zType = "ci";
1008 }else{
1009 zUses = 0;
1010 }
1011 }
1012
1013 style_header("Timeline");
1014 login_anonymous_available();
1015 timeline_temp_table();
@@ -1085,10 +1110,13 @@
1110 int n;
1111 const char *zEType = "timeline item";
1112 char *zDate;
1113 char *zNEntry = mprintf("%d", nEntry);
1114 url_add_parameter(&url, "n", zNEntry);
1115 if( zUses ){
1116 blob_appendf(&sql, " AND event.objid IN usesfile ");
1117 }
1118 if( tagid>0 ){
1119 blob_appendf(&sql,
1120 "AND (EXISTS(SELECT 1 FROM tagxref"
1121 " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);
1122
@@ -1220,10 +1248,16 @@
1248 if( zAfter==0 && zBefore==0 && zCirca==0 ){
1249 blob_appendf(&desc, "%d most recent %ss", n, zEType);
1250 }else{
1251 blob_appendf(&desc, "%d %ss", n, zEType);
1252 }
1253 if( zUses ){
1254 char *zFilenames = names_of_file(zUses);
1255 blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames,
1256 href("%R/artifact/%S",zUses), zUses);
1257 tmFlags |= TIMELINE_DISJOINT;
1258 }
1259 if( zUser ){
1260 blob_appendf(&desc, " by user %h", zUser);
1261 tmFlags |= TIMELINE_DISJOINT;
1262 }
1263 if( zTagName ){
1264
+15 -9
--- src/tkt.c
+++ src/tkt.c
@@ -238,21 +238,21 @@
238238
/*
239239
** Create the subscript interpreter and load the "common" code.
240240
*/
241241
void ticket_init(void){
242242
const char *zConfig;
243
- Th_FossilInit();
243
+ Th_FossilInit(0, 0);
244244
zConfig = ticket_common_code();
245245
Th_Eval(g.interp, 0, zConfig, -1);
246246
}
247247
248248
/*
249249
** Create the subscript interpreter and load the "change" code.
250250
*/
251251
int ticket_change(void){
252252
const char *zConfig;
253
- Th_FossilInit();
253
+ Th_FossilInit(0, 0);
254254
zConfig = ticket_change_code();
255255
return Th_Eval(g.interp, 0, zConfig, -1);
256256
}
257257
258258
/*
@@ -430,10 +430,14 @@
430430
int i;
431431
int nJ = 0;
432432
Blob tktchng, cksum;
433433
434434
login_verify_csrf_secret();
435
+ if( !captcha_is_correct() ){
436
+ @ <p class="generalError">Error: Incorrect security code.</p>
437
+ return TH_OK;
438
+ }
435439
zUuid = (const char *)pUuid;
436440
blob_zero(&tktchng);
437441
zDate = date_in_standard_format("now");
438442
blob_appendf(&tktchng, "D %s\n", zDate);
439443
free(zDate);
@@ -522,26 +526,27 @@
522526
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
523527
ticket_init();
524528
getAllTicketFields();
525529
initializeVariablesFromDb();
526530
initializeVariablesFromCGI();
527
- @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
531
+ form_begin(0, "%R/%s", g.zPath);
528532
login_insert_csrf_secret();
529533
if( P("date_override") && g.perm.Setup ){
530534
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
531535
}
532536
@ </p>
533537
zScript = ticket_newpage_code();
534
- Th_Store("login", g.zLogin);
538
+ Th_Store("login", g.zLogin ? g.zLogin : "nobody");
535539
Th_Store("date", db_text(0, "SELECT datetime('now')"));
536540
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
537541
(void*)&zNewUuid, 0);
538542
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
539543
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
540544
cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
541545
return;
542546
}
547
+ captcha_generate();
543548
@ </form>
544549
if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
545550
style_footer();
546551
}
547552
@@ -591,24 +596,25 @@
591596
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
592597
ticket_init();
593598
getAllTicketFields();
594599
initializeVariablesFromCGI();
595600
initializeVariablesFromDb();
596
- @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
601
+ form_begin(0, "%R/%s", g.zPath);
597602
@ <input type="hidden" name="name" value="%s(zName)" />
598603
login_insert_csrf_secret();
599604
@ </p>
600605
zScript = ticket_editpage_code();
601
- Th_Store("login", g.zLogin);
606
+ Th_Store("login", g.zLogin ? g.zLogin : "nobody");
602607
Th_Store("date", db_text(0, "SELECT datetime('now')"));
603608
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
604609
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
605610
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
606611
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
607612
cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
608613
return;
609614
}
615
+ captcha_generate();
610616
@ </form>
611617
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
612618
style_footer();
613619
}
614620
@@ -829,15 +835,15 @@
829835
const char *z;
830836
z = pTkt->aField[i].zName;
831837
blob_set(&val, pTkt->aField[i].zValue);
832838
if( z[0]=='+' ){
833839
@ <li>Appended to %h(&z[1]):<blockquote>
834
- wiki_convert(&val, 0, 0);
840
+ wiki_convert(&val, 0, WIKI_NOBADLINKS);
835841
@ </blockquote></li>
836842
}else if( blob_size(&val)<=50 && contains_newline(&val) ){
837843
@ <li>Change %h(z) to:<blockquote>
838
- wiki_convert(&val, 0, 0);
844
+ wiki_convert(&val, 0, WIKI_NOBADLINKS);
839845
@ </blockquote></li>
840846
}else{
841847
@ <li>Change %h(z) to "%h(blob_str(&val))"</li>
842848
}
843849
blob_reset(&val);
@@ -1006,11 +1012,11 @@
10061012
eCmd = history;
10071013
}else{
10081014
eCmd = set;
10091015
}
10101016
if( g.argc==3 ){
1011
- usage("set TICKETUUID");
1017
+ usage("set|change|history TICKETUUID");
10121018
}
10131019
zTktUuid = db_text(0,
10141020
"SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
10151021
);
10161022
if( !zTktUuid ){
10171023
--- src/tkt.c
+++ src/tkt.c
@@ -238,21 +238,21 @@
238 /*
239 ** Create the subscript interpreter and load the "common" code.
240 */
241 void ticket_init(void){
242 const char *zConfig;
243 Th_FossilInit();
244 zConfig = ticket_common_code();
245 Th_Eval(g.interp, 0, zConfig, -1);
246 }
247
248 /*
249 ** Create the subscript interpreter and load the "change" code.
250 */
251 int ticket_change(void){
252 const char *zConfig;
253 Th_FossilInit();
254 zConfig = ticket_change_code();
255 return Th_Eval(g.interp, 0, zConfig, -1);
256 }
257
258 /*
@@ -430,10 +430,14 @@
430 int i;
431 int nJ = 0;
432 Blob tktchng, cksum;
433
434 login_verify_csrf_secret();
 
 
 
 
435 zUuid = (const char *)pUuid;
436 blob_zero(&tktchng);
437 zDate = date_in_standard_format("now");
438 blob_appendf(&tktchng, "D %s\n", zDate);
439 free(zDate);
@@ -522,26 +526,27 @@
522 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
523 ticket_init();
524 getAllTicketFields();
525 initializeVariablesFromDb();
526 initializeVariablesFromCGI();
527 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
528 login_insert_csrf_secret();
529 if( P("date_override") && g.perm.Setup ){
530 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
531 }
532 @ </p>
533 zScript = ticket_newpage_code();
534 Th_Store("login", g.zLogin);
535 Th_Store("date", db_text(0, "SELECT datetime('now')"));
536 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
537 (void*)&zNewUuid, 0);
538 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
539 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
540 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
541 return;
542 }
 
543 @ </form>
544 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
545 style_footer();
546 }
547
@@ -591,24 +596,25 @@
591 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
592 ticket_init();
593 getAllTicketFields();
594 initializeVariablesFromCGI();
595 initializeVariablesFromDb();
596 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
597 @ <input type="hidden" name="name" value="%s(zName)" />
598 login_insert_csrf_secret();
599 @ </p>
600 zScript = ticket_editpage_code();
601 Th_Store("login", g.zLogin);
602 Th_Store("date", db_text(0, "SELECT datetime('now')"));
603 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
604 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
605 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
606 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
607 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
608 return;
609 }
 
610 @ </form>
611 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
612 style_footer();
613 }
614
@@ -829,15 +835,15 @@
829 const char *z;
830 z = pTkt->aField[i].zName;
831 blob_set(&val, pTkt->aField[i].zValue);
832 if( z[0]=='+' ){
833 @ <li>Appended to %h(&z[1]):<blockquote>
834 wiki_convert(&val, 0, 0);
835 @ </blockquote></li>
836 }else if( blob_size(&val)<=50 && contains_newline(&val) ){
837 @ <li>Change %h(z) to:<blockquote>
838 wiki_convert(&val, 0, 0);
839 @ </blockquote></li>
840 }else{
841 @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
842 }
843 blob_reset(&val);
@@ -1006,11 +1012,11 @@
1006 eCmd = history;
1007 }else{
1008 eCmd = set;
1009 }
1010 if( g.argc==3 ){
1011 usage("set TICKETUUID");
1012 }
1013 zTktUuid = db_text(0,
1014 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1015 );
1016 if( !zTktUuid ){
1017
--- src/tkt.c
+++ src/tkt.c
@@ -238,21 +238,21 @@
238 /*
239 ** Create the subscript interpreter and load the "common" code.
240 */
241 void ticket_init(void){
242 const char *zConfig;
243 Th_FossilInit(0, 0);
244 zConfig = ticket_common_code();
245 Th_Eval(g.interp, 0, zConfig, -1);
246 }
247
248 /*
249 ** Create the subscript interpreter and load the "change" code.
250 */
251 int ticket_change(void){
252 const char *zConfig;
253 Th_FossilInit(0, 0);
254 zConfig = ticket_change_code();
255 return Th_Eval(g.interp, 0, zConfig, -1);
256 }
257
258 /*
@@ -430,10 +430,14 @@
430 int i;
431 int nJ = 0;
432 Blob tktchng, cksum;
433
434 login_verify_csrf_secret();
435 if( !captcha_is_correct() ){
436 @ <p class="generalError">Error: Incorrect security code.</p>
437 return TH_OK;
438 }
439 zUuid = (const char *)pUuid;
440 blob_zero(&tktchng);
441 zDate = date_in_standard_format("now");
442 blob_appendf(&tktchng, "D %s\n", zDate);
443 free(zDate);
@@ -522,26 +526,27 @@
526 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
527 ticket_init();
528 getAllTicketFields();
529 initializeVariablesFromDb();
530 initializeVariablesFromCGI();
531 form_begin(0, "%R/%s", g.zPath);
532 login_insert_csrf_secret();
533 if( P("date_override") && g.perm.Setup ){
534 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
535 }
536 @ </p>
537 zScript = ticket_newpage_code();
538 Th_Store("login", g.zLogin ? g.zLogin : "nobody");
539 Th_Store("date", db_text(0, "SELECT datetime('now')"));
540 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
541 (void*)&zNewUuid, 0);
542 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
543 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
544 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
545 return;
546 }
547 captcha_generate();
548 @ </form>
549 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
550 style_footer();
551 }
552
@@ -591,24 +596,25 @@
596 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
597 ticket_init();
598 getAllTicketFields();
599 initializeVariablesFromCGI();
600 initializeVariablesFromDb();
601 form_begin(0, "%R/%s", g.zPath);
602 @ <input type="hidden" name="name" value="%s(zName)" />
603 login_insert_csrf_secret();
604 @ </p>
605 zScript = ticket_editpage_code();
606 Th_Store("login", g.zLogin ? g.zLogin : "nobody");
607 Th_Store("date", db_text(0, "SELECT datetime('now')"));
608 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
609 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
610 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
611 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
612 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
613 return;
614 }
615 captcha_generate();
616 @ </form>
617 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
618 style_footer();
619 }
620
@@ -829,15 +835,15 @@
835 const char *z;
836 z = pTkt->aField[i].zName;
837 blob_set(&val, pTkt->aField[i].zValue);
838 if( z[0]=='+' ){
839 @ <li>Appended to %h(&z[1]):<blockquote>
840 wiki_convert(&val, 0, WIKI_NOBADLINKS);
841 @ </blockquote></li>
842 }else if( blob_size(&val)<=50 && contains_newline(&val) ){
843 @ <li>Change %h(z) to:<blockquote>
844 wiki_convert(&val, 0, WIKI_NOBADLINKS);
845 @ </blockquote></li>
846 }else{
847 @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
848 }
849 blob_reset(&val);
@@ -1006,11 +1012,11 @@
1012 eCmd = history;
1013 }else{
1014 eCmd = set;
1015 }
1016 if( g.argc==3 ){
1017 usage("set|change|history TICKETUUID");
1018 }
1019 zTktUuid = db_text(0,
1020 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1021 );
1022 if( !zTktUuid ){
1023
+3 -1
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123123
db_must_be_within_tree();
124124
vid = db_lget_int("checkout", 0);
125125
if( vid==0 ){
126126
fossil_fatal("cannot find current version");
127127
}
128
- if( !nochangeFlag && !internalUpdate ) autosync(AUTOSYNC_PULL);
128
+ if( !nochangeFlag && !internalUpdate ){
129
+ autosync(SYNC_PULL + SYNC_VERBOSE*verboseFlag);
130
+ }
129131
130132
/* Create any empty directories now, as well as after the update,
131133
** so changes in settings are reflected now */
132134
if( !nochangeFlag ) ensure_empty_dirs_created();
133135
134136
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123 db_must_be_within_tree();
124 vid = db_lget_int("checkout", 0);
125 if( vid==0 ){
126 fossil_fatal("cannot find current version");
127 }
128 if( !nochangeFlag && !internalUpdate ) autosync(AUTOSYNC_PULL);
 
 
129
130 /* Create any empty directories now, as well as after the update,
131 ** so changes in settings are reflected now */
132 if( !nochangeFlag ) ensure_empty_dirs_created();
133
134
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123 db_must_be_within_tree();
124 vid = db_lget_int("checkout", 0);
125 if( vid==0 ){
126 fossil_fatal("cannot find current version");
127 }
128 if( !nochangeFlag && !internalUpdate ){
129 autosync(SYNC_PULL + SYNC_VERBOSE*verboseFlag);
130 }
131
132 /* Create any empty directories now, as well as after the update,
133 ** so changes in settings are reflected now */
134 if( !nochangeFlag ) ensure_empty_dirs_created();
135
136
+37 -11
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127127
*/
128128
void wiki_page(void){
129129
char *zTag;
130130
int rid = 0;
131131
int isSandbox;
132
+ char *zUuid;
132133
Blob wiki;
133134
Manifest *pWiki = 0;
134135
const char *zPageName;
135136
char *zBody = mprintf("%s","<i>Empty Page</i>");
136137
@@ -159,12 +160,13 @@
159160
@ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
160161
@ available on this server.</li>
161162
if( g.perm.ModWiki ){
162163
@ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
163164
}
164
- @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
165
- @ Search wiki titles: <input type="text" name="title"/>
165
+ @ <li>
166
+ form_begin(0, "%R/wfind");
167
+ @ <div>Search wiki titles: <input type="text" name="title"/>
166168
@ &nbsp; <input type="submit" /></div></form>
167169
@ </li>
168170
@ </ul>
169171
style_footer();
170172
return;
@@ -171,10 +173,11 @@
171173
}
172174
if( check_name(zPageName) ) return;
173175
isSandbox = is_sandbox(zPageName);
174176
if( isSandbox ){
175177
zBody = db_get("sandbox",zBody);
178
+ rid = 0;
176179
}else{
177180
zTag = mprintf("wiki-%s", zPageName);
178181
rid = db_int(0,
179182
"SELECT rid FROM tagxref"
180183
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
185188
if( pWiki ){
186189
zBody = pWiki->zWiki;
187190
}
188191
}
189192
if( !g.isHome ){
193
+ if( rid ){
194
+ style_submenu_element("Diff", "Last change",
195
+ "%R/wdiff?name=%T&a=%d", zPageName, rid);
196
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
197
+ style_submenu_element("Details", "Details",
198
+ "%R/info/%S", zUuid);
199
+ }
190200
if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
191201
if( db_get_boolean("wysiwyg-wiki", 0) ){
192202
style_submenu_element("Edit", "Edit Wiki Page",
193203
"%s/wikiedit?name=%T&wysiwyg=1",
194204
g.zTop, zPageName);
@@ -253,10 +263,11 @@
253263
const char *zPageName;
254264
int n;
255265
const char *z;
256266
char *zBody = (char*)P("w");
257267
int isWysiwyg = P("wysiwyg")!=0;
268
+ int goodCaptcha = 1;
258269
259270
if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
260271
if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
261272
if( zBody ){
262273
if( isWysiwyg ){
@@ -294,11 +305,13 @@
294305
}
295306
if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
296307
zBody = pWiki->zWiki;
297308
}
298309
}
299
- if( P("submit")!=0 && zBody!=0 ){
310
+ if( P("submit")!=0 && zBody!=0
311
+ && (goodCaptcha = captcha_is_correct())
312
+ ){
300313
char *zDate;
301314
Blob cksum;
302315
blob_zero(&wiki);
303316
db_begin_transaction();
304317
if( isSandbox ){
@@ -333,10 +346,13 @@
333346
if( zBody==0 ){
334347
zBody = mprintf("<i>Empty Page</i>");
335348
}
336349
style_set_current_page("%s?name=%T", g.zPath, zPageName);
337350
style_header("Edit: %s", zPageName);
351
+ if( !goodCaptcha ){
352
+ @ <p class="generalError">Error: Incorrect security code.</p>
353
+ }
338354
blob_zero(&wiki);
339355
blob_append(&wiki, zBody, -1);
340356
if( P("preview")!=0 ){
341357
@ Preview:<hr />
342358
wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
348364
}
349365
if( n<20 ) n = 20;
350366
if( n>30 ) n = 30;
351367
if( !isWysiwyg ){
352368
/* Traditional markup-only editing */
353
- @ <form method="post" action="%s(g.zTop)/wikiedit"><div>
369
+ form_begin(0, "%R/wikiedit");
370
+ @ <div>
354371
@ <textarea name="w" class="wikiedit" cols="80"
355372
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
356373
@ <br />
357374
if( db_get_boolean("wysiwyg-wiki", 0) ){
358375
@ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
360377
}
361378
@ <input type="submit" name="preview" value="Preview Your Changes" />
362379
}else{
363380
/* Wysiwyg editing */
364381
Blob html, temp;
365
- @ <form method="post" action="%s(g.zTop)/wikiedit"
366
- @ onsubmit="wysiwygSubmit()"><div>
382
+ form_begin("onsubmit='wysiwygSubmit()'", "%R/wikiedit");
383
+ @ <div>
367384
@ <input type="hidden" name="wysiwyg" value="1" />
368385
blob_zero(&temp);
369386
wiki_convert(&wiki, &temp, 0);
370387
blob_zero(&html);
371388
htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
374391
blob_reset(&html);
375392
@ <br />
376393
@ <input type="submit" name="edit-markup" value="Markup Editor"
377394
@ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
378395
}
379
- @ <input type="submit" name="submit" value="Apply These Changes" />
380396
login_insert_csrf_secret();
397
+ @ <input type="submit" name="submit" value="Apply These Changes" />
381398
@ <input type="hidden" name="name" value="%h(zPageName)" />
382399
@ <input type="submit" name="cancel" value="Cancel"
383400
@ onclick='confirm("Abandon your changes?")' />
384
- @ </div></form>
401
+ @ </div>
402
+ captcha_generate();
403
+ @ </form>
385404
manifest_destroy(pWiki);
386405
blob_reset(&wiki);
387406
style_footer();
388407
}
389408
@@ -410,11 +429,11 @@
410429
}
411430
}
412431
style_header("Create A New Wiki Page");
413432
@ <p>Rules for wiki page names:</p>
414433
well_formed_wiki_name_rules();
415
- @ <form method="post" action="%s(g.zTop)/wikinew">
434
+ form_begin(0, "%R/wikinew");
416435
@ <p>Name of new wiki page:
417436
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
418437
@ <input type="submit" value="Create" />
419438
@ </p></form>
420439
if( zName[0] ){
@@ -455,10 +474,11 @@
455474
char *zTag;
456475
int rid = 0;
457476
int isSandbox;
458477
const char *zPageName;
459478
const char *zUser;
479
+ int goodCaptcha = 1;
460480
461481
login_check_credentials();
462482
zPageName = PD("name","");
463483
if( check_name(zPageName) ) return;
464484
isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
477497
}
478498
if( !g.perm.ApndWiki ){
479499
login_needed();
480500
return;
481501
}
482
- if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
502
+ if( P("submit")!=0 && P("r")!=0 && P("u")!=0
503
+ && (goodCaptcha = captcha_is_correct())
504
+ ){
483505
char *zDate;
484506
Blob cksum;
485507
Blob body;
486508
Blob wiki;
487509
Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
525547
cgi_redirectf("wiki?name=%T", zPageName);
526548
return;
527549
}
528550
style_set_current_page("%s?name=%T", g.zPath, zPageName);
529551
style_header("Append Comment To: %s", zPageName);
552
+ if( !goodCaptcha ){
553
+ @ <p class="generalError">Error: Incorrect security code.</p>
554
+ }
530555
if( P("preview")!=0 ){
531556
Blob preview;
532557
blob_zero(&preview);
533558
appendRemark(&preview);
534559
@ Preview:<hr>
@@ -535,11 +560,11 @@
535560
wiki_convert(&preview, 0, 0);
536561
@ <hr>
537562
blob_reset(&preview);
538563
}
539564
zUser = PD("u", g.zLogin);
540
- @ <form method="post" action="%s(g.zTop)/wikiappend">
565
+ form_begin(0, "%R/wikiappend");
541566
login_insert_csrf_secret();
542567
@ <input type="hidden" name="name" value="%h(zPageName)" />
543568
@ Your Name:
544569
@ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
545570
@ Comment to append:<br />
@@ -547,10 +572,11 @@
547572
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
548573
@ <br />
549574
@ <input type="submit" name="preview" value="Preview Your Comment" />
550575
@ <input type="submit" name="submit" value="Append Your Changes" />
551576
@ <input type="submit" name="cancel" value="Cancel" />
577
+ captcha_generate();
552578
@ </form>
553579
style_footer();
554580
}
555581
556582
/*
557583
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127 */
128 void wiki_page(void){
129 char *zTag;
130 int rid = 0;
131 int isSandbox;
 
132 Blob wiki;
133 Manifest *pWiki = 0;
134 const char *zPageName;
135 char *zBody = mprintf("%s","<i>Empty Page</i>");
136
@@ -159,12 +160,13 @@
159 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
160 @ available on this server.</li>
161 if( g.perm.ModWiki ){
162 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
163 }
164 @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
165 @ Search wiki titles: <input type="text" name="title"/>
 
166 @ &nbsp; <input type="submit" /></div></form>
167 @ </li>
168 @ </ul>
169 style_footer();
170 return;
@@ -171,10 +173,11 @@
171 }
172 if( check_name(zPageName) ) return;
173 isSandbox = is_sandbox(zPageName);
174 if( isSandbox ){
175 zBody = db_get("sandbox",zBody);
 
176 }else{
177 zTag = mprintf("wiki-%s", zPageName);
178 rid = db_int(0,
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
185 if( pWiki ){
186 zBody = pWiki->zWiki;
187 }
188 }
189 if( !g.isHome ){
 
 
 
 
 
 
 
190 if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
191 if( db_get_boolean("wysiwyg-wiki", 0) ){
192 style_submenu_element("Edit", "Edit Wiki Page",
193 "%s/wikiedit?name=%T&wysiwyg=1",
194 g.zTop, zPageName);
@@ -253,10 +263,11 @@
253 const char *zPageName;
254 int n;
255 const char *z;
256 char *zBody = (char*)P("w");
257 int isWysiwyg = P("wysiwyg")!=0;
 
258
259 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
260 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
261 if( zBody ){
262 if( isWysiwyg ){
@@ -294,11 +305,13 @@
294 }
295 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
296 zBody = pWiki->zWiki;
297 }
298 }
299 if( P("submit")!=0 && zBody!=0 ){
 
 
300 char *zDate;
301 Blob cksum;
302 blob_zero(&wiki);
303 db_begin_transaction();
304 if( isSandbox ){
@@ -333,10 +346,13 @@
333 if( zBody==0 ){
334 zBody = mprintf("<i>Empty Page</i>");
335 }
336 style_set_current_page("%s?name=%T", g.zPath, zPageName);
337 style_header("Edit: %s", zPageName);
 
 
 
338 blob_zero(&wiki);
339 blob_append(&wiki, zBody, -1);
340 if( P("preview")!=0 ){
341 @ Preview:<hr />
342 wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
348 }
349 if( n<20 ) n = 20;
350 if( n>30 ) n = 30;
351 if( !isWysiwyg ){
352 /* Traditional markup-only editing */
353 @ <form method="post" action="%s(g.zTop)/wikiedit"><div>
 
354 @ <textarea name="w" class="wikiedit" cols="80"
355 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
356 @ <br />
357 if( db_get_boolean("wysiwyg-wiki", 0) ){
358 @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
360 }
361 @ <input type="submit" name="preview" value="Preview Your Changes" />
362 }else{
363 /* Wysiwyg editing */
364 Blob html, temp;
365 @ <form method="post" action="%s(g.zTop)/wikiedit"
366 @ onsubmit="wysiwygSubmit()"><div>
367 @ <input type="hidden" name="wysiwyg" value="1" />
368 blob_zero(&temp);
369 wiki_convert(&wiki, &temp, 0);
370 blob_zero(&html);
371 htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
374 blob_reset(&html);
375 @ <br />
376 @ <input type="submit" name="edit-markup" value="Markup Editor"
377 @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
378 }
379 @ <input type="submit" name="submit" value="Apply These Changes" />
380 login_insert_csrf_secret();
 
381 @ <input type="hidden" name="name" value="%h(zPageName)" />
382 @ <input type="submit" name="cancel" value="Cancel"
383 @ onclick='confirm("Abandon your changes?")' />
384 @ </div></form>
 
 
385 manifest_destroy(pWiki);
386 blob_reset(&wiki);
387 style_footer();
388 }
389
@@ -410,11 +429,11 @@
410 }
411 }
412 style_header("Create A New Wiki Page");
413 @ <p>Rules for wiki page names:</p>
414 well_formed_wiki_name_rules();
415 @ <form method="post" action="%s(g.zTop)/wikinew">
416 @ <p>Name of new wiki page:
417 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
418 @ <input type="submit" value="Create" />
419 @ </p></form>
420 if( zName[0] ){
@@ -455,10 +474,11 @@
455 char *zTag;
456 int rid = 0;
457 int isSandbox;
458 const char *zPageName;
459 const char *zUser;
 
460
461 login_check_credentials();
462 zPageName = PD("name","");
463 if( check_name(zPageName) ) return;
464 isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
477 }
478 if( !g.perm.ApndWiki ){
479 login_needed();
480 return;
481 }
482 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
 
 
483 char *zDate;
484 Blob cksum;
485 Blob body;
486 Blob wiki;
487 Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
525 cgi_redirectf("wiki?name=%T", zPageName);
526 return;
527 }
528 style_set_current_page("%s?name=%T", g.zPath, zPageName);
529 style_header("Append Comment To: %s", zPageName);
 
 
 
530 if( P("preview")!=0 ){
531 Blob preview;
532 blob_zero(&preview);
533 appendRemark(&preview);
534 @ Preview:<hr>
@@ -535,11 +560,11 @@
535 wiki_convert(&preview, 0, 0);
536 @ <hr>
537 blob_reset(&preview);
538 }
539 zUser = PD("u", g.zLogin);
540 @ <form method="post" action="%s(g.zTop)/wikiappend">
541 login_insert_csrf_secret();
542 @ <input type="hidden" name="name" value="%h(zPageName)" />
543 @ Your Name:
544 @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
545 @ Comment to append:<br />
@@ -547,10 +572,11 @@
547 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
548 @ <br />
549 @ <input type="submit" name="preview" value="Preview Your Comment" />
550 @ <input type="submit" name="submit" value="Append Your Changes" />
551 @ <input type="submit" name="cancel" value="Cancel" />
 
552 @ </form>
553 style_footer();
554 }
555
556 /*
557
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127 */
128 void wiki_page(void){
129 char *zTag;
130 int rid = 0;
131 int isSandbox;
132 char *zUuid;
133 Blob wiki;
134 Manifest *pWiki = 0;
135 const char *zPageName;
136 char *zBody = mprintf("%s","<i>Empty Page</i>");
137
@@ -159,12 +160,13 @@
160 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
161 @ available on this server.</li>
162 if( g.perm.ModWiki ){
163 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
164 }
165 @ <li>
166 form_begin(0, "%R/wfind");
167 @ <div>Search wiki titles: <input type="text" name="title"/>
168 @ &nbsp; <input type="submit" /></div></form>
169 @ </li>
170 @ </ul>
171 style_footer();
172 return;
@@ -171,10 +173,11 @@
173 }
174 if( check_name(zPageName) ) return;
175 isSandbox = is_sandbox(zPageName);
176 if( isSandbox ){
177 zBody = db_get("sandbox",zBody);
178 rid = 0;
179 }else{
180 zTag = mprintf("wiki-%s", zPageName);
181 rid = db_int(0,
182 "SELECT rid FROM tagxref"
183 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
188 if( pWiki ){
189 zBody = pWiki->zWiki;
190 }
191 }
192 if( !g.isHome ){
193 if( rid ){
194 style_submenu_element("Diff", "Last change",
195 "%R/wdiff?name=%T&a=%d", zPageName, rid);
196 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
197 style_submenu_element("Details", "Details",
198 "%R/info/%S", zUuid);
199 }
200 if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
201 if( db_get_boolean("wysiwyg-wiki", 0) ){
202 style_submenu_element("Edit", "Edit Wiki Page",
203 "%s/wikiedit?name=%T&wysiwyg=1",
204 g.zTop, zPageName);
@@ -253,10 +263,11 @@
263 const char *zPageName;
264 int n;
265 const char *z;
266 char *zBody = (char*)P("w");
267 int isWysiwyg = P("wysiwyg")!=0;
268 int goodCaptcha = 1;
269
270 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
271 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
272 if( zBody ){
273 if( isWysiwyg ){
@@ -294,11 +305,13 @@
305 }
306 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
307 zBody = pWiki->zWiki;
308 }
309 }
310 if( P("submit")!=0 && zBody!=0
311 && (goodCaptcha = captcha_is_correct())
312 ){
313 char *zDate;
314 Blob cksum;
315 blob_zero(&wiki);
316 db_begin_transaction();
317 if( isSandbox ){
@@ -333,10 +346,13 @@
346 if( zBody==0 ){
347 zBody = mprintf("<i>Empty Page</i>");
348 }
349 style_set_current_page("%s?name=%T", g.zPath, zPageName);
350 style_header("Edit: %s", zPageName);
351 if( !goodCaptcha ){
352 @ <p class="generalError">Error: Incorrect security code.</p>
353 }
354 blob_zero(&wiki);
355 blob_append(&wiki, zBody, -1);
356 if( P("preview")!=0 ){
357 @ Preview:<hr />
358 wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
364 }
365 if( n<20 ) n = 20;
366 if( n>30 ) n = 30;
367 if( !isWysiwyg ){
368 /* Traditional markup-only editing */
369 form_begin(0, "%R/wikiedit");
370 @ <div>
371 @ <textarea name="w" class="wikiedit" cols="80"
372 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
373 @ <br />
374 if( db_get_boolean("wysiwyg-wiki", 0) ){
375 @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
377 }
378 @ <input type="submit" name="preview" value="Preview Your Changes" />
379 }else{
380 /* Wysiwyg editing */
381 Blob html, temp;
382 form_begin("onsubmit='wysiwygSubmit()'", "%R/wikiedit");
383 @ <div>
384 @ <input type="hidden" name="wysiwyg" value="1" />
385 blob_zero(&temp);
386 wiki_convert(&wiki, &temp, 0);
387 blob_zero(&html);
388 htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
391 blob_reset(&html);
392 @ <br />
393 @ <input type="submit" name="edit-markup" value="Markup Editor"
394 @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
395 }
 
396 login_insert_csrf_secret();
397 @ <input type="submit" name="submit" value="Apply These Changes" />
398 @ <input type="hidden" name="name" value="%h(zPageName)" />
399 @ <input type="submit" name="cancel" value="Cancel"
400 @ onclick='confirm("Abandon your changes?")' />
401 @ </div>
402 captcha_generate();
403 @ </form>
404 manifest_destroy(pWiki);
405 blob_reset(&wiki);
406 style_footer();
407 }
408
@@ -410,11 +429,11 @@
429 }
430 }
431 style_header("Create A New Wiki Page");
432 @ <p>Rules for wiki page names:</p>
433 well_formed_wiki_name_rules();
434 form_begin(0, "%R/wikinew");
435 @ <p>Name of new wiki page:
436 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
437 @ <input type="submit" value="Create" />
438 @ </p></form>
439 if( zName[0] ){
@@ -455,10 +474,11 @@
474 char *zTag;
475 int rid = 0;
476 int isSandbox;
477 const char *zPageName;
478 const char *zUser;
479 int goodCaptcha = 1;
480
481 login_check_credentials();
482 zPageName = PD("name","");
483 if( check_name(zPageName) ) return;
484 isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
497 }
498 if( !g.perm.ApndWiki ){
499 login_needed();
500 return;
501 }
502 if( P("submit")!=0 && P("r")!=0 && P("u")!=0
503 && (goodCaptcha = captcha_is_correct())
504 ){
505 char *zDate;
506 Blob cksum;
507 Blob body;
508 Blob wiki;
509 Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
547 cgi_redirectf("wiki?name=%T", zPageName);
548 return;
549 }
550 style_set_current_page("%s?name=%T", g.zPath, zPageName);
551 style_header("Append Comment To: %s", zPageName);
552 if( !goodCaptcha ){
553 @ <p class="generalError">Error: Incorrect security code.</p>
554 }
555 if( P("preview")!=0 ){
556 Blob preview;
557 blob_zero(&preview);
558 appendRemark(&preview);
559 @ Preview:<hr>
@@ -535,11 +560,11 @@
560 wiki_convert(&preview, 0, 0);
561 @ <hr>
562 blob_reset(&preview);
563 }
564 zUser = PD("u", g.zLogin);
565 form_begin(0, "%R/wikiappend");
566 login_insert_csrf_secret();
567 @ <input type="hidden" name="name" value="%h(zPageName)" />
568 @ Your Name:
569 @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
570 @ Comment to append:<br />
@@ -547,10 +572,11 @@
572 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
573 @ <br />
574 @ <input type="submit" name="preview" value="Preview Your Comment" />
575 @ <input type="submit" name="submit" value="Append Your Changes" />
576 @ <input type="submit" name="cancel" value="Cancel" />
577 captcha_generate();
578 @ </form>
579 style_footer();
580 }
581
582 /*
583
+140 -61
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,15 +23,16 @@
2323
2424
#if INTERFACE
2525
/*
2626
** Allowed wiki transformation operations
2727
*/
28
-#define WIKI_NOFOLLOW 0x001
29
-#define WIKI_HTML 0x002
30
-#define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31
-#define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32
-#define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
28
+#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
29
+#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
30
+#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31
+#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32
+#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33
+#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
3334
#endif
3435
3536
3637
/*
3738
** These are the only markup attributes allowed.
@@ -384,22 +385,23 @@
384385
** State flags. Save the lower 16 bits for the WIKI_* flags.
385386
*/
386387
#define AT_NEWLINE 0x0010000 /* At start of a line */
387388
#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
388389
#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
389
-#define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
390
-#define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
391
-#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
392
-#define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
390
+#define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
391
+#define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
392
+#define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */
393
+#define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */
393394
394395
/*
395396
** Current state of the rendering engine
396397
*/
397398
typedef struct Renderer Renderer;
398399
struct Renderer {
399400
Blob *pOut; /* Output appended to this blob */
400401
int state; /* Flag that govern rendering */
402
+ unsigned renderFlags; /* Flags from the client */
401403
int wikiList; /* Current wiki list type */
402404
int inVerbatim; /* True in <verbatim> mode */
403405
int preVerbState; /* Value of state prior to verbatim */
404406
int wantAutoParagraph; /* True if a <p> is desired */
405407
int inAutoParagraph; /* True if within an automatic paragraph */
@@ -484,18 +486,27 @@
484486
** <
485487
** &
486488
** \n
487489
** [
488490
**
489
-** The "[" and "\n" are only considered interesting if the "useWiki"
490
-** flag is set.
491
+** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI.
492
+** The "\n" is only considered interesting if the flags constains ALLOW_WIKI.
491493
*/
492
-static int textLength(const char *z, int useWiki){
494
+static int textLength(const char *z, int flags){
493495
int n = 0;
494
- int c;
495
- while( (c = z[0])!=0 && c!='<' && c!='&' &&
496
- (useWiki==0 || (c!='[' && c!='\n')) ){
496
+ int c, x1, x2;
497
+
498
+ if( flags & ALLOW_WIKI ){
499
+ x1 = '[';
500
+ x2 = '\n';
501
+ }else if( flags & ALLOW_LINKS ){
502
+ x1 = '[';
503
+ x2 = 0;
504
+ }else{
505
+ x1 = x2 = 0;
506
+ }
507
+ while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
497508
n++;
498509
z++;
499510
}
500511
return n;
501512
}
@@ -668,13 +679,16 @@
668679
}
669680
if( z[0]=='[' && (n = linkLength(z))>0 ){
670681
*pTokenType = TOKEN_LINK;
671682
return n;
672683
}
684
+ }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
685
+ *pTokenType = TOKEN_LINK;
686
+ return n;
673687
}
674688
*pTokenType = TOKEN_TEXT;
675
- return 1 + textLength(z+1, p->state & ALLOW_WIKI);
689
+ return 1 + textLength(z+1, p->state);
676690
}
677691
678692
/*
679693
** Parse only Wiki links, return everything else as TOKEN_RAW.
680694
**
@@ -982,10 +996,11 @@
982996
/*
983997
** Begin a new paragraph if that something that is needed.
984998
*/
985999
static void startAutoParagraph(Renderer *p){
9861000
if( p->wantAutoParagraph==0 ) return;
1001
+ if( p->state & WIKI_LINKSONLY ) return;
9871002
if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
9881003
blob_appendf(p->pOut, "<p>", -1);
9891004
pushStack(p, MARKUP_P);
9901005
p->wantAutoParagraph = 0;
9911006
p->inAutoParagraph = 1;
@@ -1075,16 +1090,39 @@
10751090
rc = 0;
10761091
}
10771092
db_reset(&q);
10781093
return rc;
10791094
}
1095
+
1096
+/*
1097
+** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
1098
+** if there is one) if zTarget is a valid wiki page name. Return NULL if
1099
+** zTarget names a page that does not exist.
1100
+*/
1101
+static const char *validWikiPageName(Renderer *p, const char *zTarget){
1102
+ if( strncmp(zTarget, "wiki:", 5)==0
1103
+ && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1104
+ return zTarget+5;
1105
+ }
1106
+ if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
1107
+ if( wiki_name_is_wellformed((const unsigned char *)zTarget)
1108
+ && ((p->state & WIKI_NOBADLINKS)==0 ||
1109
+ db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget))
1110
+ ){
1111
+ return zTarget;
1112
+ }
1113
+ return 0;
1114
+}
10801115
10811116
/*
10821117
** Resolve a hyperlink. The zTarget argument is the content of the [...]
10831118
** in the wiki. Append to the output string whatever text is appropriate
10841119
** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
10851120
** close the markup.
1121
+**
1122
+** If this routine determines that no hyperlink should be generated, then
1123
+** set zClose[0] to 0.
10861124
**
10871125
** Actually, this routine might or might not append the hyperlink, depending
10881126
** on current rendering rules: specifically does the current user have
10891127
** "History" permission.
10901128
**
@@ -1112,27 +1150,23 @@
11121150
char *zClose, /* Write hyperlink closing text here */
11131151
int nClose, /* Bytes available in zClose[] */
11141152
const char *zOrig /* Complete document text */
11151153
){
11161154
const char *zTerm = "</a>";
1117
- assert( nClose>=20 );
1155
+ const char *z;
11181156
1157
+ assert( nClose>=20 );
11191158
if( strncmp(zTarget, "http:", 5)==0
11201159
|| strncmp(zTarget, "https:", 6)==0
11211160
|| strncmp(zTarget, "ftp:", 4)==0
11221161
|| strncmp(zTarget, "mailto:", 7)==0
11231162
){
11241163
blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
1125
- /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
11261164
}else if( zTarget[0]=='/' ){
11271165
blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
11281166
}else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1129
- if( 1 ){
1130
- blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
1131
- }else{
1132
- zTerm = "";
1133
- }
1167
+ blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
11341168
}else if( is_valid_uuid(zTarget) ){
11351169
int isClosed = 0;
11361170
if( is_ticket(zTarget, &isClosed) ){
11371171
/* Special display processing for tickets. Display the hyperlink
11381172
** as crossed out if the ticket is closed.
@@ -1156,32 +1190,33 @@
11561190
blob_appendf(p->pOut, "[");
11571191
zTerm = "]";
11581192
}
11591193
}
11601194
}else if( !in_this_repo(zTarget) ){
1161
- blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1162
- zTerm = "]</span>";
1195
+ if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
1196
+ zTerm = "";
1197
+ }else{
1198
+ blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1199
+ zTerm = "]</span>";
1200
+ }
11631201
}else if( g.perm.Hyperlink ){
11641202
blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
11651203
zTerm = "]</a>";
11661204
}
11671205
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
11681206
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
11691207
blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1170
- }else if( strncmp(zTarget, "wiki:", 5)==0
1171
- && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1172
- zTarget += 5;
1173
- blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget);
1174
- }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){
1175
- blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget);
1208
+ }else if( (z = validWikiPageName(p, zTarget))!=0 ){
1209
+ blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
11761210
}else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
11771211
/* Probably an array subscript in code */
1178
- blob_appendf(p->pOut, "[");
1179
- zTerm = "]";
1212
+ zTerm = "";
1213
+ }else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1214
+ zTerm = "";
11801215
}else{
1181
- blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]</span>", zTarget);
1182
- zTerm = "";
1216
+ blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1217
+ zTerm = "</span>";
11831218
}
11841219
assert( strlen(zTerm)<nClose );
11851220
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
11861221
}
11871222
@@ -1217,19 +1252,20 @@
12171252
static void wiki_render(Renderer *p, char *z){
12181253
int tokenType;
12191254
ParsedMarkup markup;
12201255
int n;
12211256
int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1222
- int wikiUseHtml = (p->state & WIKI_USE_HTML)!=0;
1257
+ int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
1258
+ int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
12231259
char *zOrig = z;
12241260
12251261
/* Make sure the attribute constants and names still align
12261262
** following changes in the attribute list. */
12271263
assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
12281264
12291265
while( z[0] ){
1230
- if( wikiUseHtml ){
1266
+ if( wikiHtmlOnly ){
12311267
n = nextRawToken(z, p, &tokenType);
12321268
}else{
12331269
n = nextWikiToken(z, p, &tokenType);
12341270
}
12351271
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1338,33 +1374,46 @@
13381374
char *zTarget;
13391375
char *zDisplay = 0;
13401376
int i, j;
13411377
int savedState;
13421378
char zClose[20];
1379
+ char cS1 = 0;
1380
+ int iS1 = 0;
13431381
13441382
startAutoParagraph(p);
13451383
zTarget = &z[1];
13461384
for(i=1; z[i] && z[i]!=']'; i++){
13471385
if( z[i]=='|' && zDisplay==0 ){
13481386
zDisplay = &z[i+1];
1349
- z[i] = 0;
1350
- for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; }
1387
+ for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
1388
+ iS1 = j;
1389
+ cS1 = z[j];
1390
+ z[j] = 0;
13511391
}
13521392
}
13531393
z[i] = 0;
13541394
if( zDisplay==0 ){
13551395
zDisplay = zTarget;
13561396
}else{
13571397
while( fossil_isspace(*zDisplay) ) zDisplay++;
13581398
}
13591399
openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1360
- savedState = p->state;
1361
- p->state &= ~ALLOW_WIKI;
1362
- p->state |= FONT_MARKUP_ONLY;
1363
- wiki_render(p, zDisplay);
1364
- p->state = savedState;
1365
- blob_append(p->pOut, zClose, -1);
1400
+ if( linksOnly || zClose[0]==0 || p->inVerbatim ){
1401
+ if( cS1 ) z[iS1] = cS1;
1402
+ if( zClose[0]!=']' ){
1403
+ blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
1404
+ }else{
1405
+ blob_appendf(p->pOut, "%h%s", zTarget, zClose);
1406
+ }
1407
+ }else{
1408
+ savedState = p->state;
1409
+ p->state &= ~ALLOW_WIKI;
1410
+ p->state |= FONT_MARKUP_ONLY;
1411
+ wiki_render(p, zDisplay);
1412
+ p->state = savedState;
1413
+ blob_append(p->pOut, zClose, -1);
1414
+ }
13661415
break;
13671416
}
13681417
case TOKEN_TEXT: {
13691418
int i;
13701419
for(i=0; i<n && fossil_isspace(z[i]); i++){}
@@ -1371,11 +1420,15 @@
13711420
if( i<n ) startAutoParagraph(p);
13721421
blob_append(p->pOut, z, n);
13731422
break;
13741423
}
13751424
case TOKEN_RAW: {
1376
- blob_append(p->pOut, z, n);
1425
+ if( linksOnly ){
1426
+ htmlize_to_blob(p->pOut, z, n);
1427
+ }else{
1428
+ blob_append(p->pOut, z, n);
1429
+ }
13771430
break;
13781431
}
13791432
case TOKEN_MARKUP: {
13801433
const char *zId;
13811434
int iDiv;
@@ -1466,22 +1519,26 @@
14661519
/* Enter <verbatim> processing. With verbatim enabled, all other
14671520
** markup other than the corresponding end-tag with the same ID is
14681521
** ignored.
14691522
*/
14701523
if( markup.iCode==MARKUP_VERBATIM ){
1471
- int vAttrIdx, vAttrDidAppend=0;
1524
+ int ii, vAttrDidAppend=0;
14721525
p->zVerbatimId = 0;
14731526
p->inVerbatim = 1;
14741527
p->preVerbState = p->state;
14751528
p->state &= ~ALLOW_WIKI;
1476
- for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1477
- if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1478
- p->zVerbatimId = markup.aAttr[0].zValue;
1479
- }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1480
- blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1481
- markup.aAttr[vAttrIdx].zValue);
1482
- vAttrDidAppend=1;
1529
+ for(ii=0; ii<markup.nAttr; ii++){
1530
+ if( markup.aAttr[ii].iACode == ATTR_ID ){
1531
+ p->zVerbatimId = markup.aAttr[ii].zValue;
1532
+ }else if( markup.aAttr[ii].iACode == ATTR_TYPE ){
1533
+ if( fossil_stricmp(markup.aAttr[ii].zValue, "allow-links")==0 ){
1534
+ p->state |= ALLOW_LINKS;
1535
+ }else{
1536
+ blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1537
+ markup.aAttr[ii].zValue);
1538
+ vAttrDidAppend=1;
1539
+ }
14831540
}
14841541
}
14851542
if( !vAttrDidAppend ) {
14861543
endAutoParagraph(p);
14871544
blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1558,10 +1615,11 @@
15581615
*/
15591616
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
15601617
Renderer renderer;
15611618
15621619
memset(&renderer, 0, sizeof(renderer));
1620
+ renderer.renderFlags = flags;
15631621
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
15641622
if( flags & WIKI_NOBLOCK ){
15651623
renderer.state |= INLINE_MARKUP_ONLY;
15661624
}
15671625
if( flags & WIKI_INLINE ){
@@ -1568,40 +1626,61 @@
15681626
renderer.wantAutoParagraph = 0;
15691627
}else{
15701628
renderer.wantAutoParagraph = 1;
15711629
}
15721630
if( wikiUsesHtml() ){
1573
- renderer.state |= WIKI_USE_HTML;
1631
+ renderer.state |= WIKI_HTMLONLY;
15741632
}
15751633
if( pOut ){
15761634
renderer.pOut = pOut;
15771635
}else{
15781636
renderer.pOut = cgi_output_blob();
15791637
}
15801638
1581
- blob_strip_bom(pIn, 0);
1639
+ blob_to_utf8_no_bom(pIn, 0);
15821640
wiki_render(&renderer, blob_str(pIn));
15831641
endAutoParagraph(&renderer);
15841642
while( renderer.nStack ){
15851643
popStack(&renderer);
15861644
}
15871645
blob_append(renderer.pOut, "\n", 1);
15881646
free(renderer.aStack);
15891647
}
1648
+
1649
+/*
1650
+** Send a string as wiki to CGI output.
1651
+*/
1652
+void wiki_write(const char *zIn, int flags){
1653
+ Blob in;
1654
+ blob_init(&in, zIn, -1);
1655
+ wiki_convert(&in, 0, flags);
1656
+ blob_reset(&in);
1657
+}
15901658
15911659
/*
15921660
** COMMAND: test-wiki-render
15931661
**
15941662
** %fossil test-wiki-render FILE [OPTIONS]
15951663
**
15961664
** Options:
15971665
** --buttons Set the WIKI_BUTTONS flag
1666
+** --htmlonly Set the WIKI_HTMLONLY flag
1667
+** --linksonly Set the WIKI_LINKSONLY flag
1668
+** --nobadlinks Set the WIKI_NOBADLINKS flag
1669
+** --inline Set the WIKI_INLINE flag
1670
+** --noblock Set the WIKI_NOBLOCK flag
15981671
*/
15991672
void test_wiki_render(void){
16001673
Blob in, out;
16011674
int flags = 0;
16021675
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1676
+ if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1677
+ if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1678
+ if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1679
+ if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1680
+ if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1681
+ db_find_and_open_repository(0,0);
16031682
verify_all_options();
16041683
if( g.argc!=3 ) usage("FILE");
16051684
blob_zero(&out);
16061685
blob_read_from_file(&in, g.argv[2]);
16071686
wiki_convert(&in, &out, flags);
@@ -1619,11 +1698,11 @@
16191698
*/
16201699
int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
16211700
char *z;
16221701
int i;
16231702
int iStart;
1624
- blob_strip_bom(pIn, 0);
1703
+ blob_to_utf8_no_bom(pIn, 0);
16251704
z = blob_str(pIn);
16261705
for(i=0; fossil_isspace(z[i]); i++){}
16271706
if( z[i]!='<' ) return 0;
16281707
i++;
16291708
if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
@@ -1656,29 +1735,29 @@
16561735
Renderer renderer;
16571736
int tokenType;
16581737
ParsedMarkup markup;
16591738
int n;
16601739
int inlineOnly;
1661
- int wikiUseHtml = 0;
1740
+ int wikiHtmlOnly = 0;
16621741
16631742
memset(&renderer, 0, sizeof(renderer));
16641743
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
16651744
if( flags & WIKI_NOBLOCK ){
16661745
renderer.state |= INLINE_MARKUP_ONLY;
16671746
}
16681747
if( wikiUsesHtml() ){
1669
- renderer.state |= WIKI_USE_HTML;
1670
- wikiUseHtml = 1;
1748
+ renderer.state |= WIKI_HTMLONLY;
1749
+ wikiHtmlOnly = 1;
16711750
}
16721751
inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
16731752
if( replaceFlag ){
16741753
db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
16751754
srctype, srcid);
16761755
}
16771756
16781757
while( z[0] ){
1679
- if( wikiUseHtml ){
1758
+ if( wikiHtmlOnly ){
16801759
n = nextRawToken(z, &renderer, &tokenType);
16811760
}else{
16821761
n = nextWikiToken(z, &renderer, &tokenType);
16831762
}
16841763
switch( tokenType ){
16851764
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,15 +23,16 @@
23
24 #if INTERFACE
25 /*
26 ** Allowed wiki transformation operations
27 */
28 #define WIKI_NOFOLLOW 0x001
29 #define WIKI_HTML 0x002
30 #define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31 #define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32 #define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
 
33 #endif
34
35
36 /*
37 ** These are the only markup attributes allowed.
@@ -384,22 +385,23 @@
384 ** State flags. Save the lower 16 bits for the WIKI_* flags.
385 */
386 #define AT_NEWLINE 0x0010000 /* At start of a line */
387 #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
388 #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
389 #define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
390 #define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
391 #define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
392 #define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
393
394 /*
395 ** Current state of the rendering engine
396 */
397 typedef struct Renderer Renderer;
398 struct Renderer {
399 Blob *pOut; /* Output appended to this blob */
400 int state; /* Flag that govern rendering */
 
401 int wikiList; /* Current wiki list type */
402 int inVerbatim; /* True in <verbatim> mode */
403 int preVerbState; /* Value of state prior to verbatim */
404 int wantAutoParagraph; /* True if a <p> is desired */
405 int inAutoParagraph; /* True if within an automatic paragraph */
@@ -484,18 +486,27 @@
484 ** <
485 ** &
486 ** \n
487 ** [
488 **
489 ** The "[" and "\n" are only considered interesting if the "useWiki"
490 ** flag is set.
491 */
492 static int textLength(const char *z, int useWiki){
493 int n = 0;
494 int c;
495 while( (c = z[0])!=0 && c!='<' && c!='&' &&
496 (useWiki==0 || (c!='[' && c!='\n')) ){
 
 
 
 
 
 
 
 
 
497 n++;
498 z++;
499 }
500 return n;
501 }
@@ -668,13 +679,16 @@
668 }
669 if( z[0]=='[' && (n = linkLength(z))>0 ){
670 *pTokenType = TOKEN_LINK;
671 return n;
672 }
 
 
 
673 }
674 *pTokenType = TOKEN_TEXT;
675 return 1 + textLength(z+1, p->state & ALLOW_WIKI);
676 }
677
678 /*
679 ** Parse only Wiki links, return everything else as TOKEN_RAW.
680 **
@@ -982,10 +996,11 @@
982 /*
983 ** Begin a new paragraph if that something that is needed.
984 */
985 static void startAutoParagraph(Renderer *p){
986 if( p->wantAutoParagraph==0 ) return;
 
987 if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
988 blob_appendf(p->pOut, "<p>", -1);
989 pushStack(p, MARKUP_P);
990 p->wantAutoParagraph = 0;
991 p->inAutoParagraph = 1;
@@ -1075,16 +1090,39 @@
1075 rc = 0;
1076 }
1077 db_reset(&q);
1078 return rc;
1079 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1080
1081 /*
1082 ** Resolve a hyperlink. The zTarget argument is the content of the [...]
1083 ** in the wiki. Append to the output string whatever text is appropriate
1084 ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
1085 ** close the markup.
 
 
 
1086 **
1087 ** Actually, this routine might or might not append the hyperlink, depending
1088 ** on current rendering rules: specifically does the current user have
1089 ** "History" permission.
1090 **
@@ -1112,27 +1150,23 @@
1112 char *zClose, /* Write hyperlink closing text here */
1113 int nClose, /* Bytes available in zClose[] */
1114 const char *zOrig /* Complete document text */
1115 ){
1116 const char *zTerm = "</a>";
1117 assert( nClose>=20 );
1118
 
1119 if( strncmp(zTarget, "http:", 5)==0
1120 || strncmp(zTarget, "https:", 6)==0
1121 || strncmp(zTarget, "ftp:", 4)==0
1122 || strncmp(zTarget, "mailto:", 7)==0
1123 ){
1124 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
1125 /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
1126 }else if( zTarget[0]=='/' ){
1127 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
1128 }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1129 if( 1 ){
1130 blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
1131 }else{
1132 zTerm = "";
1133 }
1134 }else if( is_valid_uuid(zTarget) ){
1135 int isClosed = 0;
1136 if( is_ticket(zTarget, &isClosed) ){
1137 /* Special display processing for tickets. Display the hyperlink
1138 ** as crossed out if the ticket is closed.
@@ -1156,32 +1190,33 @@
1156 blob_appendf(p->pOut, "[");
1157 zTerm = "]";
1158 }
1159 }
1160 }else if( !in_this_repo(zTarget) ){
1161 blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1162 zTerm = "]</span>";
 
 
 
 
1163 }else if( g.perm.Hyperlink ){
1164 blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
1165 zTerm = "]</a>";
1166 }
1167 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1168 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1169 blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1170 }else if( strncmp(zTarget, "wiki:", 5)==0
1171 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1172 zTarget += 5;
1173 blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget);
1174 }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){
1175 blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget);
1176 }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1177 /* Probably an array subscript in code */
1178 blob_appendf(p->pOut, "[");
1179 zTerm = "]";
 
1180 }else{
1181 blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]</span>", zTarget);
1182 zTerm = "";
1183 }
1184 assert( strlen(zTerm)<nClose );
1185 sqlite3_snprintf(nClose, zClose, "%s", zTerm);
1186 }
1187
@@ -1217,19 +1252,20 @@
1217 static void wiki_render(Renderer *p, char *z){
1218 int tokenType;
1219 ParsedMarkup markup;
1220 int n;
1221 int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1222 int wikiUseHtml = (p->state & WIKI_USE_HTML)!=0;
 
1223 char *zOrig = z;
1224
1225 /* Make sure the attribute constants and names still align
1226 ** following changes in the attribute list. */
1227 assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
1228
1229 while( z[0] ){
1230 if( wikiUseHtml ){
1231 n = nextRawToken(z, p, &tokenType);
1232 }else{
1233 n = nextWikiToken(z, p, &tokenType);
1234 }
1235 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1338,33 +1374,46 @@
1338 char *zTarget;
1339 char *zDisplay = 0;
1340 int i, j;
1341 int savedState;
1342 char zClose[20];
 
 
1343
1344 startAutoParagraph(p);
1345 zTarget = &z[1];
1346 for(i=1; z[i] && z[i]!=']'; i++){
1347 if( z[i]=='|' && zDisplay==0 ){
1348 zDisplay = &z[i+1];
1349 z[i] = 0;
1350 for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; }
 
 
1351 }
1352 }
1353 z[i] = 0;
1354 if( zDisplay==0 ){
1355 zDisplay = zTarget;
1356 }else{
1357 while( fossil_isspace(*zDisplay) ) zDisplay++;
1358 }
1359 openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1360 savedState = p->state;
1361 p->state &= ~ALLOW_WIKI;
1362 p->state |= FONT_MARKUP_ONLY;
1363 wiki_render(p, zDisplay);
1364 p->state = savedState;
1365 blob_append(p->pOut, zClose, -1);
 
 
 
 
 
 
 
 
 
1366 break;
1367 }
1368 case TOKEN_TEXT: {
1369 int i;
1370 for(i=0; i<n && fossil_isspace(z[i]); i++){}
@@ -1371,11 +1420,15 @@
1371 if( i<n ) startAutoParagraph(p);
1372 blob_append(p->pOut, z, n);
1373 break;
1374 }
1375 case TOKEN_RAW: {
1376 blob_append(p->pOut, z, n);
 
 
 
 
1377 break;
1378 }
1379 case TOKEN_MARKUP: {
1380 const char *zId;
1381 int iDiv;
@@ -1466,22 +1519,26 @@
1466 /* Enter <verbatim> processing. With verbatim enabled, all other
1467 ** markup other than the corresponding end-tag with the same ID is
1468 ** ignored.
1469 */
1470 if( markup.iCode==MARKUP_VERBATIM ){
1471 int vAttrIdx, vAttrDidAppend=0;
1472 p->zVerbatimId = 0;
1473 p->inVerbatim = 1;
1474 p->preVerbState = p->state;
1475 p->state &= ~ALLOW_WIKI;
1476 for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1477 if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1478 p->zVerbatimId = markup.aAttr[0].zValue;
1479 }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1480 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1481 markup.aAttr[vAttrIdx].zValue);
1482 vAttrDidAppend=1;
 
 
 
 
1483 }
1484 }
1485 if( !vAttrDidAppend ) {
1486 endAutoParagraph(p);
1487 blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1558,10 +1615,11 @@
1558 */
1559 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1560 Renderer renderer;
1561
1562 memset(&renderer, 0, sizeof(renderer));
 
1563 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
1564 if( flags & WIKI_NOBLOCK ){
1565 renderer.state |= INLINE_MARKUP_ONLY;
1566 }
1567 if( flags & WIKI_INLINE ){
@@ -1568,40 +1626,61 @@
1568 renderer.wantAutoParagraph = 0;
1569 }else{
1570 renderer.wantAutoParagraph = 1;
1571 }
1572 if( wikiUsesHtml() ){
1573 renderer.state |= WIKI_USE_HTML;
1574 }
1575 if( pOut ){
1576 renderer.pOut = pOut;
1577 }else{
1578 renderer.pOut = cgi_output_blob();
1579 }
1580
1581 blob_strip_bom(pIn, 0);
1582 wiki_render(&renderer, blob_str(pIn));
1583 endAutoParagraph(&renderer);
1584 while( renderer.nStack ){
1585 popStack(&renderer);
1586 }
1587 blob_append(renderer.pOut, "\n", 1);
1588 free(renderer.aStack);
1589 }
 
 
 
 
 
 
 
 
 
 
1590
1591 /*
1592 ** COMMAND: test-wiki-render
1593 **
1594 ** %fossil test-wiki-render FILE [OPTIONS]
1595 **
1596 ** Options:
1597 ** --buttons Set the WIKI_BUTTONS flag
 
 
 
 
 
1598 */
1599 void test_wiki_render(void){
1600 Blob in, out;
1601 int flags = 0;
1602 if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
 
 
 
 
 
 
1603 verify_all_options();
1604 if( g.argc!=3 ) usage("FILE");
1605 blob_zero(&out);
1606 blob_read_from_file(&in, g.argv[2]);
1607 wiki_convert(&in, &out, flags);
@@ -1619,11 +1698,11 @@
1619 */
1620 int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
1621 char *z;
1622 int i;
1623 int iStart;
1624 blob_strip_bom(pIn, 0);
1625 z = blob_str(pIn);
1626 for(i=0; fossil_isspace(z[i]); i++){}
1627 if( z[i]!='<' ) return 0;
1628 i++;
1629 if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
@@ -1656,29 +1735,29 @@
1656 Renderer renderer;
1657 int tokenType;
1658 ParsedMarkup markup;
1659 int n;
1660 int inlineOnly;
1661 int wikiUseHtml = 0;
1662
1663 memset(&renderer, 0, sizeof(renderer));
1664 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1665 if( flags & WIKI_NOBLOCK ){
1666 renderer.state |= INLINE_MARKUP_ONLY;
1667 }
1668 if( wikiUsesHtml() ){
1669 renderer.state |= WIKI_USE_HTML;
1670 wikiUseHtml = 1;
1671 }
1672 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1673 if( replaceFlag ){
1674 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1675 srctype, srcid);
1676 }
1677
1678 while( z[0] ){
1679 if( wikiUseHtml ){
1680 n = nextRawToken(z, &renderer, &tokenType);
1681 }else{
1682 n = nextWikiToken(z, &renderer, &tokenType);
1683 }
1684 switch( tokenType ){
1685
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,15 +23,16 @@
23
24 #if INTERFACE
25 /*
26 ** Allowed wiki transformation operations
27 */
28 #define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
29 #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
34 #endif
35
36
37 /*
38 ** These are the only markup attributes allowed.
@@ -384,22 +385,23 @@
385 ** State flags. Save the lower 16 bits for the WIKI_* flags.
386 */
387 #define AT_NEWLINE 0x0010000 /* At start of a line */
388 #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
389 #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
390 #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
391 #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
392 #define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */
393 #define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */
394
395 /*
396 ** Current state of the rendering engine
397 */
398 typedef struct Renderer Renderer;
399 struct Renderer {
400 Blob *pOut; /* Output appended to this blob */
401 int state; /* Flag that govern rendering */
402 unsigned renderFlags; /* Flags from the client */
403 int wikiList; /* Current wiki list type */
404 int inVerbatim; /* True in <verbatim> mode */
405 int preVerbState; /* Value of state prior to verbatim */
406 int wantAutoParagraph; /* True if a <p> is desired */
407 int inAutoParagraph; /* True if within an automatic paragraph */
@@ -484,18 +486,27 @@
486 ** <
487 ** &
488 ** \n
489 ** [
490 **
491 ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI.
492 ** The "\n" is only considered interesting if the flags constains ALLOW_WIKI.
493 */
494 static int textLength(const char *z, int flags){
495 int n = 0;
496 int c, x1, x2;
497
498 if( flags & ALLOW_WIKI ){
499 x1 = '[';
500 x2 = '\n';
501 }else if( flags & ALLOW_LINKS ){
502 x1 = '[';
503 x2 = 0;
504 }else{
505 x1 = x2 = 0;
506 }
507 while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
508 n++;
509 z++;
510 }
511 return n;
512 }
@@ -668,13 +679,16 @@
679 }
680 if( z[0]=='[' && (n = linkLength(z))>0 ){
681 *pTokenType = TOKEN_LINK;
682 return n;
683 }
684 }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
685 *pTokenType = TOKEN_LINK;
686 return n;
687 }
688 *pTokenType = TOKEN_TEXT;
689 return 1 + textLength(z+1, p->state);
690 }
691
692 /*
693 ** Parse only Wiki links, return everything else as TOKEN_RAW.
694 **
@@ -982,10 +996,11 @@
996 /*
997 ** Begin a new paragraph if that something that is needed.
998 */
999 static void startAutoParagraph(Renderer *p){
1000 if( p->wantAutoParagraph==0 ) return;
1001 if( p->state & WIKI_LINKSONLY ) return;
1002 if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
1003 blob_appendf(p->pOut, "<p>", -1);
1004 pushStack(p, MARKUP_P);
1005 p->wantAutoParagraph = 0;
1006 p->inAutoParagraph = 1;
@@ -1075,16 +1090,39 @@
1090 rc = 0;
1091 }
1092 db_reset(&q);
1093 return rc;
1094 }
1095
1096 /*
1097 ** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
1098 ** if there is one) if zTarget is a valid wiki page name. Return NULL if
1099 ** zTarget names a page that does not exist.
1100 */
1101 static const char *validWikiPageName(Renderer *p, const char *zTarget){
1102 if( strncmp(zTarget, "wiki:", 5)==0
1103 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1104 return zTarget+5;
1105 }
1106 if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
1107 if( wiki_name_is_wellformed((const unsigned char *)zTarget)
1108 && ((p->state & WIKI_NOBADLINKS)==0 ||
1109 db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget))
1110 ){
1111 return zTarget;
1112 }
1113 return 0;
1114 }
1115
1116 /*
1117 ** Resolve a hyperlink. The zTarget argument is the content of the [...]
1118 ** in the wiki. Append to the output string whatever text is appropriate
1119 ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
1120 ** close the markup.
1121 **
1122 ** If this routine determines that no hyperlink should be generated, then
1123 ** set zClose[0] to 0.
1124 **
1125 ** Actually, this routine might or might not append the hyperlink, depending
1126 ** on current rendering rules: specifically does the current user have
1127 ** "History" permission.
1128 **
@@ -1112,27 +1150,23 @@
1150 char *zClose, /* Write hyperlink closing text here */
1151 int nClose, /* Bytes available in zClose[] */
1152 const char *zOrig /* Complete document text */
1153 ){
1154 const char *zTerm = "</a>";
1155 const char *z;
1156
1157 assert( nClose>=20 );
1158 if( strncmp(zTarget, "http:", 5)==0
1159 || strncmp(zTarget, "https:", 6)==0
1160 || strncmp(zTarget, "ftp:", 4)==0
1161 || strncmp(zTarget, "mailto:", 7)==0
1162 ){
1163 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
 
1164 }else if( zTarget[0]=='/' ){
1165 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
1166 }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1167 blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
 
 
 
 
1168 }else if( is_valid_uuid(zTarget) ){
1169 int isClosed = 0;
1170 if( is_ticket(zTarget, &isClosed) ){
1171 /* Special display processing for tickets. Display the hyperlink
1172 ** as crossed out if the ticket is closed.
@@ -1156,32 +1190,33 @@
1190 blob_appendf(p->pOut, "[");
1191 zTerm = "]";
1192 }
1193 }
1194 }else if( !in_this_repo(zTarget) ){
1195 if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
1196 zTerm = "";
1197 }else{
1198 blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1199 zTerm = "]</span>";
1200 }
1201 }else if( g.perm.Hyperlink ){
1202 blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
1203 zTerm = "]</a>";
1204 }
1205 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1206 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1207 blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1208 }else if( (z = validWikiPageName(p, zTarget))!=0 ){
1209 blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
 
 
 
 
1210 }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1211 /* Probably an array subscript in code */
1212 zTerm = "";
1213 }else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1214 zTerm = "";
1215 }else{
1216 blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1217 zTerm = "</span>";
1218 }
1219 assert( strlen(zTerm)<nClose );
1220 sqlite3_snprintf(nClose, zClose, "%s", zTerm);
1221 }
1222
@@ -1217,19 +1252,20 @@
1252 static void wiki_render(Renderer *p, char *z){
1253 int tokenType;
1254 ParsedMarkup markup;
1255 int n;
1256 int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1257 int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
1258 int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
1259 char *zOrig = z;
1260
1261 /* Make sure the attribute constants and names still align
1262 ** following changes in the attribute list. */
1263 assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
1264
1265 while( z[0] ){
1266 if( wikiHtmlOnly ){
1267 n = nextRawToken(z, p, &tokenType);
1268 }else{
1269 n = nextWikiToken(z, p, &tokenType);
1270 }
1271 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1338,33 +1374,46 @@
1374 char *zTarget;
1375 char *zDisplay = 0;
1376 int i, j;
1377 int savedState;
1378 char zClose[20];
1379 char cS1 = 0;
1380 int iS1 = 0;
1381
1382 startAutoParagraph(p);
1383 zTarget = &z[1];
1384 for(i=1; z[i] && z[i]!=']'; i++){
1385 if( z[i]=='|' && zDisplay==0 ){
1386 zDisplay = &z[i+1];
1387 for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
1388 iS1 = j;
1389 cS1 = z[j];
1390 z[j] = 0;
1391 }
1392 }
1393 z[i] = 0;
1394 if( zDisplay==0 ){
1395 zDisplay = zTarget;
1396 }else{
1397 while( fossil_isspace(*zDisplay) ) zDisplay++;
1398 }
1399 openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1400 if( linksOnly || zClose[0]==0 || p->inVerbatim ){
1401 if( cS1 ) z[iS1] = cS1;
1402 if( zClose[0]!=']' ){
1403 blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
1404 }else{
1405 blob_appendf(p->pOut, "%h%s", zTarget, zClose);
1406 }
1407 }else{
1408 savedState = p->state;
1409 p->state &= ~ALLOW_WIKI;
1410 p->state |= FONT_MARKUP_ONLY;
1411 wiki_render(p, zDisplay);
1412 p->state = savedState;
1413 blob_append(p->pOut, zClose, -1);
1414 }
1415 break;
1416 }
1417 case TOKEN_TEXT: {
1418 int i;
1419 for(i=0; i<n && fossil_isspace(z[i]); i++){}
@@ -1371,11 +1420,15 @@
1420 if( i<n ) startAutoParagraph(p);
1421 blob_append(p->pOut, z, n);
1422 break;
1423 }
1424 case TOKEN_RAW: {
1425 if( linksOnly ){
1426 htmlize_to_blob(p->pOut, z, n);
1427 }else{
1428 blob_append(p->pOut, z, n);
1429 }
1430 break;
1431 }
1432 case TOKEN_MARKUP: {
1433 const char *zId;
1434 int iDiv;
@@ -1466,22 +1519,26 @@
1519 /* Enter <verbatim> processing. With verbatim enabled, all other
1520 ** markup other than the corresponding end-tag with the same ID is
1521 ** ignored.
1522 */
1523 if( markup.iCode==MARKUP_VERBATIM ){
1524 int ii, vAttrDidAppend=0;
1525 p->zVerbatimId = 0;
1526 p->inVerbatim = 1;
1527 p->preVerbState = p->state;
1528 p->state &= ~ALLOW_WIKI;
1529 for(ii=0; ii<markup.nAttr; ii++){
1530 if( markup.aAttr[ii].iACode == ATTR_ID ){
1531 p->zVerbatimId = markup.aAttr[ii].zValue;
1532 }else if( markup.aAttr[ii].iACode == ATTR_TYPE ){
1533 if( fossil_stricmp(markup.aAttr[ii].zValue, "allow-links")==0 ){
1534 p->state |= ALLOW_LINKS;
1535 }else{
1536 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1537 markup.aAttr[ii].zValue);
1538 vAttrDidAppend=1;
1539 }
1540 }
1541 }
1542 if( !vAttrDidAppend ) {
1543 endAutoParagraph(p);
1544 blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1558,10 +1615,11 @@
1615 */
1616 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1617 Renderer renderer;
1618
1619 memset(&renderer, 0, sizeof(renderer));
1620 renderer.renderFlags = flags;
1621 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
1622 if( flags & WIKI_NOBLOCK ){
1623 renderer.state |= INLINE_MARKUP_ONLY;
1624 }
1625 if( flags & WIKI_INLINE ){
@@ -1568,40 +1626,61 @@
1626 renderer.wantAutoParagraph = 0;
1627 }else{
1628 renderer.wantAutoParagraph = 1;
1629 }
1630 if( wikiUsesHtml() ){
1631 renderer.state |= WIKI_HTMLONLY;
1632 }
1633 if( pOut ){
1634 renderer.pOut = pOut;
1635 }else{
1636 renderer.pOut = cgi_output_blob();
1637 }
1638
1639 blob_to_utf8_no_bom(pIn, 0);
1640 wiki_render(&renderer, blob_str(pIn));
1641 endAutoParagraph(&renderer);
1642 while( renderer.nStack ){
1643 popStack(&renderer);
1644 }
1645 blob_append(renderer.pOut, "\n", 1);
1646 free(renderer.aStack);
1647 }
1648
1649 /*
1650 ** Send a string as wiki to CGI output.
1651 */
1652 void wiki_write(const char *zIn, int flags){
1653 Blob in;
1654 blob_init(&in, zIn, -1);
1655 wiki_convert(&in, 0, flags);
1656 blob_reset(&in);
1657 }
1658
1659 /*
1660 ** COMMAND: test-wiki-render
1661 **
1662 ** %fossil test-wiki-render FILE [OPTIONS]
1663 **
1664 ** Options:
1665 ** --buttons Set the WIKI_BUTTONS flag
1666 ** --htmlonly Set the WIKI_HTMLONLY flag
1667 ** --linksonly Set the WIKI_LINKSONLY flag
1668 ** --nobadlinks Set the WIKI_NOBADLINKS flag
1669 ** --inline Set the WIKI_INLINE flag
1670 ** --noblock Set the WIKI_NOBLOCK flag
1671 */
1672 void test_wiki_render(void){
1673 Blob in, out;
1674 int flags = 0;
1675 if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1676 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1677 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1678 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1679 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1680 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1681 db_find_and_open_repository(0,0);
1682 verify_all_options();
1683 if( g.argc!=3 ) usage("FILE");
1684 blob_zero(&out);
1685 blob_read_from_file(&in, g.argv[2]);
1686 wiki_convert(&in, &out, flags);
@@ -1619,11 +1698,11 @@
1698 */
1699 int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
1700 char *z;
1701 int i;
1702 int iStart;
1703 blob_to_utf8_no_bom(pIn, 0);
1704 z = blob_str(pIn);
1705 for(i=0; fossil_isspace(z[i]); i++){}
1706 if( z[i]!='<' ) return 0;
1707 i++;
1708 if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
@@ -1656,29 +1735,29 @@
1735 Renderer renderer;
1736 int tokenType;
1737 ParsedMarkup markup;
1738 int n;
1739 int inlineOnly;
1740 int wikiHtmlOnly = 0;
1741
1742 memset(&renderer, 0, sizeof(renderer));
1743 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1744 if( flags & WIKI_NOBLOCK ){
1745 renderer.state |= INLINE_MARKUP_ONLY;
1746 }
1747 if( wikiUsesHtml() ){
1748 renderer.state |= WIKI_HTMLONLY;
1749 wikiHtmlOnly = 1;
1750 }
1751 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1752 if( replaceFlag ){
1753 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1754 srctype, srcid);
1755 }
1756
1757 while( z[0] ){
1758 if( wikiHtmlOnly ){
1759 n = nextRawToken(z, &renderer, &tokenType);
1760 }else{
1761 n = nextWikiToken(z, &renderer, &tokenType);
1762 }
1763 switch( tokenType ){
1764
+88 -51
--- src/xfer.c
+++ src/xfer.c
@@ -273,19 +273,15 @@
273273
Blob *pUuid /* The UUID of the file to send */
274274
){
275275
static const char *const azQuery[] = {
276276
"SELECT pid FROM plink x"
277277
" WHERE cid=%d"
278
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
279
- " AND NOT EXISTS(SELECT 1 FROM plink y"
280
- " WHERE y.pid=x.cid AND y.cid=x.pid)",
281
-
282
- "SELECT pid FROM mlink x"
278
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
279
+
280
+ "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid"
283281
" WHERE fid=%d"
284282
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
285
- " AND NOT EXISTS(SELECT 1 FROM mlink y"
286
- " WHERE y.pid=x.fid AND y.fid=x.pid)"
287283
};
288284
int i;
289285
Blob src, delta;
290286
int size = 0;
291287
int srcId = 0;
@@ -303,11 +299,11 @@
303299
if( size>=blob_size(pContent)-50 ){
304300
size = 0;
305301
}else if( uuid_is_shunned(zUuid) ){
306302
size = 0;
307303
}else{
308
- if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
304
+ if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
309305
blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
310306
blob_append(pXfer->pOut, blob_buffer(&delta), size);
311307
}
312308
blob_reset(&delta);
313309
free(zUuid);
@@ -804,11 +800,11 @@
804800
*/
805801
static int run_script(const char *zScript){
806802
if( !zScript ){
807803
return TH_OK; /* No script, return success. */
808804
}
809
- Th_FossilInit(); /* Make sure TH1 is ready. */
805
+ Th_FossilInit(0, 0); /* Make sure TH1 is ready. */
810806
return Th_Eval(g.interp, 0, zScript, -1);
811807
}
812808
813809
/*
814810
** Run the pre-transfer TH1 script, if any, and returns the return code.
@@ -1270,11 +1266,23 @@
12701266
/*
12711267
** Format strings for progress reporting.
12721268
*/
12731269
static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
12741270
static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
1271
+static const char zBriefFormat[] =
1272
+ "Round-trips: %d Artifacts sent: %d received: %d\r";
12751273
1274
+#if INTERFACE
1275
+/*
1276
+** Flag options for controlling client_sync()
1277
+*/
1278
+#define SYNC_PUSH 0x0001
1279
+#define SYNC_PULL 0x0002
1280
+#define SYNC_CLONE 0x0004
1281
+#define SYNC_PRIVATE 0x0008
1282
+#define SYNC_VERBOSE 0x0010
1283
+#endif
12761284
12771285
/*
12781286
** Sync to the host identified in g.urlName and g.urlPath. This
12791287
** routine is called by the client.
12801288
**
@@ -1281,16 +1289,13 @@
12811289
** Records are pushed to the server if pushFlag is true. Records
12821290
** are pulled if pullFlag is true. A full sync occurs if both are
12831291
** true.
12841292
*/
12851293
int client_sync(
1286
- int pushFlag, /* True to do a push (or a sync) */
1287
- int pullFlag, /* True to do a pull (or a sync) */
1288
- int cloneFlag, /* True if this is a clone */
1289
- int privateFlag, /* True to exchange private branches */
1290
- int configRcvMask, /* Receive these configuration items */
1291
- int configSendMask /* Send these configuration items */
1294
+ unsigned syncFlags, /* Mask of SYNC_* flags */
1295
+ unsigned configRcvMask, /* Receive these configuration items */
1296
+ unsigned configSendMask /* Send these configuration items */
12921297
){
12931298
int go = 1; /* Loop until zero */
12941299
int nCardSent = 0; /* Number of cards sent */
12951300
int nCardRcvd = 0; /* Number of cards received */
12961301
int nCycle = 0; /* Number of round trips to the server */
@@ -1308,27 +1313,30 @@
13081313
int lastPctDone = -1; /* Last displayed pctDone */
13091314
double rArrivalTime; /* Time at which a message arrived */
13101315
const char *zSCode = db_get("server-code", "x");
13111316
const char *zPCode = db_get("project-code", 0);
13121317
int nErr = 0; /* Number of errors */
1318
+ int nRoundtrip= 0; /* Number of HTTP requests */
1319
+ int nArtifactSent = 0; /* Total artifacts sent */
1320
+ int nArtifactRcvd = 0; /* Total artifacts received */
1321
+ const char *zOpType = 0;/* Push, Pull, Sync, Clone */
13131322
1314
- if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
1315
- if( pushFlag + pullFlag + cloneFlag == 0
1323
+ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1324
+ if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
13161325
&& configRcvMask==0 && configSendMask==0 ) return 0;
13171326
13181327
transport_stats(0, 0, 1);
13191328
socket_global_init();
13201329
memset(&xfer, 0, sizeof(xfer));
13211330
xfer.pIn = &recv;
13221331
xfer.pOut = &send;
13231332
xfer.mxSend = db_get_int("max-upload", 250000);
1324
- if( privateFlag ){
1333
+ if( syncFlags & SYNC_PRIVATE ){
13251334
g.perm.Private = 1;
13261335
xfer.syncPrivate = 1;
13271336
}
13281337
1329
- assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
13301338
db_begin_transaction();
13311339
db_record_repository_filename(0);
13321340
db_multi_exec(
13331341
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
13341342
);
@@ -1339,33 +1347,39 @@
13391347
blob_zero(&xfer.line);
13401348
origConfigRcvMask = 0;
13411349
13421350
13431351
/* Send the send-private pragma if we are trying to sync private data */
1344
- if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1352
+ if( syncFlags & SYNC_PRIVATE ){
1353
+ blob_append(&send, "pragma send-private\n", -1);
1354
+ }
13451355
13461356
/*
13471357
** Always begin with a clone, pull, or push message
13481358
*/
1349
- if( cloneFlag ){
1359
+ if( syncFlags & SYNC_CLONE ){
13501360
blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1351
- pushFlag = 0;
1352
- pullFlag = 0;
1361
+ syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
13531362
nCardSent++;
13541363
/* TBD: Request all transferable configuration values */
13551364
content_enable_dephantomize(0);
1356
- }else if( pullFlag ){
1365
+ zOpType = "Clone";
1366
+ }else if( syncFlags & SYNC_PULL ){
13571367
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
13581368
nCardSent++;
1369
+ zOpType = "Pull";
13591370
}
1360
- if( pushFlag ){
1371
+ if( syncFlags & SYNC_PUSH ){
13611372
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
13621373
nCardSent++;
1374
+ if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
13631375
}
13641376
manifest_crosslink_begin();
13651377
transport_global_startup();
1366
- fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1378
+ if( syncFlags & SYNC_VERBOSE ){
1379
+ fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1380
+ }
13671381
13681382
while( go ){
13691383
int newPhantom = 0;
13701384
char *zRandomness;
13711385
@@ -1378,25 +1392,28 @@
13781392
}
13791393
13801394
/* Generate gimme cards for phantoms and leaf cards
13811395
** for all leaves.
13821396
*/
1383
- if( pullFlag || (cloneFlag && cloneSeqno==1) ){
1397
+ if( (syncFlags & SYNC_PULL)!=0
1398
+ || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1399
+ ){
13841400
request_phantoms(&xfer, mxPhantomReq);
13851401
}
1386
- if( pushFlag ){
1402
+ if( syncFlags & SYNC_PUSH ){
13871403
send_unsent(&xfer);
13881404
nCardSent += send_unclustered(&xfer);
1389
- if( privateFlag ) send_private(&xfer);
1405
+ if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
13901406
}
13911407
13921408
/* Send configuration parameter requests. On a clone, delay sending
13931409
** this until the second cycle since the login card might fail on
13941410
** the first cycle.
13951411
*/
1396
- if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
1412
+ if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
13971413
const char *zName;
1414
+ if( zOpType==0 ) zOpType = "Pull";
13981415
zName = configure_first_name(configRcvMask);
13991416
while( zName ){
14001417
blob_appendf(&send, "reqconfig %s\n", zName);
14011418
zName = configure_next_name(configRcvMask);
14021419
nCardSent++;
@@ -1411,10 +1428,11 @@
14111428
configRcvMask = 0;
14121429
}
14131430
14141431
/* Send configuration parameters being pushed */
14151432
if( configSendMask ){
1433
+ if( zOpType==0 ) zOpType = "Push";
14161434
if( configSendMask & CONFIGSET_OLDFORMAT ){
14171435
const char *zName;
14181436
zName = configure_first_name(configSendMask);
14191437
while( zName ){
14201438
send_legacy_config_card(&xfer, zName);
@@ -1434,42 +1452,50 @@
14341452
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
14351453
blob_appendf(&send, "# %s\n", zRandomness);
14361454
free(zRandomness);
14371455
14381456
/* Exchange messages with the server */
1439
- fossil_print(zValueFormat, "Sent:",
1440
- blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1441
- xfer.nFileSent, xfer.nDeltaSent);
1457
+ if( syncFlags & SYNC_VERBOSE ){
1458
+ fossil_print(zValueFormat, "Sent:",
1459
+ blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1460
+ xfer.nFileSent, xfer.nDeltaSent);
1461
+ }else{
1462
+ nRoundtrip++;
1463
+ nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
1464
+ fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1465
+ }
14421466
nCardSent = 0;
14431467
nCardRcvd = 0;
14441468
xfer.nFileSent = 0;
14451469
xfer.nDeltaSent = 0;
14461470
xfer.nGimmeSent = 0;
14471471
xfer.nIGotSent = 0;
1448
- if( !g.cgiOutput && !g.fQuiet ){
1472
+ if( syncFlags & SYNC_VERBOSE ){
14491473
fossil_print("waiting for server...");
14501474
}
14511475
fflush(stdout);
1452
- if( http_exchange(&send, &recv, cloneFlag==0 || nCycle>0) ){
1476
+ if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
14531477
nErr++;
14541478
break;
14551479
}
14561480
lastPctDone = -1;
14571481
blob_reset(&send);
14581482
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
14591483
14601484
/* Send the send-private pragma if we are trying to sync private data */
1461
- if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1485
+ if( syncFlags & SYNC_PRIVATE ){
1486
+ blob_append(&send, "pragma send-private\n", -1);
1487
+ }
14621488
14631489
/* Begin constructing the next message (which might never be
14641490
** sent) by beginning with the pull or push cards
14651491
*/
1466
- if( pullFlag ){
1492
+ if( syncFlags & SYNC_PULL ){
14671493
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
14681494
nCardSent++;
14691495
}
1470
- if( pushFlag ){
1496
+ if( syncFlags & SYNC_PUSH ){
14711497
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
14721498
nCardSent++;
14731499
}
14741500
go = 0;
14751501
@@ -1497,11 +1523,11 @@
14971523
nCardRcvd++;
14981524
continue;
14991525
}
15001526
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
15011527
nCardRcvd++;
1502
- if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){
1528
+ if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){
15031529
pctDone = (recv.iCursor*100)/recv.nUsed;
15041530
if( pctDone!=lastPctDone ){
15051531
fossil_print("\rprocessed: %d%% ", pctDone);
15061532
lastPctDone = pctDone;
15071533
fflush(stdout);
@@ -1512,20 +1538,22 @@
15121538
** file UUID DELTASRC SIZE \n CONTENT
15131539
**
15141540
** Receive a file transmitted from the server.
15151541
*/
15161542
if( blob_eq(&xfer.aToken[0],"file") ){
1517
- xfer_accept_file(&xfer, cloneFlag);
1543
+ xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0);
1544
+ nArtifactRcvd++;
15181545
}else
15191546
15201547
/* cfile UUID USIZE CSIZE \n CONTENT
15211548
** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
15221549
**
15231550
** Receive a compressed file transmitted from the server.
15241551
*/
15251552
if( blob_eq(&xfer.aToken[0],"cfile") ){
15261553
xfer_accept_compressed_file(&xfer);
1554
+ nArtifactRcvd++;
15271555
}else
15281556
15291557
/* gimme UUID
15301558
**
15311559
** Server is requesting a file. If the file is a manifest, assume
@@ -1534,11 +1562,11 @@
15341562
*/
15351563
if( blob_eq(&xfer.aToken[0], "gimme")
15361564
&& xfer.nToken==2
15371565
&& blob_is_uuid(&xfer.aToken[1])
15381566
){
1539
- if( pushFlag ){
1567
+ if( syncFlags & SYNC_PUSH ){
15401568
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
15411569
if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
15421570
}
15431571
}else
15441572
@@ -1563,11 +1591,11 @@
15631591
rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
15641592
if( rid>0 ){
15651593
if( !isPriv ) content_make_public(rid);
15661594
}else if( isPriv && !g.perm.Private ){
15671595
/* ignore private files */
1568
- }else if( pullFlag || cloneFlag ){
1596
+ }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
15691597
rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
15701598
if( rid ) newPhantom = 1;
15711599
}
15721600
remote_has(rid);
15731601
}else
@@ -1578,11 +1606,11 @@
15781606
** Should only happen in response to a clone. This message tells
15791607
** the client what product to use for the new database.
15801608
*/
15811609
if( blob_eq(&xfer.aToken[0],"push")
15821610
&& xfer.nToken==3
1583
- && cloneFlag
1611
+ && (syncFlags & SYNC_CLONE)!=0
15841612
&& blob_is_uuid(&xfer.aToken[1])
15851613
&& blob_is_uuid(&xfer.aToken[2])
15861614
){
15871615
if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
15881616
fossil_fatal("server loop");
@@ -1608,11 +1636,12 @@
16081636
Blob content;
16091637
blob_zero(&content);
16101638
blob_extract(xfer.pIn, size, &content);
16111639
g.perm.Admin = g.perm.RdAddr = 1;
16121640
configure_receive(zName, &content, origConfigRcvMask);
1613
- nCardSent++;
1641
+ nCardRcvd++;
1642
+ nArtifactRcvd++;
16141643
blob_reset(&content);
16151644
blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
16161645
}else
16171646
16181647
@@ -1659,11 +1688,15 @@
16591688
** to the next cycle.
16601689
*/
16611690
if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
16621691
char *zMsg = blob_terminate(&xfer.aToken[1]);
16631692
defossilize(zMsg);
1664
- if( zMsg ) fossil_print("\rServer says: %s\n", zMsg);
1693
+ if( (syncFlags & SYNC_PUSH) && zMsg && strglob("pull only *", zMsg) ){
1694
+ syncFlags &= ~SYNC_PUSH;
1695
+ zMsg = 0;
1696
+ }
1697
+ fossil_print("\rServer says: %s\n", zMsg);
16651698
}else
16661699
16671700
/* pragma NAME VALUE...
16681701
**
16691702
** The server can send pragmas to try to convey meta-information to
@@ -1683,11 +1716,11 @@
16831716
** is returned in the reply before the error card, so second and
16841717
** subsequent messages should be OK. Nevertheless, we need to ignore
16851718
** the error card on the first message of a clone.
16861719
*/
16871720
if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1688
- if( !cloneFlag || nCycle>0 ){
1721
+ if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
16891722
char *zMsg = blob_terminate(&xfer.aToken[1]);
16901723
defossilize(zMsg);
16911724
if( fossil_strcmp(zMsg, "login failed")==0 ){
16921725
if( nCycle<2 ){
16931726
if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
17271760
&& (configRcvMask & CONFIGSET_OLDFORMAT)!=0
17281761
){
17291762
configure_finalize_receive();
17301763
}
17311764
origConfigRcvMask = 0;
1732
- if( nCardRcvd>0 ){
1765
+ if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
17331766
fossil_print(zValueFormat, "Received:",
17341767
blob_size(&recv), nCardRcvd,
17351768
xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1769
+ }else{
1770
+ fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
17361771
}
17371772
blob_reset(&recv);
17381773
nCycle++;
17391774
17401775
/* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
17431778
nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
17441779
if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
17451780
go = 1;
17461781
mxPhantomReq = nFileRecv*2;
17471782
if( mxPhantomReq<200 ) mxPhantomReq = 200;
1748
- }else if( cloneFlag && nFileRecv>0 ){
1783
+ }else if( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
17491784
go = 1;
17501785
}
17511786
nCardRcvd = 0;
17521787
xfer.nFileRcvd = 0;
17531788
xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
17591794
if( xfer.nFileSent+xfer.nDeltaSent>0 ){
17601795
go = 1;
17611796
}
17621797
17631798
/* If this is a clone, the go at least two rounds */
1764
- if( cloneFlag && nCycle==1 ) go = 1;
1799
+ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
17651800
17661801
/* Stop the cycle if the server sends a "clone_seqno 0" card and
17671802
** we have gone at least two rounds. Always go at least two rounds
17681803
** on a clone in order to be sure to retrieve the configuration
17691804
** information which is only sent on the second round.
17701805
*/
17711806
if( cloneSeqno<=0 && nCycle>1 ) go = 0;
17721807
};
17731808
transport_stats(&nSent, &nRcvd, 1);
1774
- fossil_print("Total network traffic: %lld bytes sent, %lld bytes received\n",
1775
- nSent, nRcvd);
1809
+ if( (syncFlags & SYNC_VERBOSE)==0 ) fossil_print("\n");
1810
+ fossil_print(
1811
+ "%s finished with %lld bytes sent, %lld bytes received\n",
1812
+ zOpType, nSent, nRcvd);
17761813
transport_close();
17771814
transport_global_shutdown();
17781815
db_multi_exec("DROP TABLE onremote");
17791816
manifest_crosslink_end();
17801817
content_enable_dephantomize(1);
17811818
db_end_transaction(0);
17821819
return nErr;
17831820
}
17841821
--- src/xfer.c
+++ src/xfer.c
@@ -273,19 +273,15 @@
273 Blob *pUuid /* The UUID of the file to send */
274 ){
275 static const char *const azQuery[] = {
276 "SELECT pid FROM plink x"
277 " WHERE cid=%d"
278 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
279 " AND NOT EXISTS(SELECT 1 FROM plink y"
280 " WHERE y.pid=x.cid AND y.cid=x.pid)",
281
282 "SELECT pid FROM mlink x"
283 " WHERE fid=%d"
284 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
285 " AND NOT EXISTS(SELECT 1 FROM mlink y"
286 " WHERE y.pid=x.fid AND y.fid=x.pid)"
287 };
288 int i;
289 Blob src, delta;
290 int size = 0;
291 int srcId = 0;
@@ -303,11 +299,11 @@
303 if( size>=blob_size(pContent)-50 ){
304 size = 0;
305 }else if( uuid_is_shunned(zUuid) ){
306 size = 0;
307 }else{
308 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
309 blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
310 blob_append(pXfer->pOut, blob_buffer(&delta), size);
311 }
312 blob_reset(&delta);
313 free(zUuid);
@@ -804,11 +800,11 @@
804 */
805 static int run_script(const char *zScript){
806 if( !zScript ){
807 return TH_OK; /* No script, return success. */
808 }
809 Th_FossilInit(); /* Make sure TH1 is ready. */
810 return Th_Eval(g.interp, 0, zScript, -1);
811 }
812
813 /*
814 ** Run the pre-transfer TH1 script, if any, and returns the return code.
@@ -1270,11 +1266,23 @@
1270 /*
1271 ** Format strings for progress reporting.
1272 */
1273 static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
1274 static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
 
 
1275
 
 
 
 
 
 
 
 
 
 
1276
1277 /*
1278 ** Sync to the host identified in g.urlName and g.urlPath. This
1279 ** routine is called by the client.
1280 **
@@ -1281,16 +1289,13 @@
1281 ** Records are pushed to the server if pushFlag is true. Records
1282 ** are pulled if pullFlag is true. A full sync occurs if both are
1283 ** true.
1284 */
1285 int client_sync(
1286 int pushFlag, /* True to do a push (or a sync) */
1287 int pullFlag, /* True to do a pull (or a sync) */
1288 int cloneFlag, /* True if this is a clone */
1289 int privateFlag, /* True to exchange private branches */
1290 int configRcvMask, /* Receive these configuration items */
1291 int configSendMask /* Send these configuration items */
1292 ){
1293 int go = 1; /* Loop until zero */
1294 int nCardSent = 0; /* Number of cards sent */
1295 int nCardRcvd = 0; /* Number of cards received */
1296 int nCycle = 0; /* Number of round trips to the server */
@@ -1308,27 +1313,30 @@
1308 int lastPctDone = -1; /* Last displayed pctDone */
1309 double rArrivalTime; /* Time at which a message arrived */
1310 const char *zSCode = db_get("server-code", "x");
1311 const char *zPCode = db_get("project-code", 0);
1312 int nErr = 0; /* Number of errors */
 
 
 
 
1313
1314 if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
1315 if( pushFlag + pullFlag + cloneFlag == 0
1316 && configRcvMask==0 && configSendMask==0 ) return 0;
1317
1318 transport_stats(0, 0, 1);
1319 socket_global_init();
1320 memset(&xfer, 0, sizeof(xfer));
1321 xfer.pIn = &recv;
1322 xfer.pOut = &send;
1323 xfer.mxSend = db_get_int("max-upload", 250000);
1324 if( privateFlag ){
1325 g.perm.Private = 1;
1326 xfer.syncPrivate = 1;
1327 }
1328
1329 assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
1330 db_begin_transaction();
1331 db_record_repository_filename(0);
1332 db_multi_exec(
1333 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1334 );
@@ -1339,33 +1347,39 @@
1339 blob_zero(&xfer.line);
1340 origConfigRcvMask = 0;
1341
1342
1343 /* Send the send-private pragma if we are trying to sync private data */
1344 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
 
 
1345
1346 /*
1347 ** Always begin with a clone, pull, or push message
1348 */
1349 if( cloneFlag ){
1350 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1351 pushFlag = 0;
1352 pullFlag = 0;
1353 nCardSent++;
1354 /* TBD: Request all transferable configuration values */
1355 content_enable_dephantomize(0);
1356 }else if( pullFlag ){
 
1357 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1358 nCardSent++;
 
1359 }
1360 if( pushFlag ){
1361 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1362 nCardSent++;
 
1363 }
1364 manifest_crosslink_begin();
1365 transport_global_startup();
1366 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
 
 
1367
1368 while( go ){
1369 int newPhantom = 0;
1370 char *zRandomness;
1371
@@ -1378,25 +1392,28 @@
1378 }
1379
1380 /* Generate gimme cards for phantoms and leaf cards
1381 ** for all leaves.
1382 */
1383 if( pullFlag || (cloneFlag && cloneSeqno==1) ){
 
 
1384 request_phantoms(&xfer, mxPhantomReq);
1385 }
1386 if( pushFlag ){
1387 send_unsent(&xfer);
1388 nCardSent += send_unclustered(&xfer);
1389 if( privateFlag ) send_private(&xfer);
1390 }
1391
1392 /* Send configuration parameter requests. On a clone, delay sending
1393 ** this until the second cycle since the login card might fail on
1394 ** the first cycle.
1395 */
1396 if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
1397 const char *zName;
 
1398 zName = configure_first_name(configRcvMask);
1399 while( zName ){
1400 blob_appendf(&send, "reqconfig %s\n", zName);
1401 zName = configure_next_name(configRcvMask);
1402 nCardSent++;
@@ -1411,10 +1428,11 @@
1411 configRcvMask = 0;
1412 }
1413
1414 /* Send configuration parameters being pushed */
1415 if( configSendMask ){
 
1416 if( configSendMask & CONFIGSET_OLDFORMAT ){
1417 const char *zName;
1418 zName = configure_first_name(configSendMask);
1419 while( zName ){
1420 send_legacy_config_card(&xfer, zName);
@@ -1434,42 +1452,50 @@
1434 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1435 blob_appendf(&send, "# %s\n", zRandomness);
1436 free(zRandomness);
1437
1438 /* Exchange messages with the server */
1439 fossil_print(zValueFormat, "Sent:",
1440 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1441 xfer.nFileSent, xfer.nDeltaSent);
 
 
 
 
 
 
1442 nCardSent = 0;
1443 nCardRcvd = 0;
1444 xfer.nFileSent = 0;
1445 xfer.nDeltaSent = 0;
1446 xfer.nGimmeSent = 0;
1447 xfer.nIGotSent = 0;
1448 if( !g.cgiOutput && !g.fQuiet ){
1449 fossil_print("waiting for server...");
1450 }
1451 fflush(stdout);
1452 if( http_exchange(&send, &recv, cloneFlag==0 || nCycle>0) ){
1453 nErr++;
1454 break;
1455 }
1456 lastPctDone = -1;
1457 blob_reset(&send);
1458 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1459
1460 /* Send the send-private pragma if we are trying to sync private data */
1461 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
 
 
1462
1463 /* Begin constructing the next message (which might never be
1464 ** sent) by beginning with the pull or push cards
1465 */
1466 if( pullFlag ){
1467 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1468 nCardSent++;
1469 }
1470 if( pushFlag ){
1471 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1472 nCardSent++;
1473 }
1474 go = 0;
1475
@@ -1497,11 +1523,11 @@
1497 nCardRcvd++;
1498 continue;
1499 }
1500 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1501 nCardRcvd++;
1502 if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){
1503 pctDone = (recv.iCursor*100)/recv.nUsed;
1504 if( pctDone!=lastPctDone ){
1505 fossil_print("\rprocessed: %d%% ", pctDone);
1506 lastPctDone = pctDone;
1507 fflush(stdout);
@@ -1512,20 +1538,22 @@
1512 ** file UUID DELTASRC SIZE \n CONTENT
1513 **
1514 ** Receive a file transmitted from the server.
1515 */
1516 if( blob_eq(&xfer.aToken[0],"file") ){
1517 xfer_accept_file(&xfer, cloneFlag);
 
1518 }else
1519
1520 /* cfile UUID USIZE CSIZE \n CONTENT
1521 ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
1522 **
1523 ** Receive a compressed file transmitted from the server.
1524 */
1525 if( blob_eq(&xfer.aToken[0],"cfile") ){
1526 xfer_accept_compressed_file(&xfer);
 
1527 }else
1528
1529 /* gimme UUID
1530 **
1531 ** Server is requesting a file. If the file is a manifest, assume
@@ -1534,11 +1562,11 @@
1534 */
1535 if( blob_eq(&xfer.aToken[0], "gimme")
1536 && xfer.nToken==2
1537 && blob_is_uuid(&xfer.aToken[1])
1538 ){
1539 if( pushFlag ){
1540 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1541 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1542 }
1543 }else
1544
@@ -1563,11 +1591,11 @@
1563 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1564 if( rid>0 ){
1565 if( !isPriv ) content_make_public(rid);
1566 }else if( isPriv && !g.perm.Private ){
1567 /* ignore private files */
1568 }else if( pullFlag || cloneFlag ){
1569 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
1570 if( rid ) newPhantom = 1;
1571 }
1572 remote_has(rid);
1573 }else
@@ -1578,11 +1606,11 @@
1578 ** Should only happen in response to a clone. This message tells
1579 ** the client what product to use for the new database.
1580 */
1581 if( blob_eq(&xfer.aToken[0],"push")
1582 && xfer.nToken==3
1583 && cloneFlag
1584 && blob_is_uuid(&xfer.aToken[1])
1585 && blob_is_uuid(&xfer.aToken[2])
1586 ){
1587 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
1588 fossil_fatal("server loop");
@@ -1608,11 +1636,12 @@
1608 Blob content;
1609 blob_zero(&content);
1610 blob_extract(xfer.pIn, size, &content);
1611 g.perm.Admin = g.perm.RdAddr = 1;
1612 configure_receive(zName, &content, origConfigRcvMask);
1613 nCardSent++;
 
1614 blob_reset(&content);
1615 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1616 }else
1617
1618
@@ -1659,11 +1688,15 @@
1659 ** to the next cycle.
1660 */
1661 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
1662 char *zMsg = blob_terminate(&xfer.aToken[1]);
1663 defossilize(zMsg);
1664 if( zMsg ) fossil_print("\rServer says: %s\n", zMsg);
 
 
 
 
1665 }else
1666
1667 /* pragma NAME VALUE...
1668 **
1669 ** The server can send pragmas to try to convey meta-information to
@@ -1683,11 +1716,11 @@
1683 ** is returned in the reply before the error card, so second and
1684 ** subsequent messages should be OK. Nevertheless, we need to ignore
1685 ** the error card on the first message of a clone.
1686 */
1687 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1688 if( !cloneFlag || nCycle>0 ){
1689 char *zMsg = blob_terminate(&xfer.aToken[1]);
1690 defossilize(zMsg);
1691 if( fossil_strcmp(zMsg, "login failed")==0 ){
1692 if( nCycle<2 ){
1693 if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
1727 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
1728 ){
1729 configure_finalize_receive();
1730 }
1731 origConfigRcvMask = 0;
1732 if( nCardRcvd>0 ){
1733 fossil_print(zValueFormat, "Received:",
1734 blob_size(&recv), nCardRcvd,
1735 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
 
 
1736 }
1737 blob_reset(&recv);
1738 nCycle++;
1739
1740 /* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
1743 nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
1744 if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
1745 go = 1;
1746 mxPhantomReq = nFileRecv*2;
1747 if( mxPhantomReq<200 ) mxPhantomReq = 200;
1748 }else if( cloneFlag && nFileRecv>0 ){
1749 go = 1;
1750 }
1751 nCardRcvd = 0;
1752 xfer.nFileRcvd = 0;
1753 xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
1759 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1760 go = 1;
1761 }
1762
1763 /* If this is a clone, the go at least two rounds */
1764 if( cloneFlag && nCycle==1 ) go = 1;
1765
1766 /* Stop the cycle if the server sends a "clone_seqno 0" card and
1767 ** we have gone at least two rounds. Always go at least two rounds
1768 ** on a clone in order to be sure to retrieve the configuration
1769 ** information which is only sent on the second round.
1770 */
1771 if( cloneSeqno<=0 && nCycle>1 ) go = 0;
1772 };
1773 transport_stats(&nSent, &nRcvd, 1);
1774 fossil_print("Total network traffic: %lld bytes sent, %lld bytes received\n",
1775 nSent, nRcvd);
 
 
1776 transport_close();
1777 transport_global_shutdown();
1778 db_multi_exec("DROP TABLE onremote");
1779 manifest_crosslink_end();
1780 content_enable_dephantomize(1);
1781 db_end_transaction(0);
1782 return nErr;
1783 }
1784
--- src/xfer.c
+++ src/xfer.c
@@ -273,19 +273,15 @@
273 Blob *pUuid /* The UUID of the file to send */
274 ){
275 static const char *const azQuery[] = {
276 "SELECT pid FROM plink x"
277 " WHERE cid=%d"
278 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
279
280 "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid"
 
 
281 " WHERE fid=%d"
282 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
 
 
283 };
284 int i;
285 Blob src, delta;
286 int size = 0;
287 int srcId = 0;
@@ -303,11 +299,11 @@
299 if( size>=blob_size(pContent)-50 ){
300 size = 0;
301 }else if( uuid_is_shunned(zUuid) ){
302 size = 0;
303 }else{
304 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
305 blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
306 blob_append(pXfer->pOut, blob_buffer(&delta), size);
307 }
308 blob_reset(&delta);
309 free(zUuid);
@@ -804,11 +800,11 @@
800 */
801 static int run_script(const char *zScript){
802 if( !zScript ){
803 return TH_OK; /* No script, return success. */
804 }
805 Th_FossilInit(0, 0); /* Make sure TH1 is ready. */
806 return Th_Eval(g.interp, 0, zScript, -1);
807 }
808
809 /*
810 ** Run the pre-transfer TH1 script, if any, and returns the return code.
@@ -1270,11 +1266,23 @@
1266 /*
1267 ** Format strings for progress reporting.
1268 */
1269 static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
1270 static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
1271 static const char zBriefFormat[] =
1272 "Round-trips: %d Artifacts sent: %d received: %d\r";
1273
1274 #if INTERFACE
1275 /*
1276 ** Flag options for controlling client_sync()
1277 */
1278 #define SYNC_PUSH 0x0001
1279 #define SYNC_PULL 0x0002
1280 #define SYNC_CLONE 0x0004
1281 #define SYNC_PRIVATE 0x0008
1282 #define SYNC_VERBOSE 0x0010
1283 #endif
1284
1285 /*
1286 ** Sync to the host identified in g.urlName and g.urlPath. This
1287 ** routine is called by the client.
1288 **
@@ -1281,16 +1289,13 @@
1289 ** Records are pushed to the server if pushFlag is true. Records
1290 ** are pulled if pullFlag is true. A full sync occurs if both are
1291 ** true.
1292 */
1293 int client_sync(
1294 unsigned syncFlags, /* Mask of SYNC_* flags */
1295 unsigned configRcvMask, /* Receive these configuration items */
1296 unsigned configSendMask /* Send these configuration items */
 
 
 
1297 ){
1298 int go = 1; /* Loop until zero */
1299 int nCardSent = 0; /* Number of cards sent */
1300 int nCardRcvd = 0; /* Number of cards received */
1301 int nCycle = 0; /* Number of round trips to the server */
@@ -1308,27 +1313,30 @@
1313 int lastPctDone = -1; /* Last displayed pctDone */
1314 double rArrivalTime; /* Time at which a message arrived */
1315 const char *zSCode = db_get("server-code", "x");
1316 const char *zPCode = db_get("project-code", 0);
1317 int nErr = 0; /* Number of errors */
1318 int nRoundtrip= 0; /* Number of HTTP requests */
1319 int nArtifactSent = 0; /* Total artifacts sent */
1320 int nArtifactRcvd = 0; /* Total artifacts received */
1321 const char *zOpType = 0;/* Push, Pull, Sync, Clone */
1322
1323 if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1324 if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
1325 && configRcvMask==0 && configSendMask==0 ) return 0;
1326
1327 transport_stats(0, 0, 1);
1328 socket_global_init();
1329 memset(&xfer, 0, sizeof(xfer));
1330 xfer.pIn = &recv;
1331 xfer.pOut = &send;
1332 xfer.mxSend = db_get_int("max-upload", 250000);
1333 if( syncFlags & SYNC_PRIVATE ){
1334 g.perm.Private = 1;
1335 xfer.syncPrivate = 1;
1336 }
1337
 
1338 db_begin_transaction();
1339 db_record_repository_filename(0);
1340 db_multi_exec(
1341 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1342 );
@@ -1339,33 +1347,39 @@
1347 blob_zero(&xfer.line);
1348 origConfigRcvMask = 0;
1349
1350
1351 /* Send the send-private pragma if we are trying to sync private data */
1352 if( syncFlags & SYNC_PRIVATE ){
1353 blob_append(&send, "pragma send-private\n", -1);
1354 }
1355
1356 /*
1357 ** Always begin with a clone, pull, or push message
1358 */
1359 if( syncFlags & SYNC_CLONE ){
1360 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1361 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
 
1362 nCardSent++;
1363 /* TBD: Request all transferable configuration values */
1364 content_enable_dephantomize(0);
1365 zOpType = "Clone";
1366 }else if( syncFlags & SYNC_PULL ){
1367 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1368 nCardSent++;
1369 zOpType = "Pull";
1370 }
1371 if( syncFlags & SYNC_PUSH ){
1372 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1373 nCardSent++;
1374 if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
1375 }
1376 manifest_crosslink_begin();
1377 transport_global_startup();
1378 if( syncFlags & SYNC_VERBOSE ){
1379 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1380 }
1381
1382 while( go ){
1383 int newPhantom = 0;
1384 char *zRandomness;
1385
@@ -1378,25 +1392,28 @@
1392 }
1393
1394 /* Generate gimme cards for phantoms and leaf cards
1395 ** for all leaves.
1396 */
1397 if( (syncFlags & SYNC_PULL)!=0
1398 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1399 ){
1400 request_phantoms(&xfer, mxPhantomReq);
1401 }
1402 if( syncFlags & SYNC_PUSH ){
1403 send_unsent(&xfer);
1404 nCardSent += send_unclustered(&xfer);
1405 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
1406 }
1407
1408 /* Send configuration parameter requests. On a clone, delay sending
1409 ** this until the second cycle since the login card might fail on
1410 ** the first cycle.
1411 */
1412 if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1413 const char *zName;
1414 if( zOpType==0 ) zOpType = "Pull";
1415 zName = configure_first_name(configRcvMask);
1416 while( zName ){
1417 blob_appendf(&send, "reqconfig %s\n", zName);
1418 zName = configure_next_name(configRcvMask);
1419 nCardSent++;
@@ -1411,10 +1428,11 @@
1428 configRcvMask = 0;
1429 }
1430
1431 /* Send configuration parameters being pushed */
1432 if( configSendMask ){
1433 if( zOpType==0 ) zOpType = "Push";
1434 if( configSendMask & CONFIGSET_OLDFORMAT ){
1435 const char *zName;
1436 zName = configure_first_name(configSendMask);
1437 while( zName ){
1438 send_legacy_config_card(&xfer, zName);
@@ -1434,42 +1452,50 @@
1452 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1453 blob_appendf(&send, "# %s\n", zRandomness);
1454 free(zRandomness);
1455
1456 /* Exchange messages with the server */
1457 if( syncFlags & SYNC_VERBOSE ){
1458 fossil_print(zValueFormat, "Sent:",
1459 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1460 xfer.nFileSent, xfer.nDeltaSent);
1461 }else{
1462 nRoundtrip++;
1463 nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
1464 fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1465 }
1466 nCardSent = 0;
1467 nCardRcvd = 0;
1468 xfer.nFileSent = 0;
1469 xfer.nDeltaSent = 0;
1470 xfer.nGimmeSent = 0;
1471 xfer.nIGotSent = 0;
1472 if( syncFlags & SYNC_VERBOSE ){
1473 fossil_print("waiting for server...");
1474 }
1475 fflush(stdout);
1476 if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1477 nErr++;
1478 break;
1479 }
1480 lastPctDone = -1;
1481 blob_reset(&send);
1482 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1483
1484 /* Send the send-private pragma if we are trying to sync private data */
1485 if( syncFlags & SYNC_PRIVATE ){
1486 blob_append(&send, "pragma send-private\n", -1);
1487 }
1488
1489 /* Begin constructing the next message (which might never be
1490 ** sent) by beginning with the pull or push cards
1491 */
1492 if( syncFlags & SYNC_PULL ){
1493 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1494 nCardSent++;
1495 }
1496 if( syncFlags & SYNC_PUSH ){
1497 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1498 nCardSent++;
1499 }
1500 go = 0;
1501
@@ -1497,11 +1523,11 @@
1523 nCardRcvd++;
1524 continue;
1525 }
1526 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1527 nCardRcvd++;
1528 if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){
1529 pctDone = (recv.iCursor*100)/recv.nUsed;
1530 if( pctDone!=lastPctDone ){
1531 fossil_print("\rprocessed: %d%% ", pctDone);
1532 lastPctDone = pctDone;
1533 fflush(stdout);
@@ -1512,20 +1538,22 @@
1538 ** file UUID DELTASRC SIZE \n CONTENT
1539 **
1540 ** Receive a file transmitted from the server.
1541 */
1542 if( blob_eq(&xfer.aToken[0],"file") ){
1543 xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0);
1544 nArtifactRcvd++;
1545 }else
1546
1547 /* cfile UUID USIZE CSIZE \n CONTENT
1548 ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
1549 **
1550 ** Receive a compressed file transmitted from the server.
1551 */
1552 if( blob_eq(&xfer.aToken[0],"cfile") ){
1553 xfer_accept_compressed_file(&xfer);
1554 nArtifactRcvd++;
1555 }else
1556
1557 /* gimme UUID
1558 **
1559 ** Server is requesting a file. If the file is a manifest, assume
@@ -1534,11 +1562,11 @@
1562 */
1563 if( blob_eq(&xfer.aToken[0], "gimme")
1564 && xfer.nToken==2
1565 && blob_is_uuid(&xfer.aToken[1])
1566 ){
1567 if( syncFlags & SYNC_PUSH ){
1568 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1569 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1570 }
1571 }else
1572
@@ -1563,11 +1591,11 @@
1591 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1592 if( rid>0 ){
1593 if( !isPriv ) content_make_public(rid);
1594 }else if( isPriv && !g.perm.Private ){
1595 /* ignore private files */
1596 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
1597 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
1598 if( rid ) newPhantom = 1;
1599 }
1600 remote_has(rid);
1601 }else
@@ -1578,11 +1606,11 @@
1606 ** Should only happen in response to a clone. This message tells
1607 ** the client what product to use for the new database.
1608 */
1609 if( blob_eq(&xfer.aToken[0],"push")
1610 && xfer.nToken==3
1611 && (syncFlags & SYNC_CLONE)!=0
1612 && blob_is_uuid(&xfer.aToken[1])
1613 && blob_is_uuid(&xfer.aToken[2])
1614 ){
1615 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
1616 fossil_fatal("server loop");
@@ -1608,11 +1636,12 @@
1636 Blob content;
1637 blob_zero(&content);
1638 blob_extract(xfer.pIn, size, &content);
1639 g.perm.Admin = g.perm.RdAddr = 1;
1640 configure_receive(zName, &content, origConfigRcvMask);
1641 nCardRcvd++;
1642 nArtifactRcvd++;
1643 blob_reset(&content);
1644 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1645 }else
1646
1647
@@ -1659,11 +1688,15 @@
1688 ** to the next cycle.
1689 */
1690 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
1691 char *zMsg = blob_terminate(&xfer.aToken[1]);
1692 defossilize(zMsg);
1693 if( (syncFlags & SYNC_PUSH) && zMsg && strglob("pull only *", zMsg) ){
1694 syncFlags &= ~SYNC_PUSH;
1695 zMsg = 0;
1696 }
1697 fossil_print("\rServer says: %s\n", zMsg);
1698 }else
1699
1700 /* pragma NAME VALUE...
1701 **
1702 ** The server can send pragmas to try to convey meta-information to
@@ -1683,11 +1716,11 @@
1716 ** is returned in the reply before the error card, so second and
1717 ** subsequent messages should be OK. Nevertheless, we need to ignore
1718 ** the error card on the first message of a clone.
1719 */
1720 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1721 if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
1722 char *zMsg = blob_terminate(&xfer.aToken[1]);
1723 defossilize(zMsg);
1724 if( fossil_strcmp(zMsg, "login failed")==0 ){
1725 if( nCycle<2 ){
1726 if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
1760 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
1761 ){
1762 configure_finalize_receive();
1763 }
1764 origConfigRcvMask = 0;
1765 if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
1766 fossil_print(zValueFormat, "Received:",
1767 blob_size(&recv), nCardRcvd,
1768 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1769 }else{
1770 fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1771 }
1772 blob_reset(&recv);
1773 nCycle++;
1774
1775 /* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
1778 nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
1779 if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
1780 go = 1;
1781 mxPhantomReq = nFileRecv*2;
1782 if( mxPhantomReq<200 ) mxPhantomReq = 200;
1783 }else if( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
1784 go = 1;
1785 }
1786 nCardRcvd = 0;
1787 xfer.nFileRcvd = 0;
1788 xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
1794 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1795 go = 1;
1796 }
1797
1798 /* If this is a clone, the go at least two rounds */
1799 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
1800
1801 /* Stop the cycle if the server sends a "clone_seqno 0" card and
1802 ** we have gone at least two rounds. Always go at least two rounds
1803 ** on a clone in order to be sure to retrieve the configuration
1804 ** information which is only sent on the second round.
1805 */
1806 if( cloneSeqno<=0 && nCycle>1 ) go = 0;
1807 };
1808 transport_stats(&nSent, &nRcvd, 1);
1809 if( (syncFlags & SYNC_VERBOSE)==0 ) fossil_print("\n");
1810 fossil_print(
1811 "%s finished with %lld bytes sent, %lld bytes received\n",
1812 zOpType, nSent, nRcvd);
1813 transport_close();
1814 transport_global_shutdown();
1815 db_multi_exec("DROP TABLE onremote");
1816 manifest_crosslink_end();
1817 content_enable_dephantomize(1);
1818 db_end_transaction(0);
1819 return nErr;
1820 }
1821
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -35,10 +35,14 @@
3535
# will run on the platform that is doing the build. This is used
3636
# to compile code-generator programs as part of the build process.
3737
# See TCC below for the C compiler for building the finished binary.
3838
#
3939
BCC = gcc
40
+
41
+#### Enable compiling with debug symbols (much larger binary)
42
+#
43
+# FOSSIL_ENABLE_SYMBOLS = 1
4044
4145
#### Enable JSON (http://www.json.org) support using "cson"
4246
#
4347
# FOSSIL_ENABLE_JSON = 1
4448
@@ -112,11 +116,18 @@
112116
# will run on the target platform. This is usually the same
113117
# as BCC, unless you are cross-compiling. This C compiler builds
114118
# the finished binary for fossil. The BCC compiler above is used
115119
# for building intermediate code-generator tools.
116120
#
117
-TCC = $(PREFIX)gcc -g -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
121
+TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
122
+
123
+#### Add the necessary command line options to build with debugging
124
+# symbols, if enabled.
125
+#
126
+ifdef FOSSIL_ENABLE_SYMBOLS
127
+TCC += -g
128
+endif
118129
119130
#### Compile resources for use in building executables that will run
120131
# on the target platform.
121132
#
122133
RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
123134
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -35,10 +35,14 @@
35 # will run on the platform that is doing the build. This is used
36 # to compile code-generator programs as part of the build process.
37 # See TCC below for the C compiler for building the finished binary.
38 #
39 BCC = gcc
 
 
 
 
40
41 #### Enable JSON (http://www.json.org) support using "cson"
42 #
43 # FOSSIL_ENABLE_JSON = 1
44
@@ -112,11 +116,18 @@
112 # will run on the target platform. This is usually the same
113 # as BCC, unless you are cross-compiling. This C compiler builds
114 # the finished binary for fossil. The BCC compiler above is used
115 # for building intermediate code-generator tools.
116 #
117 TCC = $(PREFIX)gcc -g -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
 
 
 
 
 
 
 
118
119 #### Compile resources for use in building executables that will run
120 # on the target platform.
121 #
122 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
123
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -35,10 +35,14 @@
35 # will run on the platform that is doing the build. This is used
36 # to compile code-generator programs as part of the build process.
37 # See TCC below for the C compiler for building the finished binary.
38 #
39 BCC = gcc
40
41 #### Enable compiling with debug symbols (much larger binary)
42 #
43 # FOSSIL_ENABLE_SYMBOLS = 1
44
45 #### Enable JSON (http://www.json.org) support using "cson"
46 #
47 # FOSSIL_ENABLE_JSON = 1
48
@@ -112,11 +116,18 @@
116 # will run on the target platform. This is usually the same
117 # as BCC, unless you are cross-compiling. This C compiler builds
118 # the finished binary for fossil. The BCC compiler above is used
119 # for building intermediate code-generator tools.
120 #
121 TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
122
123 #### Add the necessary command line options to build with debugging
124 # symbols, if enabled.
125 #
126 ifdef FOSSIL_ENABLE_SYMBOLS
127 TCC += -g
128 endif
129
130 #### Compile resources for use in building executables that will run
131 # on the target platform.
132 #
133 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
134
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -35,10 +35,14 @@
3535
# will run on the platform that is doing the build. This is used
3636
# to compile code-generator programs as part of the build process.
3737
# See TCC below for the C compiler for building the finished binary.
3838
#
3939
BCC = gcc
40
+
41
+#### Enable compiling with debug symbols (much larger binary)
42
+#
43
+# FOSSIL_ENABLE_SYMBOLS = 1
4044
4145
#### Enable JSON (http://www.json.org) support using "cson"
4246
#
4347
FOSSIL_ENABLE_JSON = 1
4448
@@ -113,10 +117,17 @@
113117
# as BCC, unless you are cross-compiling. This C compiler builds
114118
# the finished binary for fossil. The BCC compiler above is used
115119
# for building intermediate code-generator tools.
116120
#
117121
TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
122
+
123
+#### Add the necessary command line options to build with debugging
124
+# symbols, if enabled.
125
+#
126
+ifdef FOSSIL_ENABLE_SYMBOLS
127
+TCC += -g
128
+endif
118129
119130
#### Compile resources for use in building executables that will run
120131
# on the target platform.
121132
#
122133
RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
123134
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -35,10 +35,14 @@
35 # will run on the platform that is doing the build. This is used
36 # to compile code-generator programs as part of the build process.
37 # See TCC below for the C compiler for building the finished binary.
38 #
39 BCC = gcc
 
 
 
 
40
41 #### Enable JSON (http://www.json.org) support using "cson"
42 #
43 FOSSIL_ENABLE_JSON = 1
44
@@ -113,10 +117,17 @@
113 # as BCC, unless you are cross-compiling. This C compiler builds
114 # the finished binary for fossil. The BCC compiler above is used
115 # for building intermediate code-generator tools.
116 #
117 TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
 
 
 
 
 
 
 
118
119 #### Compile resources for use in building executables that will run
120 # on the target platform.
121 #
122 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
123
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -35,10 +35,14 @@
35 # will run on the platform that is doing the build. This is used
36 # to compile code-generator programs as part of the build process.
37 # See TCC below for the C compiler for building the finished binary.
38 #
39 BCC = gcc
40
41 #### Enable compiling with debug symbols (much larger binary)
42 #
43 # FOSSIL_ENABLE_SYMBOLS = 1
44
45 #### Enable JSON (http://www.json.org) support using "cson"
46 #
47 FOSSIL_ENABLE_JSON = 1
48
@@ -113,10 +117,17 @@
117 # as BCC, unless you are cross-compiling. This C compiler builds
118 # the finished binary for fossil. The BCC compiler above is used
119 # for building intermediate code-generator tools.
120 #
121 TCC = $(PREFIX)gcc -Os -Wall -L$(ZLIBDIR) -I$(ZINCDIR)
122
123 #### Add the necessary command line options to build with debugging
124 # symbols, if enabled.
125 #
126 ifdef FOSSIL_ENABLE_SYMBOLS
127 TCC += -g
128 endif
129
130 #### Compile resources for use in building executables that will run
131 # on the target platform.
132 #
133 RCC = $(PREFIX)windres -I$(SRCDIR) -I$(ZINCDIR)
134

Keyboard Shortcuts

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